Cargo workspace plus a Vite frontend, following ~/git/architecture/generic.md. Every block header carries its author's wormhole reward preimage in a `pow_` PreRuntime digest, so authorship for the whole network is derivable from headers alone — no indexer, no registration, no way for a miner to be left out. That decoding, the hashrate maths and the telemetry name attribution live in blackbeard-core with no I/O at all, so the parts that are easy to get subtly wrong are exercised by unit tests rather than only against a live chain. The browser holds one WebSocket: snapshot on subscribe, deltas thereafter. The head stream is itself a push (chain_subscribeNewHeads), so a block reaches the page the moment the node imports it. Messages are serialised once per broadcast, and leaderboards are recomputed only for windows a socket is actually watching. No RxJS — useSyncExternalStore is React's own contract for this. Verified against the live Planck testnet: 12/12 headers decoded, telemetry names attributed (quanpool-planck, baba-gorchitsa, …), warm start restoring 84 blocks and 5 held names across a restart. Three findings worth recording, all in CLAUDE.md: - substrate-telemetry sends its JSON in *binary* frames. A text-only client connects, subscribes, reports healthy and receives nothing at all — and a Python probe hides it, because json.loads accepts bytes. - Difficulty is a little-endian U512; decoding it big-endian gives a number wrong by ~10^150 that still renders fine. - Planck's real block interval is ~13-15s against a 6s target with enormous variance, so a measured interval needs 20 tip samples before it is publishable. Deploy assets, the Gitea Actions workflow and script/infra-setup.sh are included; port 25864 is registered in architecture/port-allocations.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSDYiibCtELsrjQq6KXnoi
35 lines
977 B
TypeScript
35 lines
977 B
TypeScript
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
import type { MinerId } from "./MinerId";
|
|
|
|
/**
|
|
* A block trimmed for the live ticker: what scrolls past on the front page.
|
|
*
|
|
* Separate from [`BlockObservation`] because the ticker is pushed on every
|
|
* block to every connected browser, and shipping the full record — including
|
|
* the difficulty string, which changes rarely — would multiply the socket's
|
|
* bandwidth for nothing.
|
|
*/
|
|
export type RecentBlock = {
|
|
/**
|
|
* Block number.
|
|
*/
|
|
height: number,
|
|
/**
|
|
* Author's reward preimage.
|
|
*/
|
|
miner: MinerId,
|
|
/**
|
|
* Display name for the author at the time the block was pushed.
|
|
*/
|
|
display: string,
|
|
/**
|
|
* When this observer first saw it.
|
|
*/
|
|
observed_at: string,
|
|
/**
|
|
* Seconds since the previous block, by observation. `None` for the first
|
|
* block after a restart, where there is no previous observation to
|
|
* subtract.
|
|
*/
|
|
gap_seconds: number | null, };
|