diff --git a/.gitignore b/.gitignore index 9308a4b..13ba211 100644 --- a/.gitignore +++ b/.gitignore @@ -324,3 +324,9 @@ TSWLatexianTemp* # option is specified. Footnotes are the stored in a file with suffix Notes.bib. # Uncomment the next line to have this generated file ignored. #*Notes.bib + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so diff --git a/consensus/__init__.py b/consensus/__init__.py deleted file mode 100644 index 119baf4..0000000 --- a/consensus/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .pow import mine_block, calculate_hash, MiningExceededError - -__all__ = ["mine_block", "calculate_hash", "MiningExceededError"] \ No newline at end of file diff --git a/main.py b/main.py index d9670c0..add86c1 100644 --- a/main.py +++ b/main.py @@ -4,10 +4,7 @@ from nacl.signing import SigningKey from nacl.encoding import HexEncoder -from core import Transaction, Blockchain, Block, State -from node import Mempool -from network import P2PNetwork -from consensus import mine_block +from minichain import Transaction, Blockchain, Block, State, Mempool, P2PNetwork, mine_block logger = logging.getLogger(__name__) @@ -80,21 +77,20 @@ def sync_nonce(state, pending_nonce_map, address): async def node_loop(): logger.info("Starting MiniChain Node with Smart Contracts") - state = State() - chain = Blockchain(state) + chain = Blockchain() mempool = Mempool() pending_nonce_map = {} - def claim_nonce(address): - account = state.get_account(address) + def claim_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) next_nonce = max(account_nonce, local_nonce) pending_nonce_map[address] = next_nonce + 1 return next_nonce - network = P2PNetwork(None) + network = P2PNetwork() async def _handle_network_data(data): logger.info("Received network data: %s", data) @@ -127,10 +123,10 @@ async def _handle_network_data(data): except Exception: logger.exception("Error processing network data: %s", data) - network.handler_callback = _handle_network_data + network.register_handler(_handle_network_data) try: - await _run_node(network, state, chain, mempool, pending_nonce_map, claim_nonce) + await _run_node(network, chain, mempool, pending_nonce_map, claim_nonce) finally: await network.stop() diff --git a/core/__init__.py b/minichain/__init__.py similarity index 53% rename from core/__init__.py rename to minichain/__init__.py index ce204c7..a3e42ae 100644 --- a/core/__init__.py +++ b/minichain/__init__.py @@ -1,13 +1,21 @@ +from .pow import mine_block, calculate_hash, MiningExceededError from .block import Block from .chain import Blockchain from .transaction import Transaction from .state import State from .contract import ContractMachine +from .p2p import P2PNetwork +from .mempool import Mempool __all__ = [ + "mine_block", + "calculate_hash", + "MiningExceededError", "Block", "Blockchain", "Transaction", "State", "ContractMachine", + "P2PNetwork", + "Mempool", ] diff --git a/core/block.py b/minichain/block.py similarity index 98% rename from core/block.py rename to minichain/block.py index 23f7536..859a32a 100644 --- a/core/block.py +++ b/minichain/block.py @@ -2,8 +2,7 @@ import hashlib import json from typing import List, Optional -from core.transaction import Transaction - +from .transaction import Transaction def _sha256(data: str) -> str: return hashlib.sha256(data.encode()).hexdigest() diff --git a/core/chain.py b/minichain/chain.py similarity index 96% rename from core/chain.py rename to minichain/chain.py index 9545864..78ac73f 100644 --- a/core/chain.py +++ b/minichain/chain.py @@ -1,6 +1,6 @@ -from core.block import Block -from core.state import State -from consensus import calculate_hash +from .block import Block +from .state import State +from .pow import calculate_hash import logging import threading diff --git a/core/contract.py b/minichain/contract.py similarity index 100% rename from core/contract.py rename to minichain/contract.py diff --git a/node/mempool.py b/minichain/mempool.py similarity index 97% rename from node/mempool.py rename to minichain/mempool.py index 8bb941a..06a60d0 100644 --- a/node/mempool.py +++ b/minichain/mempool.py @@ -1,4 +1,4 @@ -from consensus.pow import calculate_hash +from .pow import calculate_hash import logging import threading diff --git a/network/p2p.py b/minichain/p2p.py similarity index 69% rename from network/p2p.py rename to minichain/p2p.py index ef0a9dd..aacbf49 100644 --- a/network/p2p.py +++ b/minichain/p2p.py @@ -19,16 +19,42 @@ class P2PNetwork: } """ - def __init__(self, handler_callback): + def __init__(self, handler_callback=None): + self._handler_callback = None + if handler_callback is not None: + self.register_handler(handler_callback) + self.pubsub = None # Will be set in real implementation + + def register_handler(self, handler_callback): if not callable(handler_callback): raise ValueError("handler_callback must be callable") - self.handler_callback = handler_callback - self.pubsub = None # Will be set in real implementation + self._handler_callback = handler_callback async def start(self): logger.info("Network: Listening on /ip4/0.0.0.0/tcp/0") # In real libp2p, we would await host.start() here + async def stop(self): + """Clean up network resources cleanly upon shutdown.""" + logger.info("Network: Shutting down") + if self.pubsub: + try: + shutdown_meth = None + for method_name in ('close', 'stop', 'aclose', 'shutdown'): + if hasattr(self.pubsub, method_name): + shutdown_meth = getattr(self.pubsub, method_name) + break + + if shutdown_meth: + import asyncio + res = shutdown_meth() + if asyncio.iscoroutine(res): + await res + except Exception as e: + logger.error("Network: Error shutting down pubsub: %s", e) + finally: + self.pubsub = None + async def _broadcast_message(self, topic, msg_type, payload): msg = json.dumps({"type": msg_type, "data": payload}) if self.pubsub: @@ -84,6 +110,9 @@ async def handle_message(self, msg): return try: - await self.handler_callback(data) + if self._handler_callback: + await self._handler_callback(data) + else: + logger.warning("Network Error: No handler_callback registered") except Exception: logger.exception("Error in network handler callback for data: %s", data) diff --git a/consensus/pow.py b/minichain/pow.py similarity index 100% rename from consensus/pow.py rename to minichain/pow.py diff --git a/core/state.py b/minichain/state.py similarity index 99% rename from core/state.py rename to minichain/state.py index 17bc68c..ce9a6f0 100644 --- a/core/state.py +++ b/minichain/state.py @@ -1,6 +1,6 @@ from nacl.hash import sha256 from nacl.encoding import HexEncoder -from core.contract import ContractMachine +from .contract import ContractMachine import copy import logging diff --git a/core/transaction.py b/minichain/transaction.py similarity index 100% rename from core/transaction.py rename to minichain/transaction.py diff --git a/network/__init__.py b/network/__init__.py deleted file mode 100644 index 742fbe2..0000000 --- a/network/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .p2p import P2PNetwork - -__all__ = ["P2PNetwork"] \ No newline at end of file diff --git a/node/__init__.py b/node/__init__.py deleted file mode 100644 index 434db3e..0000000 --- a/node/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .mempool import Mempool - -__all__ = ["Mempool"] \ No newline at end of file diff --git a/tests/test_contract.py b/tests/test_contract.py index 111222d..2ac6e9f 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -2,7 +2,7 @@ import sys import os -from core import State, Transaction +from minichain import State, Transaction from nacl.signing import SigningKey from nacl.encoding import HexEncoder diff --git a/tests/test_core.py b/tests/test_core.py index 1b7a189..0a818ed 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2,7 +2,7 @@ from nacl.signing import SigningKey from nacl.encoding import HexEncoder -from core import Transaction, Blockchain, State # Removed unused imports +from minichain import Transaction, Blockchain, State # Removed unused imports class TestCore(unittest.TestCase): def setUp(self):