From 15542b842ff5dde9b28bf054ed2ac82f8d523bcf Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 11:40:29 +0530 Subject: [PATCH 01/15] fixed difficulty target check in proof of work mining function --- main.py | 160 ++++++++++++++++++------------ minichain/chain.py | 7 +- minichain/consensus/difficulty.py | 60 +++++++++++ minichain/pow.py | 4 + 4 files changed, 165 insertions(+), 66 deletions(-) create mode 100644 minichain/consensus/difficulty.py diff --git a/main.py b/main.py index add86c1..54fe05b 100644 --- a/main.py +++ b/main.py @@ -1,29 +1,31 @@ import asyncio import logging import re +import time from nacl.signing import SigningKey -from nacl.encoding import HexEncoder - -from minichain import Transaction, Blockchain, Block, State, Mempool, P2PNetwork, mine_block +import nacl.encoding +# Local project imports +from minichain import Transaction, Blockchain, Block, mine_block, Mempool, P2PNetwork logger = logging.getLogger(__name__) BURN_ADDRESS = "0" * 40 +# ------------------------- +# Wallet Creation +# ------------------------- def create_wallet(): sk = SigningKey.generate() - pk = sk.verify_key.encode(encoder=HexEncoder).decode() + pk = sk.verify_key.encode(encoder=nacl.encoding.HexEncoder).decode() return sk, pk +# ------------------------- +# Mining + Block Processing +# ------------------------- def mine_and_process_block(chain, mempool, pending_nonce_map): - """ - Mine block and let Blockchain handle validation + state updates. - DO NOT manually apply transactions again. - """ - pending_txs = mempool.get_transactions_for_block() block = Block( @@ -32,57 +34,71 @@ def mine_and_process_block(chain, mempool, pending_nonce_map): transactions=pending_txs, ) + # PID Difficulty Adjustment (handled internally) + block.difficulty = chain.difficulty_adjuster.adjust( + chain.last_block.difficulty + ) + + start_time = time.time() mined_block = mine_block(block) + mining_time = time.time() - start_time + + # Attach mining time to block (optional but useful) + mined_block.mining_time = mining_time if not hasattr(mined_block, "miner"): mined_block.miner = BURN_ADDRESS - deployed_contracts: list[str] = [] + deployed_contracts = [] if chain.add_block(mined_block): - logger.info("Block #%s added", mined_block.index) + logger.info("Block #%s added with Difficulty: %s", + mined_block.index, + mined_block.difficulty) - miner_attr = getattr(mined_block, "miner", None) - if isinstance(miner_attr, str) and re.match(r'^[0-9a-fA-F]{40}$', miner_attr): - miner_address = miner_attr - else: - logger.warning("Invalid miner address. Crediting burn address.") - miner_address = BURN_ADDRESS + # Reward miner + miner_attr = getattr(mined_block, "miner", BURN_ADDRESS) + miner_address = ( + miner_attr if re.match(r'^[0-9a-fA-F]{40}$', str(miner_attr)) + else BURN_ADDRESS + ) - # Reward must go through chain.state chain.state.credit_mining_reward(miner_address) for tx in mined_block.transactions: sync_nonce(chain.state, pending_nonce_map, tx.sender) - # Track deployed contracts if your state.apply_transaction returns address result = chain.state.get_account(tx.receiver) if tx.receiver else None if isinstance(result, dict): deployed_contracts.append(tx.receiver) return mined_block, deployed_contracts + else: logger.error("Block rejected by chain") return None, [] +# ------------------------- +# Nonce Sync +# ------------------------- def sync_nonce(state, pending_nonce_map, address): account = state.get_account(address) - if account and "nonce" in account: - pending_nonce_map[address] = account["nonce"] - else: - pending_nonce_map[address] = 0 + pending_nonce_map[address] = account.get("nonce", 0) if account else 0 +# ------------------------- +# Node Logic +# ------------------------- async def node_loop(): - logger.info("Starting MiniChain Node with Smart Contracts") + logger.info("Starting MiniChain Node with PID Difficulty Adjustment") chain = Blockchain() mempool = Mempool() - + network = P2PNetwork() pending_nonce_map = {} - def claim_nonce(address) -> int: + def get_next_nonce(address) -> int: account = chain.state.get_account(address) account_nonce = account.get("nonce", 0) if account else 0 local_nonce = pending_nonce_map.get(address, account_nonce) @@ -90,11 +106,7 @@ def claim_nonce(address) -> int: pending_nonce_map[address] = next_nonce + 1 return next_nonce - network = P2PNetwork() - async def _handle_network_data(data): - logger.info("Received network data: %s", data) - try: if data["type"] == "tx": tx = Transaction(**data["data"]) @@ -103,60 +115,56 @@ async def _handle_network_data(data): elif data["type"] == "block": block_data = data["data"] - transactions_raw = block_data.get("transactions", []) - transactions = [Transaction(**tx_data) for tx_data in transactions_raw] + txs = [ + Transaction(**tx_d) + for tx_d in block_data.get("transactions", []) + ] block = Block( - index=block_data.get("index"), - previous_hash=block_data.get("previous_hash"), - transactions=transactions, + index=block_data["index"], + previous_hash=block_data["previous_hash"], + transactions=txs, timestamp=block_data.get("timestamp"), - difficulty=block_data.get("difficulty") + difficulty=block_data.get("difficulty"), ) block.nonce = block_data.get("nonce", 0) block.hash = block_data.get("hash") - if chain.add_block(block): - logger.info("Received block added to chain: #%s", block.index) + chain.add_block(block) - except Exception: - logger.exception("Error processing network data: %s", data) + except Exception as e: + logger.error(f"Network error: {e}") network.register_handler(_handle_network_data) try: - await _run_node(network, chain, mempool, pending_nonce_map, claim_nonce) + await _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce) finally: await network.stop() +# ------------------------- +# Run Node +# ------------------------- async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): await network.start() alice_sk, alice_pk = create_wallet() bob_sk, bob_pk = create_wallet() - logger.info("Alice Address: %s...", alice_pk[:10]) - logger.info("Bob Address: %s...", bob_pk[:10]) - - logger.info("[1] Genesis: Crediting Alice with 100 coins") + # Initial funding chain.state.credit_mining_reward(alice_pk, reward=100) sync_nonce(chain.state, pending_nonce_map, alice_pk) - # ------------------------------- - # Alice Payment - # ------------------------------- - - logger.info("[2] Transaction: Alice sends 10 coins to Bob") - - nonce = get_next_nonce(alice_pk) + # Alice sends Bob 10 coins + logger.info("[2] Alice sending 10 coins to Bob") tx_payment = Transaction( sender=alice_pk, receiver=bob_pk, amount=10, - nonce=nonce, + nonce=get_next_nonce(alice_pk), ) tx_payment.sign(alice_sk) @@ -164,29 +172,51 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): await network.broadcast_transaction(tx_payment) # ------------------------------- - # Mine Block 1 + # PID Demo: Mining 5 Blocks # ------------------------------- + logger.info("[3] Mining Multiple Blocks (Watch Difficulty Adjust)") - logger.info("[3] Mining Block 1") - mine_and_process_block(chain, mempool, pending_nonce_map) + for i in range(5): + await asyncio.sleep(1) - # ------------------------------- - # Final State Check - # ------------------------------- + logger.info(f"\nMining Block {i+1}") - logger.info("[4] Final State Check") + mined_block, _ = mine_and_process_block( + chain, mempool, pending_nonce_map + ) + if mined_block: + logger.info("Block mined in %.2f seconds", + mined_block.mining_time) + + logger.info("New difficulty: %s", + chain.last_block.difficulty) + + # Final balances alice_acc = chain.state.get_account(alice_pk) bob_acc = chain.state.get_account(bob_pk) - logger.info("Alice Balance: %s", alice_acc.get("balance", 0) if alice_acc else 0) - logger.info("Bob Balance: %s", bob_acc.get("balance", 0) if bob_acc else 0) + logger.info( + "Final Balances -> Alice: %s, Bob: %s", + alice_acc.get("balance", 0), + bob_acc.get("balance", 0), + ) +# ------------------------- +# Entry Point +# ------------------------- def main(): - logging.basicConfig(level=logging.INFO) - asyncio.run(node_loop()) + logging.basicConfig( + level=logging.INFO, + format='%(message)s' + ) + + try: + asyncio.run(node_loop()) + except KeyboardInterrupt: + pass if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/minichain/chain.py b/minichain/chain.py index 78ac73f..099df71 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -1,6 +1,7 @@ from .block import Block from .state import State from .pow import calculate_hash +from minichain.consensus.difficulty import PIDDifficultyAdjuster import logging import threading @@ -13,6 +14,8 @@ class Blockchain: """ def __init__(self): + self.difficulty = 3 + self.difficulty_adjuster = PIDDifficultyAdjuster(target_block_time=5) self.chain = [] self.state = State() self._lock = threading.RLock() @@ -74,4 +77,6 @@ def add_block(self, block): # All transactions valid → commit state and append block self.state = temp_state self.chain.append(block) - return True + self.difficulty = self.difficulty_adjuster.adjust(self.difficulty) + print("New difficulty: ", self.difficulty) + return True \ No newline at end of file diff --git a/minichain/consensus/difficulty.py b/minichain/consensus/difficulty.py new file mode 100644 index 0000000..43d5d24 --- /dev/null +++ b/minichain/consensus/difficulty.py @@ -0,0 +1,60 @@ +import time + +class PIDDifficultyAdjuster: + def __init__(self, target_block_time=5, kp=0.5, ki=0.05, kd=0.1): + self.target_block_time = target_block_time + # PID Coefficients + self.kp = kp + self.ki = ki + self.kd = kd + + self.integral = 0 + self.previous_error = 0 + self.last_block_time = time.time() + + # Limit the integral to prevent "Windup" + # This stops the difficulty from tanking if the network goes offline + self.integral_limit = 100 + + # Max percentage the difficulty can change in one block (e.g., 10%) + self.max_change_factor = 0.1 + + def adjust(self, current_difficulty, actual_block_time=None): + """ + Calculates the new difficulty based on the time since the last block. + """ + # --- FIX: Handle the case where current_difficulty is None --- + if current_difficulty is None: + current_difficulty = 1000 # Default starting difficulty + + if actual_block_time is None: + now = time.time() + actual_block_time = now - self.last_block_time + self.last_block_time = now + + # Error = Goal - Reality + error = self.target_block_time - actual_block_time + + # Update Integral with clamping (Anti-Windup) + self.integral = max(min(self.integral + error, self.integral_limit), -self.integral_limit) + + # Derivative: how fast is the error changing? + derivative = error - self.previous_error + self.previous_error = error + + # Calculate PID Adjustment + adjustment = ( + self.kp * error + + self.ki * self.integral + + self.kd * derivative + ) + + # Apply adjustment with a cap to maintain stability + # Now current_difficulty is guaranteed to be a number + max_delta = current_difficulty * self.max_change_factor + clamped_adjustment = max(min(adjustment, max_delta), -max_delta) + + new_difficulty = current_difficulty + int(clamped_adjustment) + + # Safety: Difficulty must never drop below 1 + return max(1, new_difficulty) \ No newline at end of file diff --git a/minichain/pow.py b/minichain/pow.py index b8484b1..7e5211b 100644 --- a/minichain/pow.py +++ b/minichain/pow.py @@ -55,10 +55,14 @@ def mine_block( header_dict["nonce"] = local_nonce block_hash = calculate_hash(header_dict) + #check difficulty target + target = "0" * difficulty + # Check difficulty target if block_hash.startswith(target): block.nonce = local_nonce # Assign only on success block.hash = block_hash + elapsed_time = time.monotonic() - start_time if logger: logger.info("Success! Hash: %s", block_hash) return block From c1f61f9c40ec282a3d03c1a6d8e1b76872ecccf4 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:19:57 +0530 Subject: [PATCH 02/15] Update minichain/pow.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- minichain/pow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minichain/pow.py b/minichain/pow.py index 7e5211b..7905a0c 100644 --- a/minichain/pow.py +++ b/minichain/pow.py @@ -62,9 +62,9 @@ def mine_block( if block_hash.startswith(target): block.nonce = local_nonce # Assign only on success block.hash = block_hash - elapsed_time = time.monotonic() - start_time if logger: - logger.info("Success! Hash: %s", block_hash) + elapsed_time = time.monotonic() - start_time + logger.info("Success! Hash: %s (%.3fs)", block_hash, elapsed_time) return block # Allow cancellation via progress callback (pass nonce explicitly) From 8ecc46d32238b2fd1e73cc7215fc6b309f03fe1a Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:20:44 +0530 Subject: [PATCH 03/15] Update main.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 54fe05b..8201c7b 100644 --- a/main.py +++ b/main.py @@ -151,7 +151,7 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): await network.start() alice_sk, alice_pk = create_wallet() - bob_sk, bob_pk = create_wallet() + _bob_sk, bob_pk = create_wallet() # Initial funding chain.state.credit_mining_reward(alice_pk, reward=100) From 48a2943a6966c328b7eed30c0882bfe6e630588b Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:28:57 +0530 Subject: [PATCH 04/15] Update main.py --- main.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 8201c7b..acc7605 100644 --- a/main.py +++ b/main.py @@ -34,13 +34,11 @@ def mine_and_process_block(chain, mempool, pending_nonce_map): transactions=pending_txs, ) - # PID Difficulty Adjustment (handled internally) - block.difficulty = chain.difficulty_adjuster.adjust( - chain.last_block.difficulty - ) + # Mine using current consensus difficulty; chain updates next difficulty after acceptance + block.difficulty = chain.difficulty start_time = time.time() - mined_block = mine_block(block) + mined_block = mine_block(block, difficulty=block.difficulty) mining_time = time.time() - start_time # Attach mining time to block (optional but useful) @@ -133,8 +131,8 @@ async def _handle_network_data(data): chain.add_block(block) - except Exception as e: - logger.error(f"Network error: {e}") + except Exception: ++ logger.exception("Network error while handling incoming data") network.register_handler(_handle_network_data) @@ -219,4 +217,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From e7c9879cf854b8c850f41b69d2893382e6a5a10a Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:36:16 +0530 Subject: [PATCH 05/15] Update chain.py --- minichain/chain.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/minichain/chain.py b/minichain/chain.py index 099df71..6cd65e9 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -63,6 +63,17 @@ def add_block(self, block): logger.warning("Block %s rejected: Invalid hash %s", block.index, block.hash) return False + # Enforce PoW difficulty + if block.difficulty != self.difficulty: + logger.warning( + "Block %s rejected: Invalid difficulty %s != %s", + block.index, block.difficulty, self.difficulty + ) + return False + if not block.hash.startswith("0" * self.difficulty): + logger.warning("Block %s rejected: Hash does not meet difficulty target", block.index) + return False + # Validate transactions on a temporary state copy temp_state = self.state.copy() @@ -78,5 +89,5 @@ def add_block(self, block): self.state = temp_state self.chain.append(block) self.difficulty = self.difficulty_adjuster.adjust(self.difficulty) - print("New difficulty: ", self.difficulty) - return True \ No newline at end of file + logger.info("New difficulty: %s", self.difficulty) + return True From 5d64086a93e4d910759de2440782d3d838977f7b Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:40:53 +0530 Subject: [PATCH 06/15] Update difficulty.py --- minichain/consensus/difficulty.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/minichain/consensus/difficulty.py b/minichain/consensus/difficulty.py index 43d5d24..0e4fdd4 100644 --- a/minichain/consensus/difficulty.py +++ b/minichain/consensus/difficulty.py @@ -10,7 +10,7 @@ def __init__(self, target_block_time=5, kp=0.5, ki=0.05, kd=0.1): self.integral = 0 self.previous_error = 0 - self.last_block_time = time.time() + self.last_block_time = time.monotonic() # Limit the integral to prevent "Windup" # This stops the difficulty from tanking if the network goes offline @@ -28,7 +28,7 @@ def adjust(self, current_difficulty, actual_block_time=None): current_difficulty = 1000 # Default starting difficulty if actual_block_time is None: - now = time.time() + now = time.monotonic() actual_block_time = now - self.last_block_time self.last_block_time = now @@ -51,10 +51,13 @@ def adjust(self, current_difficulty, actual_block_time=None): # Apply adjustment with a cap to maintain stability # Now current_difficulty is guaranteed to be a number - max_delta = current_difficulty * self.max_change_factor + max_delta = max(1, int(round(current_difficulty * self.max_change_factor))) clamped_adjustment = max(min(adjustment, max_delta), -max_delta) - new_difficulty = current_difficulty + int(clamped_adjustment) + delta = int(round(clamped_adjustment)) + if delta == 0 and clamped_adjustment != 0: + delta = 1 if clamped_adjustment > 0 else -1 + new_difficulty = current_difficulty + delta # Safety: Difficulty must never drop below 1 - return max(1, new_difficulty) \ No newline at end of file + return max(1, new_difficulty) From 96aa5cbbbb7c1e52812761f0ac5a9818759c2cc4 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 1 Mar 2026 12:42:20 +0530 Subject: [PATCH 07/15] Update pow.py --- minichain/pow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minichain/pow.py b/minichain/pow.py index 7905a0c..6540413 100644 --- a/minichain/pow.py +++ b/minichain/pow.py @@ -55,8 +55,8 @@ def mine_block( header_dict["nonce"] = local_nonce block_hash = calculate_hash(header_dict) - #check difficulty target - target = "0" * difficulty + # Check difficulty target + if block_hash.startswith(target): # Check difficulty target if block_hash.startswith(target): From 4f1b92bbf5fc7b301ea7ac42cdbdceb4c02501b6 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sat, 7 Mar 2026 19:35:41 +0530 Subject: [PATCH 08/15] Update minichain/chain.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- minichain/chain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minichain/chain.py b/minichain/chain.py index 6cd65e9..9008029 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -89,5 +89,6 @@ def add_block(self, block): self.state = temp_state self.chain.append(block) self.difficulty = self.difficulty_adjuster.adjust(self.difficulty) - logger.info("New difficulty: %s", self.difficulty) + logger.info("New difficulty: %s", self.difficulty) + return True return True From b206d82aa333a843b56b08015998ae841cd02782 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 8 Mar 2026 12:28:10 +0530 Subject: [PATCH 09/15] Update main.py --- main.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/main.py b/main.py index e032157..2b4a8c7 100644 --- a/main.py +++ b/main.py @@ -324,12 +324,9 @@ async def _handle_network_data(data): difficulty=block_data.get("difficulty"), ) - await network.start(port=port) - - chain.add_block(block) - - except Exception: -+ logger.exception("Network error while handling incoming data") + chain.add_block(block) + except Exception: + logger.exception("Network error while handling incoming data") # Nonce counter kept as a mutable list so the CLI closure can mutate it nonce_counter = [0] @@ -362,7 +359,8 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): amount=10, nonce=get_next_nonce(alice_pk), ) - + tx_payment.sign(alice_sk) + mempool.add_transaction(tx_payment) # ------------------------------- # PID Demo: Mining 5 Blocks # ------------------------------- @@ -373,16 +371,19 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): logger.info(f"\nMining Block {i+1}") - mined_block, _ = mine_and_process_block( - chain, mempool, pending_nonce_map - ) + mined = mine_and_process_block(chain, mempool, pending_nonce_map) + if not mined: + logger.info("No pending transactions to mine in this iteration") + continue + mined_block, _ = mined + if mined_block: logger.info("Block mined in %.2f seconds", mined_block.mining_time) logger.info("New difficulty: %s", - chain.last_block.difficulty) + chain.difficulty) # Final balances alice_acc = chain.state.get_account(alice_pk) From 67a5b82dd5faf9f3060f6fa2ea726bfb6968842c Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 8 Mar 2026 12:34:19 +0530 Subject: [PATCH 10/15] Update chain.py --- minichain/chain.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/minichain/chain.py b/minichain/chain.py index 9008029..b5ce514 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -86,9 +86,14 @@ def add_block(self, block): return False # All transactions valid → commit state and append block + previous_timestamp = self.last_block.timestamp self.state = temp_state self.chain.append(block) - self.difficulty = self.difficulty_adjuster.adjust(self.difficulty) - logger.info("New difficulty: %s", self.difficulty) - return True - return True + actual_block_time = max(0, (block.timestamp - previous_timestamp) / 1000) + self.difficulty = self.difficulty_adjuster.adjust( + self.difficulty, + actual_block_time=actual_block_time, + ) + logger.info("New difficulty: %s", self.difficulty) + return True + return True From b1377d2fd97908faad300d2d9bb3880269dcf1a5 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Sun, 8 Mar 2026 12:47:56 +0530 Subject: [PATCH 11/15] Update minichain/chain.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- minichain/chain.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/minichain/chain.py b/minichain/chain.py index b5ce514..3b346d5 100644 --- a/minichain/chain.py +++ b/minichain/chain.py @@ -91,9 +91,8 @@ def add_block(self, block): self.chain.append(block) actual_block_time = max(0, (block.timestamp - previous_timestamp) / 1000) self.difficulty = self.difficulty_adjuster.adjust( - self.difficulty, - actual_block_time=actual_block_time, - ) - logger.info("New difficulty: %s", self.difficulty) - return True - return True + self.difficulty, + actual_block_time=actual_block_time, + ) + logger.info("New difficulty: %s", self.difficulty) + return True From 1fb37ae0b6f3e277ff942f347d2f2122a19c785e Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Mon, 9 Mar 2026 10:14:42 +0530 Subject: [PATCH 12/15] Update main.py --- main.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 2b4a8c7..34d0297 100644 --- a/main.py +++ b/main.py @@ -353,14 +353,6 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): # Alice sends Bob 10 coins logger.info("[2] Alice sending 10 coins to Bob") - tx_payment = Transaction( - sender=alice_pk, - receiver=bob_pk, - amount=10, - nonce=get_next_nonce(alice_pk), - ) - tx_payment.sign(alice_sk) - mempool.add_transaction(tx_payment) # ------------------------------- # PID Demo: Mining 5 Blocks # ------------------------------- @@ -368,6 +360,14 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): for i in range(5): await asyncio.sleep(1) + tx_payment = Transaction( + sender=alice_pk, + receiver=bob_pk, + amount=10, + nonce=get_next_nonce(alice_pk), + ) + tx_payment.sign(alice_sk) + mempool.add_transaction(tx_payment) logger.info(f"\nMining Block {i+1}") From 560a82d3d70f4d921de1a070f1a42c50bc776789 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Mon, 9 Mar 2026 10:17:23 +0530 Subject: [PATCH 13/15] Update pow.py --- minichain/pow.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/minichain/pow.py b/minichain/pow.py index 6540413..12aa367 100644 --- a/minichain/pow.py +++ b/minichain/pow.py @@ -55,9 +55,6 @@ def mine_block( header_dict["nonce"] = local_nonce block_hash = calculate_hash(header_dict) - # Check difficulty target - if block_hash.startswith(target): - # Check difficulty target if block_hash.startswith(target): block.nonce = local_nonce # Assign only on success From cd7c8d98e23dc279db3a257865acfc709be2574a Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Mon, 9 Mar 2026 10:20:39 +0530 Subject: [PATCH 14/15] Update main.py --- main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 34d0297..19dbe9e 100644 --- a/main.py +++ b/main.py @@ -323,8 +323,11 @@ async def _handle_network_data(data): timestamp=block_data.get("timestamp"), difficulty=block_data.get("difficulty"), ) + block.nonce = block_data.get("nonce", 0) + block.hash = block_data.get("hash") + block.miner = block_data.get("miner", BURN_ADDRESS) - chain.add_block(block) + chain.add_block(block) except Exception: logger.exception("Network error while handling incoming data") From 0a2d556b4d13f93a514af4ec521de604bcb1ccb6 Mon Sep 17 00:00:00 2001 From: anshulchikhale30p Date: Mon, 9 Mar 2026 10:28:50 +0530 Subject: [PATCH 15/15] Update main.py --- main.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 19dbe9e..1400211 100644 --- a/main.py +++ b/main.py @@ -402,14 +402,67 @@ async def _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce): # ------------------------- # Entry Point # ------------------------- + +async def start_interactive_node(port=None, connect=None): + chain = Blockchain() + mempool = Mempool() + network = P2PNetwork() + pending_nonce_map = {} + + sk, pk = create_wallet() + + nonce_counter = [0] + + await network.start(port=port) + + if connect: + host, port_str = connect.rsplit(":", 1) + await network.connect_to_peer(host, int(port_str)) + + try: + await cli_loop(sk, pk, chain, mempool, network, nonce_counter) + finally: + await network.stop() + + +async def run_demo(): + chain = Blockchain() + mempool = Mempool() + network = P2PNetwork() + pending_nonce_map = {} + + await network.start() + + def get_next_nonce(address): + account = chain.state.get_account(address) + account_nonce = account.get("nonce", 0) if account else 0 + local_nonce = pending_nonce_map.get(address, account_nonce) + next_nonce = max(account_nonce, local_nonce) + pending_nonce_map[address] = next_nonce + 1 + return next_nonce + + try: + await _run_node(network, chain, mempool, pending_nonce_map, get_next_nonce) + finally: + await network.stop() + + def main(): - logging.basicConfig( - level=logging.INFO, - format='%(message)s' - ) + parser = argparse.ArgumentParser(description="MiniChain Node") + + parser.add_argument("--port", type=int, help="Port to run node") + parser.add_argument("--connect", help="Peer to connect to host:port") + parser.add_argument("--demo", action="store_true", help="Run Alice/Bob demo") + + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO, format="%(message)s") try: - asyncio.run(node_loop()) + if args.demo: + asyncio.run(run_demo()) + else: + asyncio.run(start_interactive_node(args.port, args.connect)) except KeyboardInterrupt: pass