Lints and prettify.

This commit is contained in:
Alejandro Cabeza Romero 2026-03-09 11:28:46 +00:00
parent 98c56fadb7
commit 9b56a4c8d8
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
15 changed files with 92 additions and 68 deletions

View File

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

View File

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

View File

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

View File

@ -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 (049).
"""
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:

View File

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

View File

@ -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(),
}
)

View File

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

View File

@ -9,11 +9,11 @@ import TransactionDetailPage from './pages/TransactionDetail.js';
const ROOT = document.getElementById('app');
// Detect the Base Path from the HTML <base> tag.
// If the tag is missing or equals "__BASE_PATH__", default to root "/".
// Detect the Base Path from the HTML <base> 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}/`;
})();

View File

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

View File

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

View File

@ -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)}`;

View File

@ -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) }),
),

View File

@ -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 }),
),
);
}

View File

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

View File

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