From 9b56a4c8d818dc5b58425bbd50dc386fed3aefac Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Mon, 9 Mar 2026 11:28:46 +0000 Subject: [PATCH] Lints and prettify. --- .github/workflows/publish-docker-image.yml | 2 +- src/api/v1/blocks.py | 16 +++++---- src/api/v1/transactions.py | 20 +++++------ src/db/blocks.py | 36 ++++--------------- src/db/transaction.py | 4 ++- .../api/serializers/signed_transaction.py | 6 +++- src/node/lifespan.py | 4 ++- static/app.js | 6 ++-- static/components/BlocksTable.js | 7 +++- static/components/TransactionsTable.js | 13 +++++-- static/lib/api.js | 3 +- static/pages/Home.js | 4 ++- static/pages/TransactionDetail.js | 24 +++++++++---- static/styles.css | 14 ++++++-- tests/test_block_forks.py | 1 + 15 files changed, 92 insertions(+), 68 deletions(-) diff --git a/.github/workflows/publish-docker-image.yml b/.github/workflows/publish-docker-image.yml index 7ac0856..d8ac549 100644 --- a/.github/workflows/publish-docker-image.yml +++ b/.github/workflows/publish-docker-image.yml @@ -38,7 +38,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: ./Dockerfile + file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: | diff --git a/src/api/v1/blocks.py b/src/api/v1/blocks.py index 9138443..ab00e93 100644 --- a/src/api/v1/blocks.py +++ b/src/api/v1/blocks.py @@ -24,13 +24,15 @@ async def list_blocks( blocks, total_count = await request.app.state.block_repository.get_paginated(page, page_size, fork=fork) total_pages = (total_count + page_size - 1) // page_size # ceiling division - return JSONResponse({ - "blocks": [BlockRead.from_block(block).model_dump(mode="json") for block in blocks], - "page": page, - "page_size": page_size, - "total_count": total_count, - "total_pages": total_pages, - }) + return JSONResponse( + { + "blocks": [BlockRead.from_block(block).model_dump(mode="json") for block in blocks], + "page": page, + "page_size": page_size, + "total_count": total_count, + "total_pages": total_pages, + } + ) async def _get_blocks_stream_serialized( diff --git a/src/api/v1/transactions.py b/src/api/v1/transactions.py index f3319a3..57729d8 100644 --- a/src/api/v1/transactions.py +++ b/src/api/v1/transactions.py @@ -47,18 +47,18 @@ async def list_transactions( page_size: int = Query(10, ge=1, le=100, alias="page-size"), fork: int = Query(...), ) -> Response: - transactions, total_count = await request.app.state.transaction_repository.get_paginated( - page, page_size, fork=fork - ) + transactions, total_count = await request.app.state.transaction_repository.get_paginated(page, page_size, fork=fork) total_pages = (total_count + page_size - 1) // page_size - return JSONResponse({ - "transactions": [TransactionRead.from_transaction(tx).model_dump(mode="json") for tx in transactions], - "page": page, - "page_size": page_size, - "total_count": total_count, - "total_pages": total_pages, - }) + return JSONResponse( + { + "transactions": [TransactionRead.from_transaction(tx).model_dump(mode="json") for tx in transactions], + "page": page, + "page_size": page_size, + "total_count": total_count, + "total_pages": total_pages, + } + ) async def get(request: NBERequest, transaction_hash: str, fork: int = Query(...)) -> Response: diff --git a/src/db/blocks.py b/src/db/blocks.py index 7ab98e9..99721a7 100644 --- a/src/db/blocks.py +++ b/src/db/blocks.py @@ -3,16 +3,13 @@ from asyncio import sleep from typing import AsyncIterator, Dict, List from rusty_results import Empty, Option, Some -from sqlalchemy import Result, Select +from sqlalchemy import Result, Select, func as sa_func from sqlalchemy.orm import aliased from sqlmodel import select -from sqlalchemy import func as sa_func - from db.clients import DbClient from models.block import Block - logger = logging.getLogger(__name__) @@ -25,33 +22,18 @@ def chain_block_ids_cte(*, fork: int): from fork 0 at height 50, the CTE returns fork 1 blocks (50+) AND the ancestor fork 0 blocks (0–49). """ - tip_hash = ( - select(Block.hash) - .where(Block.fork == fork) - .order_by(Block.height.desc()) - .limit(1) - .scalar_subquery() - ) + tip_hash = select(Block.hash).where(Block.fork == fork).order_by(Block.height.desc()).limit(1).scalar_subquery() - base = select(Block.id, Block.hash, Block.parent_block).where( - Block.hash == tip_hash - ) + base = select(Block.id, Block.hash, Block.parent_block).where(Block.hash == tip_hash) cte = base.cte(name="chain", recursive=True) - recursive = select(Block.id, Block.hash, Block.parent_block).where( - Block.hash == cte.c.parent_block - ) + recursive = select(Block.id, Block.hash, Block.parent_block).where(Block.hash == cte.c.parent_block) return cte.union_all(recursive) def get_latest_statement(limit: int, *, fork: int, output_ascending: bool = True) -> Select: chain = chain_block_ids_cte(fork=fork) - base = ( - select(Block) - .join(chain, Block.id == chain.c.id) - .order_by(Block.height.desc()) - .limit(limit) - ) + base = select(Block).join(chain, Block.id == chain.c.id).order_by(Block.height.desc()).limit(limit) if not output_ascending: return base @@ -191,9 +173,7 @@ class BlockRepository: parent_hashes_in_batch = {b.parent_block for b in blocks_to_add} parents_with_children: set[bytes] = set() if parent_hashes_in_batch: - stmt = select(Block.parent_block).where( - Block.parent_block.in_(parent_hashes_in_batch) - ).distinct() + stmt = select(Block.parent_block).where(Block.parent_block.in_(parent_hashes_in_batch)).distinct() for ph in session.exec(stmt).all(): parents_with_children.add(ph) @@ -310,9 +290,7 @@ class BlockRepository: while True: statement = ( - select(Block) - .where(Block.fork == fork, Block.height >= height_cursor) - .order_by(Block.height.asc()) + select(Block).where(Block.fork == fork, Block.height >= height_cursor).order_by(Block.height.asc()) ) with self.client.session() as session: diff --git a/src/db/transaction.py b/src/db/transaction.py index 9aee8f7..bb4e55d 100644 --- a/src/db/transaction.py +++ b/src/db/transaction.py @@ -74,7 +74,9 @@ class TransactionRepository: if limit == 0: return [] - statement = get_latest_statement(limit, fork=fork, output_ascending=ascending, preload_relationships=preload_relationships) + statement = get_latest_statement( + limit, fork=fork, output_ascending=ascending, preload_relationships=preload_relationships + ) with self.client.session() as session: results: Result[Transaction] = session.exec(statement) diff --git a/src/node/api/serializers/signed_transaction.py b/src/node/api/serializers/signed_transaction.py index 5915c24..8ffe9fd 100644 --- a/src/node/api/serializers/signed_transaction.py +++ b/src/node/api/serializers/signed_transaction.py @@ -83,5 +83,9 @@ class SignedTransactionSerializer(NbeSerializer, FromRandom): n = len(transaction.operations_contents) operations_proofs = [OperationProofSerializer.from_random() for _ in range(n)] return cls.model_validate( - {"mantle_tx": transaction, "ops_proofs": operations_proofs, "ledger_tx_proof": Groth16ProofSerializer.from_random()} + { + "mantle_tx": transaction, + "ops_proofs": operations_proofs, + "ledger_tx_proof": Groth16ProofSerializer.from_random(), + } ) diff --git a/src/node/lifespan.py b/src/node/lifespan.py index d202f38..0ac8ae0 100644 --- a/src/node/lifespan.py +++ b/src/node/lifespan.py @@ -155,7 +155,9 @@ async def subscribe_to_new_blocks(app: "NBE"): # Re-check if parent now exists after backfill parent_exists = (await app.state.block_repository.get_by_hash(block.parent_block)).is_some if not parent_exists: - logger.warning(f"Parent block still not found after backfill for block at slot {block.slot}. Skipping block.") + logger.warning( + f"Parent block still not found after backfill for block at slot {block.slot}. Skipping block." + ) continue # Capture values before create() detaches the block from the session diff --git a/static/app.js b/static/app.js index d8457c9..cd7bf3b 100644 --- a/static/app.js +++ b/static/app.js @@ -9,11 +9,11 @@ import TransactionDetailPage from './pages/TransactionDetail.js'; const ROOT = document.getElementById('app'); - // Detect the Base Path from the HTML tag. - // If the tag is missing or equals "__BASE_PATH__", default to root "/". +// Detect the Base Path from the HTML tag. +// If the tag is missing or equals "__BASE_PATH__", default to root "/". const BASE_PATH = (() => { const baseHref = document.querySelector('base')?.getAttribute('href'); - if (!baseHref || baseHref === "__BASE_PATH__") return '/'; + if (!baseHref || baseHref === '__BASE_PATH__') return '/'; return baseHref.endsWith('/') ? baseHref : `${baseHref}/`; })(); diff --git a/static/components/BlocksTable.js b/static/components/BlocksTable.js index d9c822c..bed272e 100644 --- a/static/components/BlocksTable.js +++ b/static/components/BlocksTable.js @@ -210,7 +210,12 @@ export default function BlocksTable({ live, onDisableLive }) { h( 'div', { class: 'card-header', style: 'display:flex; justify-content:space-between; align-items:center;' }, - h('div', null, h('strong', null, 'Blocks '), !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount))), + h( + 'div', + null, + h('strong', null, 'Blocks '), + !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount)), + ), ), h( 'div', diff --git a/static/components/TransactionsTable.js b/static/components/TransactionsTable.js index fd2e3fb..8e751a2 100644 --- a/static/components/TransactionsTable.js +++ b/static/components/TransactionsTable.js @@ -262,7 +262,12 @@ export default function TransactionsTable({ live, onDisableLive }) { h( 'div', { class: 'card-header', style: 'display:flex; justify-content:space-between; align-items:center;' }, - h('div', null, h('strong', null, 'Transactions '), !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount))), + h( + 'div', + null, + h('strong', null, 'Transactions '), + !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount)), + ), ), h( 'div', @@ -310,7 +315,11 @@ export default function TransactionsTable({ live, onDisableLive }) { h( 'span', { style: 'color:var(--muted); font-size:13px;' }, - live ? 'Streaming live transactions...' : totalPages > 0 ? `Page ${page + 1} of ${totalPages}` : 'No transactions', + live + ? 'Streaming live transactions...' + : totalPages > 0 + ? `Page ${page + 1} of ${totalPages}` + : 'No transactions', ), h( 'button', diff --git a/static/lib/api.js b/static/lib/api.js index c47be9d..effd18b 100644 --- a/static/lib/api.js +++ b/static/lib/api.js @@ -15,8 +15,7 @@ const TRANSACTIONS_STREAM = joinUrl(API_PREFIX, 'transactions/stream'); const FORK_CHOICE = joinUrl(API_PREFIX, 'fork-choice'); const BLOCK_DETAIL_BY_HASH = (hash) => joinUrl(API_PREFIX, 'blocks', encodeHash(hash)); -const BLOCKS_STREAM = (fork) => - `${joinUrl(API_PREFIX, 'blocks/stream')}?fork=${encodeURIComponent(fork)}`; +const BLOCKS_STREAM = (fork) => `${joinUrl(API_PREFIX, 'blocks/stream')}?fork=${encodeURIComponent(fork)}`; const BLOCKS_LIST = (page, pageSize, fork) => `${joinUrl(API_PREFIX, 'blocks/list')}?page=${encodeURIComponent(page)}&page-size=${encodeURIComponent(pageSize)}&fork=${encodeURIComponent(fork)}`; diff --git a/static/pages/Home.js b/static/pages/Home.js index 26b34eb..3d22f7c 100644 --- a/static/pages/Home.js +++ b/static/pages/Home.js @@ -40,7 +40,9 @@ export default function HomeView() { live ? 'LIVE \u2022' : 'LIVE', ), ), - h('section', { class: 'two-columns twocol' }, + h( + 'section', + { class: 'two-columns twocol' }, h(BlocksTable, { live, onDisableLive: () => setLive(false) }), h(TransactionsTable, { live, onDisableLive: () => setLive(false) }), ), diff --git a/static/pages/TransactionDetail.js b/static/pages/TransactionDetail.js index e80e33f..bcc2b0d 100644 --- a/static/pages/TransactionDetail.js +++ b/static/pages/TransactionDetail.js @@ -319,10 +319,7 @@ function tryDecodeUtf8Hex(hex) { } /** Human-friendly label for a content field key */ -const fieldLabel = (key) => - key - .replace(/_/g, ' ') - .replace(/\b\w/g, (c) => c.toUpperCase()); +const fieldLabel = (key) => key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); /** Render the value of a single content field */ function FieldValue({ value }) { @@ -390,10 +387,16 @@ function OperationProof({ proof }) { entries.length > 0 && h( 'div', - { style: 'margin-top:4px; display:grid; grid-template-columns:auto 1fr; gap:4px 12px; align-items:baseline;' }, + { + style: 'margin-top:4px; display:grid; grid-template-columns:auto 1fr; gap:4px 12px; align-items:baseline;', + }, ...entries.flatMap(([key, value]) => [ h('span', { style: 'color:var(--muted); font-size:12px; white-space:nowrap;' }, fieldLabel(key)), - h('span', { class: 'mono', style: 'font-size:12px; overflow-wrap:anywhere; word-break:break-all;' }, renderBytes(value)), + h( + 'span', + { class: 'mono', style: 'font-size:12px; overflow-wrap:anywhere; word-break:break-all;' }, + renderBytes(value), + ), ]), ), ); @@ -562,6 +565,13 @@ export default function TransactionDetail({ parameters }) { !tx && !err && h('p', null, 'Loading…'), // Success - tx && h(Fragment, null, h(Summary, { tx }), h(Operations, { operations: tx.operations }), h(Ledger, { ledger: tx.ledger })), + tx && + h( + Fragment, + null, + h(Summary, { tx }), + h(Operations, { operations: tx.operations }), + h(Ledger, { ledger: tx.ledger }), + ), ); } diff --git a/static/styles.css b/static/styles.css index 9bbba3c..648a4f7 100644 --- a/static/styles.css +++ b/static/styles.css @@ -161,6 +161,16 @@ button.pill:disabled { } @keyframes live-pulse { - 0%, 100% { box-shadow: 0 0 4px #ff4444, 0 0 8px #ff4444; } - 50% { box-shadow: 0 0 8px #ff4444, 0 0 16px #ff4444, 0 0 24px #ff6666; } + 0%, + 100% { + box-shadow: + 0 0 4px #ff4444, + 0 0 8px #ff4444; + } + 50% { + box-shadow: + 0 0 8px #ff4444, + 0 0 16px #ff4444, + 0 0 24px #ff6666; + } } diff --git a/tests/test_block_forks.py b/tests/test_block_forks.py index 65bdd10..3cfc6e8 100644 --- a/tests/test_block_forks.py +++ b/tests/test_block_forks.py @@ -259,6 +259,7 @@ def test_batch_with_fork_and_chain(client, repo): def test_fork_choice_empty_db(client, repo): """Fork choice returns Empty when no blocks exist.""" from rusty_results import Empty + result = asyncio.run(repo.get_fork_choice()) assert isinstance(result, Empty)