From 310e564e12498c1e00784e9c6a14672a01b5855c Mon Sep 17 00:00:00 2001 From: siddhant Date: Thu, 28 May 2026 03:35:05 +0530 Subject: [PATCH 1/2] feat(core): implement external genesis configuration support --- genesis.json | 14 ++++++++++++++ minichain/chain.py | 33 +++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 genesis.json diff --git a/genesis.json b/genesis.json new file mode 100644 index 0000000..4d09efe --- /dev/null +++ b/genesis.json @@ -0,0 +1,14 @@ +{ + "chain_id": "minichain_testnet_1", + "timestamp": 1716880000000, + "difficulty": 4, + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "alloc": { + "0000000000000000000000000000000000000001": { + "balance": 1000000000 + }, + "0000000000000000000000000000000000000002": { + "balance": 500000000 + } + } +} diff --git a/minichain/chain.py b/minichain/chain.py index b65d575..c0814ac 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -3,6 +3,8 @@ from .pow import calculate_hash import logging import threading +import json +import os logger = logging.getLogger(__name__) @@ -28,22 +30,41 @@ class Blockchain: Manages the blockchain, validates blocks, and commits state transitions. """ - def __init__(self): + def __init__(self, genesis_path="genesis.json"): self.chain = [] self.state = State() self._lock = threading.RLock() - self._create_genesis_block() + self._create_genesis_block(genesis_path) - def _create_genesis_block(self): + def _create_genesis_block(self, genesis_path): """ - Creates the genesis block with a fixed hash. + Creates the genesis block and initializes state from config. """ + config = {} + if os.path.exists(genesis_path): + try: + with open(genesis_path, "r") as f: + config = json.load(f) + except Exception as e: + logger.error(f"Failed to load genesis config: {e}") + + # Apply genesis allocations + alloc = config.get("alloc", {}) + for address, data in alloc.items(): + account = self.state.get_account(address) + account['balance'] = data.get("balance", 0) + + timestamp = config.get("timestamp") + difficulty = config.get("difficulty") + genesis_block = Block( index=0, previous_hash="0", - transactions=[] + transactions=[], + timestamp=timestamp, + difficulty=difficulty ) - genesis_block.hash = "0" * 64 + genesis_block.hash = config.get("hash", "0" * 64) self.chain.append(genesis_block) @property From 568a521ae82bb3c599d7530881fbd5928bfa78cd Mon Sep 17 00:00:00 2001 From: siddhant Date: Thu, 28 May 2026 15:33:05 +0530 Subject: [PATCH 2/2] fix: genesis ititialisation security issues --- genesis.json | 1 - minichain/chain.py | 24 ++++++++++++++++++++++-- minichain/persistence.py | 5 ++++- tests/test_persistence.py | 4 ++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/genesis.json b/genesis.json index 4d09efe..fe44101 100644 --- a/genesis.json +++ b/genesis.json @@ -2,7 +2,6 @@ "chain_id": "minichain_testnet_1", "timestamp": 1716880000000, "difficulty": 4, - "hash": "0000000000000000000000000000000000000000000000000000000000000000", "alloc": { "0000000000000000000000000000000000000001": { "balance": 1000000000 diff --git a/minichain/chain.py b/minichain/chain.py index c0814ac..bea653b 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -5,6 +5,7 @@ import threading import json import os +import sys logger = logging.getLogger(__name__) @@ -47,12 +48,20 @@ def _create_genesis_block(self, genesis_path): config = json.load(f) except Exception as e: logger.error(f"Failed to load genesis config: {e}") + sys.exit(1) + else: + logger.error(f"Failed to load genesis config: file {genesis_path} does not exist.") + sys.exit(1) # Apply genesis allocations alloc = config.get("alloc", {}) for address, data in alloc.items(): + balance = data.get("balance", 0) + if not isinstance(balance, int) or balance < 0: + logger.error(f"Invalid genesis balance for {address}: {balance}. Must be a non-negative integer.") + sys.exit(1) account = self.state.get_account(address) - account['balance'] = data.get("balance", 0) + account['balance'] = balance timestamp = config.get("timestamp") difficulty = config.get("difficulty") @@ -64,7 +73,18 @@ def _create_genesis_block(self, genesis_path): timestamp=timestamp, difficulty=difficulty ) - genesis_block.hash = config.get("hash", "0" * 64) + + computed_hash = calculate_hash(genesis_block.to_header_dict()) + config_hash = config.get("hash") + + if config_hash: + if config_hash != computed_hash: + logger.error(f"Genesis hash mismatch. Config hash: {config_hash}, Computed hash: {computed_hash}") + sys.exit(1) + genesis_block.hash = config_hash + else: + genesis_block.hash = computed_hash + self.chain.append(genesis_block) @property diff --git a/minichain/persistence.py b/minichain/persistence.py index 10ec21b..6fa1fd6 100644 --- a/minichain/persistence.py +++ b/minichain/persistence.py @@ -126,8 +126,11 @@ def load(path: str = ".") -> Blockchain: def _verify_chain_integrity(blocks: list[Block]) -> None: """Verify genesis, hash linkage, and block hashes.""" genesis = blocks[0] - if genesis.index != 0 or genesis.hash != "0" * 64: + if genesis.index != 0: raise ValueError("Invalid genesis block") + from .pow import calculate_hash + if genesis.hash != calculate_hash(genesis.to_header_dict()): + raise ValueError("Invalid genesis block hash") for i in range(1, len(blocks)): block = blocks[i] diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 09ae89c..ff46454 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -89,7 +89,7 @@ def test_genesis_only_chain(self): save(bc, path=self.tmpdir) restored = load(path=self.tmpdir) self.assertEqual(len(restored.chain), 1) - self.assertEqual(restored.chain[0].hash, "0" * 64) + self.assertEqual(restored.chain[0].hash, bc.chain[0].hash) def test_state_snapshot_preserved(self): bc, alice_pk, bob_pk = self._chain_with_tx() @@ -254,7 +254,7 @@ def test_corrupt_sqlite_falls_back_to_legacy_json(self): restored = load(path=self.tmpdir) self.assertEqual(len(restored.chain), 1) - self.assertEqual(restored.chain[0].hash, "0" * 64) + self.assertEqual(restored.chain[0].hash, bc.chain[0].hash) if __name__ == "__main__":