Merge 259e3bfea12ce77954b5349e1e64b5c37c8c1771 into db76ad97f8497d73038c8038e0647eaedb65351c

This commit is contained in:
Petar Radovic 2026-03-28 13:18:56 +00:00 committed by GitHub
commit e0a566df36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 83 additions and 3 deletions

View File

@ -1,4 +1,4 @@
from typing import List, Self
from typing import List, Optional, Self
from core.models import NbeSchema
from core.types import HexBytes
@ -16,6 +16,8 @@ class BlockRead(NbeSchema):
fork: int
block_root: HexBytes
proof_of_leadership: ProofOfLeadership
lib: Optional[HexBytes] = None
tip: Optional[HexBytes] = None
transactions: List[Transaction]
@classmethod
@ -29,5 +31,7 @@ class BlockRead(NbeSchema):
fork=block.fork,
block_root=block.block_root,
proof_of_leadership=block.proof_of_leadership,
lib=block.lib,
tip=block.tip,
transactions=block.transactions,
)

View File

@ -1,5 +1,5 @@
import logging
from typing import TYPE_CHECKING, List, Self
from typing import TYPE_CHECKING, List, Optional, Self
from sqlalchemy import Column
from sqlmodel import Field, Relationship
@ -30,6 +30,8 @@ class Block(TimestampedModel, table=True):
proof_of_leadership: ProofOfLeadership = Field(
sa_column=Column(PydanticJsonColumn(ProofOfLeadership), nullable=False)
)
lib: Optional[HexBytes] = Field(default=None, nullable=True)
tip: Optional[HexBytes] = Field(default=None, nullable=True)
# --- Relationships --- #

View File

@ -113,6 +113,8 @@ class HttpNodeApi(NodeApi):
try:
event = json.loads(line)
block = BlockSerializer.model_validate(event["block"])
block.lib = event.get("lib")
block.tip = event.get("tip")
except (ValidationError, KeyError, json.JSONDecodeError) as error:
logger.exception(error)
continue

View File

@ -1,7 +1,7 @@
import logging
from os import getenv
from random import randint
from typing import List, Self
from typing import List, Optional, Self
from rusty_results import Empty, Option
@ -28,6 +28,8 @@ logger = logging.getLogger(__name__)
class BlockSerializer(NbeSerializer, FromRandom):
header: HeaderSerializer
transactions: List[SignedTransactionSerializer]
lib: Optional[str] = None
tip: Optional[str] = None
@classmethod
def model_validate_json(cls, *args, **kwargs) -> Self:
@ -46,6 +48,8 @@ class BlockSerializer(NbeSerializer, FromRandom):
"slot": self.header.slot,
"block_root": self.header.block_root,
"proof_of_leadership": self.header.proof_of_leadership.into_proof_of_leadership(),
"lib": bytes.fromhex(self.lib) if self.lib else None,
"tip": bytes.fromhex(self.tip) if self.tip else None,
}
).with_transactions(transactions)

View File

@ -21,6 +21,8 @@ const normalize = (raw) => {
hash: raw.hash ?? header?.hash ?? '',
parent: raw.parent_block_hash ?? header?.parent_block ?? raw.parent_block ?? '',
root: raw.block_root ?? header?.block_root ?? '',
lib: raw.lib ?? '',
tip: raw.tip ?? '',
transactionCount: txLen,
};
};
@ -179,6 +181,10 @@ export default function BlocksTable({ live, onDisableLive }) {
h('td', null, h('span', { class: 'mono', title: b.root }, shortenHex(b.root))),
// Transactions
h('td', null, h('span', { class: 'mono' }, String(b.transactionCount))),
// LIB
h('td', null, h('span', { class: 'mono', title: b.lib }, b.lib ? shortenHex(b.lib) : '—')),
// Tip
h('td', null, h('span', { class: 'mono', title: b.tip }, b.tip ? shortenHex(b.tip) : '—')),
);
};
@ -192,6 +198,8 @@ export default function BlocksTable({ live, onDisableLive }) {
h('td', null, '\u00A0'),
h('td', null, '\u00A0'),
h('td', null, '\u00A0'),
h('td', null, '\u00A0'),
h('td', null, '\u00A0'),
);
};
@ -232,6 +240,8 @@ export default function BlocksTable({ live, onDisableLive }) {
h('col', { style: 'width:200px' }), // Parent
h('col', { style: 'width:200px' }), // Block Root
h('col', { style: 'width:100px' }), // Transactions
h('col', { style: 'width:160px' }), // LIB
h('col', { style: 'width:160px' }), // Tip
),
h(
'thead',
@ -245,6 +255,8 @@ export default function BlocksTable({ live, onDisableLive }) {
h('th', null, 'Parent'),
h('th', null, 'Block Root'),
h('th', null, 'Transactions'),
h('th', null, 'LIB'),
h('th', null, 'Tip'),
),
),
h('tbody', null, ...rows),

View File

@ -138,6 +138,8 @@ export default function BlockDetailPage({ parameters }) {
const blockRoot = block?.block_root ?? header?.block_root ?? '';
const currentBlockHash = block?.hash ?? header?.hash ?? '';
const parentHash = block?.parent_block_hash ?? header?.parent_block ?? '';
const lib = block?.lib ?? '';
const tip = block?.tip ?? '';
return h(
'main',
@ -255,6 +257,60 @@ export default function BlockDetailPage({ parameters }) {
),
h(CopyPill, { text: parentHash }),
),
// LIB
h('div', null, h('b', null, 'LIB:')),
h(
'div',
{ style: 'display:flex; gap:8px; flex-wrap:wrap; align-items:flex-start;' },
lib
? h(
'a',
{
class: 'pill mono linkish',
href: PAGE.BLOCK_DETAIL(lib),
title: String(lib),
style: 'max-width:100%; overflow-wrap:anywhere; word-break:break-word;',
},
shortenHex(lib),
)
: h(
'span',
{
class: 'pill mono',
style: 'max-width:100%; overflow-wrap:anywhere; word-break:break-word;',
},
'—',
),
lib && h(CopyPill, { text: lib }),
),
// Tip
h('div', null, h('b', null, 'Tip:')),
h(
'div',
{ style: 'display:flex; gap:8px; flex-wrap:wrap; align-items:flex-start;' },
tip
? h(
'a',
{
class: 'pill mono linkish',
href: PAGE.BLOCK_DETAIL(tip),
title: String(tip),
style: 'max-width:100%; overflow-wrap:anywhere; word-break:break-word;',
},
shortenHex(tip),
)
: h(
'span',
{
class: 'pill mono',
style: 'max-width:100%; overflow-wrap:anywhere; word-break:break-word;',
},
'—',
),
tip && h(CopyPill, { text: tip }),
),
),
),