From dd5c7f0e22499608f8a5f2802dfc97050242cf55 Mon Sep 17 00:00:00 2001 From: oib Date: Thu, 26 Feb 2026 23:26:59 +0100 Subject: [PATCH] fix: resolve explorer transaction search, timestamp formatting, and optimize RPC total_count queries --- apps/blockchain-explorer/main.py | 28 +++++++++++++++++++ .../src/aitbc_chain/rpc/router.py | 14 ++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/apps/blockchain-explorer/main.py b/apps/blockchain-explorer/main.py index 2d383efa..051c0a97 100644 --- a/apps/blockchain-explorer/main.py +++ b/apps/blockchain-explorer/main.py @@ -418,6 +418,34 @@ async def api_block(height: int): return await get_block(height) + +@app.get("/api/transactions/{tx_hash}") +async def api_transaction(tx_hash: str): + """API endpoint for transaction data, normalized for frontend""" + async with httpx.AsyncClient() as client: + try: + response = await client.get(f"{BLOCKCHAIN_RPC_URL}/tx/{tx_hash}") + if response.status_code == 200: + tx = response.json() + # Normalize for frontend expectations + payload = tx.get("payload", {}) + return { + "hash": tx.get("tx_hash"), + "block_height": tx.get("block_height"), + "from": tx.get("sender"), + "to": tx.get("recipient"), + "type": payload.get("type", "transfer"), + "amount": payload.get("amount", 0), + "fee": payload.get("fee", 0), + "timestamp": tx.get("created_at") + } + elif response.status_code == 404: + raise HTTPException(status_code=404, detail="Transaction not found") + except httpx.RequestError as e: + print(f"Error fetching transaction: {e}") + raise HTTPException(status_code=500, detail="Internal server error") + + @app.get("/health") async def health(): """Health check endpoint""" diff --git a/apps/blockchain-node/src/aitbc_chain/rpc/router.py b/apps/blockchain-node/src/aitbc_chain/rpc/router.py index 37da77d4..c290fd03 100644 --- a/apps/blockchain-node/src/aitbc_chain/rpc/router.py +++ b/apps/blockchain-node/src/aitbc_chain/rpc/router.py @@ -1,4 +1,5 @@ from __future__ import annotations +from sqlalchemy import func import asyncio import json @@ -200,8 +201,9 @@ async def get_transactions(limit: int = 20, offset: int = 0) -> Dict[str, Any]: .limit(limit) ).all() - # Get total count for pagination info - total_count = len(session.exec(select(Transaction)).all()) +# Get total count for pagination info using optimized SQL count + total_count = session.exec(select(func.count()).select_from(Transaction)).one() + if not transactions: metrics_registry.increment("rpc_get_transactions_empty_total") @@ -350,8 +352,8 @@ async def get_address_details(address: str, limit: int = 20, offset: int = 0) -> ).all() # Get total counts - total_sent = len(session.exec(select(Transaction).where(Transaction.sender == address)).all()) - total_received = len(session.exec(select(Transaction).where(Transaction.recipient == address)).all()) + total_sent = session.exec(select(func.count()).select_from(Transaction).where(Transaction.sender == address)).one() + total_received = session.exec(select(func.count()).select_from(Transaction).where(Transaction.recipient == address)).one() # Serialize transactions serialize_tx = lambda tx: { @@ -419,8 +421,8 @@ async def get_addresses(limit: int = 20, offset: int = 0, min_balance: int = 0) address_list = [] for addr in addresses: # Get transaction counts - sent_count = len(session.exec(select(Transaction).where(Transaction.sender == addr.address)).all()) - received_count = len(session.exec(select(Transaction).where(Transaction.recipient == addr.address)).all()) + sent_count = session.exec(select(func.count()).select_from(Transaction).where(Transaction.sender == addr.address)).one() + received_count = session.exec(select(func.count()).select_from(Transaction).where(Transaction.recipient == addr.address)).one() address_list.append({ "address": addr.address,