consensus: improve block proposal timing and return status, fix transaction field mapping in sync
Some checks failed
Integration Tests / test-service-integration (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled

- Add initial sleep before first proposal to prevent immediate block creation
- Return bool from _propose_block to indicate if block was actually proposed
- Wait for next slot only after successful proposal, use regular interval after skipped proposals
- Add 1s sleep after proposal errors to prevent tight error loops
- Add parent_hash, proposer, and state_root fields to /rpc/blocks-range response
- Fix transaction field mapping
This commit is contained in:
aitbc
2026-04-10 16:10:45 +02:00
parent 632595b0ba
commit 68fa807256
3 changed files with 23 additions and 9 deletions

View File

@@ -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