diff --git a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py b/apps/blockchain-node/src/aitbc_chain/consensus/poa.py index f05827e1..e6549847 100755 --- a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py +++ b/apps/blockchain-node/src/aitbc_chain/consensus/poa.py @@ -120,10 +120,11 @@ class PoAProposer: return async def _propose_block(self) -> None: - # Check internal mempool + # Check internal mempool - but produce empty blocks to keep chain moving from ..mempool import get_mempool - if get_mempool().size(self._config.chain_id) == 0: - return + mempool_size = get_mempool().size(self._config.chain_id) + if mempool_size == 0: + self._logger.debug("No transactions in mempool, producing empty block") 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() diff --git a/apps/blockchain-node/src/aitbc_chain/main.py b/apps/blockchain-node/src/aitbc_chain/main.py index c3d2b1c4..5d4f3756 100755 --- a/apps/blockchain-node/src/aitbc_chain/main.py +++ b/apps/blockchain-node/src/aitbc_chain/main.py @@ -103,7 +103,7 @@ class BlockchainNode: if isinstance(tx_data, str): import json tx_data = json.loads(tx_data) - chain_id = tx_data.get("chain_id", "ait-devnet") + chain_id = tx_data.get("chain_id", settings.chain_id) mempool.add(tx_data, chain_id=chain_id) except Exception as exc: logger.error(f"Error processing transaction from gossip: {exc}") @@ -121,7 +121,7 @@ class BlockchainNode: if isinstance(block_data, str): import json block_data = json.loads(block_data) - chain_id = block_data.get("chain_id", "ait-devnet") + chain_id = block_data.get("chain_id", settings.chain_id) logger.info(f"Importing block for chain {chain_id}: {block_data.get('height')}") sync = ChainSync(session_factory=session_scope, chain_id=chain_id) res = sync.import_block(block_data) diff --git a/apps/blockchain-node/src/aitbc_chain/mempool.py b/apps/blockchain-node/src/aitbc_chain/mempool.py index b32b8e37..baf5a09b 100755 --- a/apps/blockchain-node/src/aitbc_chain/mempool.py +++ b/apps/blockchain-node/src/aitbc_chain/mempool.py @@ -38,7 +38,10 @@ class InMemoryMempool: self._max_size = max_size self._min_fee = min_fee - def add(self, tx: Dict[str, Any], chain_id: str = "ait-devnet") -> str: + def add(self, tx: Dict[str, Any], chain_id: str = None) -> str: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id fee = tx.get("fee", 0) if fee < self._min_fee: raise ValueError(f"Fee {fee} below minimum {self._min_fee}") @@ -59,11 +62,17 @@ class InMemoryMempool: metrics_registry.increment(f"mempool_tx_added_total_{chain_id}") return tx_hash - def list_transactions(self, chain_id: str = "ait-devnet") -> List[PendingTransaction]: + def list_transactions(self, chain_id: str = None) -> List[PendingTransaction]: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: return list(self._transactions.values()) - def drain(self, max_count: int, max_bytes: int, chain_id: str = "ait-devnet") -> List[PendingTransaction]: + def drain(self, max_count: int, max_bytes: int, chain_id: str = None) -> List[PendingTransaction]: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id """Drain transactions for block inclusion, prioritized by fee (highest first).""" with self._lock: sorted_txs = sorted( @@ -87,14 +96,20 @@ class InMemoryMempool: metrics_registry.increment(f"mempool_tx_drained_total_{chain_id}", float(len(result))) return result - def remove(self, tx_hash: str, chain_id: str = "ait-devnet") -> bool: + def remove(self, tx_hash: str, chain_id: str = None) -> bool: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: removed = self._transactions.pop(tx_hash, None) is not None if removed: metrics_registry.set_gauge("mempool_size", float(len(self._transactions))) return removed - def size(self, chain_id: str = "ait-devnet") -> int: + def size(self, chain_id: str = None) -> int: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: return len(self._transactions) @@ -135,7 +150,10 @@ class DatabaseMempool: self._conn.execute("CREATE INDEX IF NOT EXISTS idx_mempool_fee ON mempool(fee DESC)") self._conn.commit() - def add(self, tx: Dict[str, Any], chain_id: str = "ait-devnet") -> str: + def add(self, tx: Dict[str, Any], chain_id: str = None) -> str: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id fee = tx.get("fee", 0) if fee < self._min_fee: raise ValueError(f"Fee {fee} below minimum {self._min_fee}") @@ -169,7 +187,10 @@ class DatabaseMempool: self._update_gauge(chain_id) return tx_hash - def list_transactions(self, chain_id: str = "ait-devnet") -> List[PendingTransaction]: + def list_transactions(self, chain_id: str = None) -> List[PendingTransaction]: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: rows = self._conn.execute( "SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool WHERE chain_id = ? ORDER BY fee DESC, received_at ASC", @@ -182,7 +203,10 @@ class DatabaseMempool: ) for r in rows ] - def drain(self, max_count: int, max_bytes: int, chain_id: str = "ait-devnet") -> List[PendingTransaction]: + def drain(self, max_count: int, max_bytes: int, chain_id: str = None) -> List[PendingTransaction]: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: rows = self._conn.execute( "SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool WHERE chain_id = ? ORDER BY fee DESC, received_at ASC", @@ -214,7 +238,10 @@ class DatabaseMempool: self._update_gauge(chain_id) return result - def remove(self, tx_hash: str, chain_id: str = "ait-devnet") -> bool: + def remove(self, tx_hash: str, chain_id: str = None) -> bool: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: cursor = self._conn.execute("DELETE FROM mempool WHERE chain_id = ? AND tx_hash = ?", (chain_id, tx_hash)) self._conn.commit() @@ -223,11 +250,17 @@ class DatabaseMempool: self._update_gauge(chain_id) return removed - def size(self, chain_id: str = "ait-devnet") -> int: + def size(self, chain_id: str = None) -> int: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id with self._lock: return self._conn.execute("SELECT COUNT(*) FROM mempool WHERE chain_id = ?", (chain_id,)).fetchone()[0] - def _update_gauge(self, chain_id: str = "ait-devnet") -> None: + def _update_gauge(self, chain_id: str = None) -> None: + from .config import settings + if chain_id is None: + chain_id = settings.chain_id count = self._conn.execute("SELECT COUNT(*) FROM mempool WHERE chain_id = ?", (chain_id,)).fetchone()[0] metrics_registry.set_gauge(f"mempool_size_{chain_id}", float(count)) diff --git a/apps/blockchain-node/src/aitbc_chain/rpc/router.py b/apps/blockchain-node/src/aitbc_chain/rpc/router.py index 4fbd7250..95c75178 100755 --- a/apps/blockchain-node/src/aitbc_chain/rpc/router.py +++ b/apps/blockchain-node/src/aitbc_chain/rpc/router.py @@ -18,6 +18,13 @@ from ..models import Account, Block, Receipt, Transaction router = APIRouter() +def get_chain_id(chain_id: str = None) -> str: + """Get chain_id from parameter or use default from settings""" + if chain_id is None: + from ..config import settings + return settings.chain_id + return chain_id + def _serialize_receipt(receipt: Receipt) -> Dict[str, Any]: return { @@ -63,7 +70,14 @@ class EstimateFeeRequest(BaseModel): @router.get("/head", summary="Get current chain head") -async def get_head(chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_head(chain_id: str = None) -> Dict[str, Any]: + """Get current chain head""" + from ..config import settings as cfg + + # Use default chain_id from settings if not provided + if chain_id is None: + chain_id = cfg.chain_id + metrics_registry.increment("rpc_get_head_total") start = time.perf_counter() with session_scope() as session: @@ -157,7 +171,8 @@ async def get_blocks_range(start: int, end: int) -> Dict[str, Any]: @router.get("/tx/{tx_hash}", summary="Get transaction by hash") -async def get_transaction(tx_hash: str, chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_transaction(tx_hash: str, chain_id: str = None) -> Dict[str, Any]: + chain_id = get_chain_id(chain_id) metrics_registry.increment("rpc_get_transaction_total") start = time.perf_counter() with session_scope() as session: @@ -296,7 +311,7 @@ async def get_receipts(limit: int = 20, offset: int = 0) -> Dict[str, Any]: @router.get("/getBalance/{address}", summary="Get account balance") -async def get_balance(address: str, chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_balance(address: str, chain_id: str = None) -> Dict[str, Any]: metrics_registry.increment("rpc_get_balance_total") start = time.perf_counter() with session_scope() as session: @@ -442,7 +457,7 @@ async def get_addresses(limit: int = 20, offset: int = 0, min_balance: int = 0) @router.post("/sendTx", summary="Submit a new transaction") -async def send_transaction(request: TransactionRequest, chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def send_transaction(request: TransactionRequest, chain_id: str = None) -> Dict[str, Any]: metrics_registry.increment("rpc_send_tx_total") start = time.perf_counter() mempool = get_mempool() @@ -481,7 +496,7 @@ async def send_transaction(request: TransactionRequest, chain_id: str = "ait-dev @router.post("/submitReceipt", summary="Submit receipt claim transaction") -async def submit_receipt(request: ReceiptSubmissionRequest, chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def submit_receipt(request: ReceiptSubmissionRequest, chain_id: str = None) -> Dict[str, Any]: metrics_registry.increment("rpc_submit_receipt_total") start = time.perf_counter() tx_payload = { @@ -539,7 +554,7 @@ class ImportBlockRequest(BaseModel): @router.post("/importBlock", summary="Import a block from a remote peer") -async def import_block(request: ImportBlockRequest, chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def import_block(request: ImportBlockRequest, chain_id: str = None) -> Dict[str, Any]: from ..sync import ChainSync, ProposerSignatureValidator from ..config import settings as cfg @@ -578,7 +593,7 @@ async def import_block(request: ImportBlockRequest, chain_id: str = "ait-devnet" @router.get("/syncStatus", summary="Get chain sync status") -async def sync_status(chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def sync_status(chain_id: str = None) -> Dict[str, Any]: from ..sync import ChainSync from ..config import settings as cfg @@ -588,7 +603,7 @@ async def sync_status(chain_id: str = "ait-devnet") -> Dict[str, Any]: @router.get("/info", summary="Get blockchain information") -async def get_blockchain_info(chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_blockchain_info(chain_id: str = None) -> Dict[str, Any]: """Get comprehensive blockchain information""" from ..config import settings as cfg @@ -634,31 +649,45 @@ async def get_blockchain_info(chain_id: str = "ait-devnet") -> Dict[str, Any]: @router.get("/supply", summary="Get token supply information") -async def get_token_supply(chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_token_supply(chain_id: str = None) -> Dict[str, Any]: """Get token supply information""" from ..config import settings as cfg + # Use default chain_id from settings if not provided + if chain_id is None: + chain_id = cfg.chain_id + metrics_registry.increment("rpc_supply_total") start = time.perf_counter() with session_scope() as session: - # Simple implementation for now - response = { - "chain_id": chain_id, - "total_supply": 1000000000, # 1 billion from genesis - "circulating_supply": 0, # No transactions yet - "faucet_balance": 1000000000, # All tokens in faucet - "faucet_address": "ait1faucet000000000000000000000000000000000", - "mint_per_unit": cfg.mint_per_unit, - "total_accounts": 0 - } + # Production implementation - no faucet in mainnet + if chain_id == "ait-mainnet": + response = { + "chain_id": chain_id, + "total_supply": 1000000000, # 1 billion from genesis + "circulating_supply": 0, # No transactions yet + "mint_per_unit": cfg.mint_per_unit, + "total_accounts": 0 + } + else: + # Devnet with faucet + response = { + "chain_id": chain_id, + "total_supply": 1000000000, # 1 billion from genesis + "circulating_supply": 0, # No transactions yet + "faucet_balance": 1000000000, # All tokens in faucet + "faucet_address": "ait1faucet000000000000000000000000000000000", + "mint_per_unit": cfg.mint_per_unit, + "total_accounts": 0 + } metrics_registry.observe("rpc_supply_duration_seconds", time.perf_counter() - start) return response @router.get("/validators", summary="List blockchain validators") -async def get_validators(chain_id: str = "ait-devnet") -> Dict[str, Any]: +async def get_validators(chain_id: str = None) -> Dict[str, Any]: """List blockchain validators (authorities)""" from ..config import settings as cfg @@ -690,7 +719,7 @@ async def get_validators(chain_id: str = "ait-devnet") -> Dict[str, Any]: @router.get("/state", summary="Get blockchain state information") -async def get_chain_state(chain_id: str = "ait-devnet"): +async def get_chain_state(chain_id: str = None): """Get blockchain state information for a chain""" start = time.perf_counter() @@ -710,7 +739,7 @@ async def get_chain_state(chain_id: str = "ait-devnet"): @router.get("/rpc/getBalance/{address}", summary="Get account balance") -async def get_balance(address: str, chain_id: str = "ait-devnet"): +async def get_balance(address: str, chain_id: str = None): """Get account balance for a specific address""" start = time.perf_counter() @@ -753,7 +782,7 @@ async def get_balance(address: str, chain_id: str = "ait-devnet"): @router.get("/rpc/head", summary="Get current chain head") -async def get_head(chain_id: str = "ait-devnet"): +async def get_head(chain_id: str = None): """Get current chain head block""" start = time.perf_counter() @@ -799,7 +828,7 @@ async def get_head(chain_id: str = "ait-devnet"): @router.get("/rpc/transactions", summary="Get latest transactions") -async def get_transactions(chain_id: str = "ait-devnet", limit: int = 20, offset: int = 0): +async def get_transactions(chain_id: str = None, limit: int = 20, offset: int = 0): """Get latest transactions""" start = time.perf_counter()