Skip to content

Commit c968202

Browse files
authored
Merge branch 'main' into main
2 parents 2ea1466 + f0fa807 commit c968202

15 files changed

Lines changed: 911 additions & 325 deletions

README.md

Lines changed: 6 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
<!-- Organization Logo -->
55
<div align="center" style="display: flex; align-items: center; justify-content: center; gap: 16px;">
66
<img alt="Stability Nexus" src="public/stability.svg" width="175">
7-
<img src="public/todo-project-logo.svg" width="175" />
7+
<img alt="MiniChain" src="public/minichain.svg" width="175" />
88
</div>
99

1010
&nbsp;
1111

1212
<!-- Organization Name -->
1313
<div align="center">
1414

15-
[![Static Badge](https://img.shields.io/badge/Stability_Nexus-/TODO-228B22?style=for-the-badge&labelColor=FFC517)](https://TODO.stability.nexus/)
15+
[![Static Badge](https://img.shields.io/badge/Stability_Nexus-MiniChain-228B22?style=for-the-badge&labelColor=FFC517)](https://stability.nexus/)
1616

1717
<!-- Correct deployed url to be added -->
1818

@@ -32,9 +32,9 @@
3232
<a href="https://discord.gg/YzDKeEfWtS">
3333
<img src="https://img.shields.io/discord/995968619034984528?style=flat&logo=discord&logoColor=white&logoSize=auto&label=Discord&labelColor=5865F2&color=57F287" alt="Discord Badge"/></a>
3434
&nbsp;&nbsp;
35-
<!-- Medium -->
36-
<a href="https://news.stability.nexus/">
37-
<img src="https://img.shields.io/badge/Medium-black?style=flat&logo=medium&logoColor=black&logoSize=auto&color=white" alt="Medium Badge"></a>
35+
<!-- Blogs -->
36+
<a href="https://viewpoints.stability.nexus/">
37+
<img src="https://img.shields.io/badge/Stable_Viewpoints-Articles-2ea44f?style=flat&labelColor=facc15" alt="Stable Viewpoints"></a>
3838
&nbsp;&nbsp;
3939
<!-- LinkedIn -->
4040
<a href="https://linkedin.com/company/stability-nexus">
@@ -101,65 +101,12 @@ It is encouraged that you develop an initial prototype during the application ph
101101
* Read this book: https://www.marabu.dev/blockchain-foundations.pdf
102102

103103

104-
---
105-
106-
## Project Maturity
107-
108-
TODO: In the checklist below, mark the items that have been completed and delete items that are not applicable to the current project:
109-
110-
* [ ] The project has a logo.
111-
* [ ] The project has a favicon.
112-
* [ ] The protocol:
113-
- [ ] has been described and formally specified in a paper.
114-
- [ ] has had its main properties mathematically proven.
115-
- [ ] has been formally verified.
116-
* [ ] The smart contracts:
117-
- [ ] were thoroughly reviewed by at least two knights of The Stable Order.
118-
- [ ] were deployed to:
119-
- [ ] Ergo
120-
- [ ] Cardano
121-
- [ ] EVM Chains:
122-
- [ ] Ethereum Classic
123-
- [ ] Ethereum
124-
- [ ] Polygon
125-
- [ ] BSC
126-
- [ ] Base
127-
* [ ] The mobile app:
128-
- [ ] has an _About_ page containing the Stability Nexus's logo and pointing to the social media accounts of the Stability Nexus.
129-
- [ ] is available for download as a release in this repo.
130-
- [ ] is available in the relevant app stores.
131-
* [ ] The web frontend:
132-
- [ ] has proper title and metadata.
133-
- [ ] has proper open graph metadata, to ensure that it is shown well when shared in social media (Discord, Telegram, Twitter, LinkedIn).
134-
- [ ] has a footer, containing the Stability Nexus's logo and pointing to the social media accounts of the Stability Nexus.
135-
- [ ] is fully static and client-side.
136-
- [ ] is deployed to Github Pages via a Github Workflow.
137-
- [ ] is accessible through the https://TODO:PROJECT-NAME.stability.nexus domain.
138-
* [ ] the project is listed in [https://stability.nexus/protocols](https://stability.nexus/protocols).
139-
140104
---
141105

142106
## Tech Stack
143107

144108
TODO:
145109

146-
### Frontend
147-
148-
TODO:
149-
150-
- Next.js 14+ (React)
151-
- TypeScript
152-
- TailwindCSS
153-
- shadcn/ui
154-
155-
### Blockchain
156-
157-
TODO:
158-
159-
- Wagmi
160-
- Solidity Smart Contracts
161-
- Ethers.js
162-
163110
---
164111

165112
## Getting Started
@@ -168,63 +115,15 @@ TODO:
168115

169116
TODO
170117

171-
- Node.js 18+
172-
- npm/yarn/pnpm
173-
- MetaMask or any other web3 wallet browser extension
174-
175118
### Installation
176119

177120
TODO
178121

179-
#### 1. Clone the Repository
180-
181-
```bash
182-
git clone https://github.com/StabilityNexus/TODO.git
183-
cd TODO
184-
```
185-
186-
#### 2. Install Dependencies
187-
188-
Using your preferred package manager:
189-
190-
```bash
191-
npm install
192-
# or
193-
yarn install
194-
# or
195-
pnpm install
196-
```
197-
198-
#### 3. Run the Development Server
199-
200-
Start the app locally:
201-
202-
```bash
203-
npm run dev
204-
# or
205-
yarn dev
206-
# or
207-
pnpm dev
208-
```
209-
210-
#### 4. Open your Browser
211-
212-
Navigate to [http://localhost:3000](http://localhost:3000) to see the application.
213-
214122
---
215123

216124
## Contributing
217125

218-
We welcome contributions of all kinds! To contribute:
219-
220-
1. Fork the repository and create your feature branch (`git checkout -b feature/AmazingFeature`).
221-
2. Commit your changes (`git commit -m 'Add some AmazingFeature'`).
222-
3. Run the development workflow commands to ensure code quality:
223-
- `npm run format:write`
224-
- `npm run lint:fix`
225-
- `npm run typecheck`
226-
4. Push your branch (`git push origin feature/AmazingFeature`).
227-
5. Open a Pull Request for review.
126+
We welcome contributions of all kinds!
228127

229128
If you encounter bugs, need help, or have feature requests:
230129

genesis.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"chain_id": "minichain_testnet_1",
3+
"timestamp": 1716880000000,
4+
"difficulty": 4,
5+
"alloc": {
6+
"0000000000000000000000000000000000000001": {
7+
"balance": 1000000000
8+
},
9+
"0000000000000000000000000000000000000002": {
10+
"balance": 500000000
11+
}
12+
}
13+
}

main.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import argparse
2020
import asyncio
2121
import logging
22-
import os
2322
import re
2423
import sys
2524

@@ -77,6 +76,8 @@ def mine_and_process_block(chain, mempool, miner_pk):
7776
logger.info("No mineable transactions in current queue window.")
7877
return None
7978

79+
temp_state.credit_mining_reward(miner_pk)
80+
8081
block = Block(
8182
index=chain.last_block.index + 1,
8283
previous_hash=chain.last_block.hash,
@@ -89,7 +90,6 @@ def mine_and_process_block(chain, mempool, miner_pk):
8990
if chain.add_block(mined_block):
9091
logger.info("✅ Block #%d mined and added (%d txs)", mined_block.index, len(mineable_txs))
9192
mempool.remove_transactions(mineable_txs)
92-
chain.state.credit_mining_reward(miner_pk)
9393
return mined_block
9494
else:
9595
logger.error("❌ Block rejected by chain")
@@ -148,10 +148,6 @@ async def handler(data):
148148
if chain.add_block(block):
149149
logger.info("📥 Received Block #%d — added to chain", block.index)
150150

151-
# Apply mining reward for the remote miner (burn address as placeholder)
152-
miner = payload.get("miner", BURN_ADDRESS)
153-
chain.state.credit_mining_reward(miner)
154-
155151
# Drop only confirmed transactions so higher nonces can remain queued.
156152
mempool.remove_transactions(block.transactions)
157153
else:
@@ -295,11 +291,12 @@ async def run_node(port: int, host: str, connect_to: str | None, fund: int, data
295291

296292
# Load existing chain from disk, or start fresh
297293
chain = None
298-
if datadir and os.path.exists(os.path.join(datadir, "data.json")):
294+
if datadir:
299295
try:
300-
from minichain.persistence import load
301-
chain = load(datadir)
302-
logger.info("Restored chain from '%s'", datadir)
296+
from minichain.persistence import load, persistence_exists
297+
if persistence_exists(datadir):
298+
chain = load(datadir)
299+
logger.info("Restored chain from '%s'", datadir)
303300
except FileNotFoundError as e:
304301
logger.warning("Could not load saved chain: %s — starting fresh", e)
305302
except ValueError as e:

minichain/block.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ def __init__(
5959
self.difficulty: Optional[int] = difficulty
6060
self.nonce: int = 0
6161
self.hash: Optional[str] = None
62+
self.state_root: Optional[str] = state_root
63+
self.miner: Optional[str] = miner
6264

6365
# NEW: compute merkle root once
6466
self.merkle_root: Optional[str] = _calculate_merkle_root(self.transactions)
@@ -71,9 +73,11 @@ def to_header_dict(self):
7173
"index": self.index,
7274
"previous_hash": self.previous_hash,
7375
"merkle_root": self.merkle_root,
76+
"state_root": self.state_root,
7477
"timestamp": self.timestamp,
7578
"difficulty": self.difficulty,
7679
"nonce": self.nonce,
80+
"miner": self.miner,
7781
}
7882
# Include miner in header only when present (optional field) <-- Reworded comment
7983
if self.miner is not None:

minichain/chain.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from .pow import calculate_hash
44
import logging
55
import threading
6+
import json
7+
import os
8+
import sys
69

710
logger = logging.getLogger(__name__)
811

@@ -28,22 +31,61 @@ class Blockchain:
2831
Manages the blockchain, validates blocks, and commits state transitions.
2932
"""
3033

31-
def __init__(self):
34+
def __init__(self, genesis_path="genesis.json"):
3235
self.chain = []
3336
self.state = State()
3437
self._lock = threading.RLock()
35-
self._create_genesis_block()
38+
self._create_genesis_block(genesis_path)
3639

37-
def _create_genesis_block(self):
40+
def _create_genesis_block(self, genesis_path):
3841
"""
39-
Creates the genesis block with a fixed hash.
42+
Creates the genesis block and initializes state from config.
4043
"""
44+
config = {}
45+
if os.path.exists(genesis_path):
46+
try:
47+
with open(genesis_path, "r") as f:
48+
config = json.load(f)
49+
except Exception as e:
50+
logger.error(f"Failed to load genesis config: {e}")
51+
sys.exit(1)
52+
else:
53+
logger.error(f"Failed to load genesis config: file {genesis_path} does not exist.")
54+
sys.exit(1)
55+
56+
# Apply genesis allocations
57+
alloc = config.get("alloc", {})
58+
for address, data in alloc.items():
59+
balance = data.get("balance", 0)
60+
if not isinstance(balance, int) or balance < 0:
61+
logger.error(f"Invalid genesis balance for {address}: {balance}. Must be a non-negative integer.")
62+
sys.exit(1)
63+
account = self.state.get_account(address)
64+
account['balance'] = balance
65+
66+
timestamp = config.get("timestamp")
67+
difficulty = config.get("difficulty")
68+
4169
genesis_block = Block(
4270
index=0,
4371
previous_hash="0",
44-
transactions=[]
72+
transactions=[],
73+
timestamp=timestamp,
74+
difficulty=difficulty,
75+
state_root=self.state.state_root()
4576
)
46-
genesis_block.hash = "0" * 64
77+
78+
computed_hash = calculate_hash(genesis_block.to_header_dict())
79+
config_hash = config.get("hash")
80+
81+
if config_hash:
82+
if config_hash != computed_hash:
83+
logger.error(f"Genesis hash mismatch. Config hash: {config_hash}, Computed hash: {computed_hash}")
84+
sys.exit(1)
85+
genesis_block.hash = config_hash
86+
else:
87+
genesis_block.hash = computed_hash
88+
4789
self.chain.append(genesis_block)
4890

4991
@property
@@ -78,6 +120,14 @@ def add_block(self, block):
78120
logger.warning("Block %s rejected: Transaction failed validation", block.index)
79121
return False
80122

123+
if block.miner:
124+
temp_state.credit_mining_reward(block.miner)
125+
126+
# Verify state root
127+
if block.state_root != temp_state.state_root():
128+
logger.warning("Block %s rejected: Invalid state root. Expected %s, got %s", block.index, temp_state.state_root(), block.state_root)
129+
return False
130+
81131
# All transactions valid → commit state and append block
82132
self.state = temp_state
83133
self.chain.append(block)

0 commit comments

Comments
 (0)