- Update key capabilities to include GPU marketplace, payments, billing, and governance - Expand CLI section from basic examples to 12 command groups with 90+ subcommands - Add detailed test results table showing 208 passing tests across 6 test suites - Update documentation links to reference new CLI reference and coordinator API docs - Revise test commands to reflect actual test structure (
86 lines
2.4 KiB
Python
86 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from contextlib import asynccontextmanager
|
|
from typing import Optional
|
|
|
|
from .config import settings
|
|
from .consensus import PoAProposer, ProposerConfig, CircuitBreaker
|
|
from .database import init_db, session_scope
|
|
from .logger import get_logger
|
|
from .mempool import init_mempool
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class BlockchainNode:
|
|
def __init__(self) -> None:
|
|
self._stop_event = asyncio.Event()
|
|
self._proposer: Optional[PoAProposer] = None
|
|
|
|
async def start(self) -> None:
|
|
logger.info("Starting blockchain node", extra={"chain_id": settings.chain_id})
|
|
init_db()
|
|
init_mempool(
|
|
backend=settings.mempool_backend,
|
|
db_path=str(settings.db_path.parent / "mempool.db"),
|
|
max_size=settings.mempool_max_size,
|
|
min_fee=settings.min_fee,
|
|
)
|
|
self._start_proposer()
|
|
try:
|
|
await self._stop_event.wait()
|
|
finally:
|
|
await self._shutdown()
|
|
|
|
async def stop(self) -> None:
|
|
logger.info("Stopping blockchain node")
|
|
self._stop_event.set()
|
|
await self._shutdown()
|
|
|
|
def _start_proposer(self) -> None:
|
|
if self._proposer is not None:
|
|
return
|
|
|
|
proposer_config = ProposerConfig(
|
|
chain_id=settings.chain_id,
|
|
proposer_id=settings.proposer_id,
|
|
interval_seconds=settings.block_time_seconds,
|
|
max_block_size_bytes=settings.max_block_size_bytes,
|
|
max_txs_per_block=settings.max_txs_per_block,
|
|
)
|
|
cb = CircuitBreaker(
|
|
threshold=settings.circuit_breaker_threshold,
|
|
timeout=settings.circuit_breaker_timeout,
|
|
)
|
|
self._proposer = PoAProposer(config=proposer_config, session_factory=session_scope, circuit_breaker=cb)
|
|
asyncio.create_task(self._proposer.start())
|
|
|
|
async def _shutdown(self) -> None:
|
|
if self._proposer is None:
|
|
return
|
|
await self._proposer.stop()
|
|
self._proposer = None
|
|
|
|
|
|
@asynccontextmanager
|
|
async def node_app() -> asyncio.AbstractAsyncContextManager[BlockchainNode]: # type: ignore[override]
|
|
node = BlockchainNode()
|
|
try:
|
|
yield node
|
|
finally:
|
|
await node.stop()
|
|
|
|
|
|
def run() -> None:
|
|
asyncio.run(_run())
|
|
|
|
|
|
async def _run() -> None:
|
|
async with node_app() as node:
|
|
await node.start()
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
run()
|