diff --git a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py b/apps/blockchain-node/src/aitbc_chain/consensus/poa.py index 11ee4fa8..fecefde0 100755 --- a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py +++ b/apps/blockchain-node/src/aitbc_chain/consensus/poa.py @@ -96,14 +96,24 @@ class PoAProposer: self._task = None async def _run_loop(self) -> None: + # Initial sleep so we don't start proposing immediately + await asyncio.sleep(self._config.interval_seconds) while not self._stop_event.is_set(): - await self._wait_until_next_slot() if self._stop_event.is_set(): break try: - await self._propose_block() + proposed = await self._propose_block() + if proposed: + await self._wait_until_next_slot() + else: + # If we skipped proposing, wait a regular interval + try: + await asyncio.wait_for(self._stop_event.wait(), timeout=self._config.interval_seconds) + except asyncio.TimeoutError: + pass except Exception as exc: # pragma: no cover - defensive logging self._logger.exception("Failed to propose block", extra={"error": str(exc)}) + await asyncio.sleep(1.0) async def _wait_until_next_slot(self) -> None: head = self._fetch_chain_head() @@ -119,7 +129,7 @@ class PoAProposer: except asyncio.TimeoutError: return - async def _propose_block(self) -> None: + async def _propose_block(self) -> bool: # Check internal mempool and include transactions from ..mempool import get_mempool from ..models import Transaction, Account @@ -131,7 +141,7 @@ class PoAProposer: mempool_size = mempool.size(self._config.chain_id) if mempool_size == 0: self._logger.info(f"[PROPOSE] Skipping block proposal: mempool is empty (chain={self._config.chain_id})") - return + return False with self._session_factory() as session: head = session.exec(select(Block).where(Block.chain_id == self._config.chain_id).order_by(Block.height.desc()).limit(1)).first() @@ -283,6 +293,8 @@ class PoAProposer: }, ) + return True + async def _ensure_genesis_block(self) -> None: with self._session_factory() as session: # Check if genesis block already exists diff --git a/apps/blockchain-node/src/aitbc_chain/rpc/router.py b/apps/blockchain-node/src/aitbc_chain/rpc/router.py index 653c337c..dd7cd554 100755 --- a/apps/blockchain-node/src/aitbc_chain/rpc/router.py +++ b/apps/blockchain-node/src/aitbc_chain/rpc/router.py @@ -254,10 +254,13 @@ async def get_blocks_range(start: int = 0, end: int = 10, include_tx: bool = Tru block_data = { "height": b.height, "hash": b.hash, + "parent_hash": b.parent_hash, + "proposer": b.proposer, "timestamp": b.timestamp.isoformat(), "tx_count": b.tx_count, + "state_root": b.state_root, } - + if include_tx: # Fetch transactions for this block txs = session.exec( diff --git a/apps/blockchain-node/src/aitbc_chain/sync.py b/apps/blockchain-node/src/aitbc_chain/sync.py index 54126e6e..4e840b90 100755 --- a/apps/blockchain-node/src/aitbc_chain/sync.py +++ b/apps/blockchain-node/src/aitbc_chain/sync.py @@ -288,10 +288,9 @@ class ChainSync: # Import transactions if provided and apply state changes if transactions: for tx_data in transactions: - sender_addr = tx_data.get("sender", "") - payload = tx_data.get("payload", {}) or {} - recipient_addr = payload.get("to") or tx_data.get("recipient", "") - value = int(payload.get("value", 0) or 0) + sender_addr = tx_data.get("from", "") + recipient_addr = tx_data.get("to", "") + value = int(tx_data.get("amount", 0) or 0) fee = int(tx_data.get("fee", 0) or 0) tx_hash = tx_data.get("tx_hash", "")