Blocks are now stamped with the Bitcoin network name ("main", "test",
"signet") at ingest time. The reconcile loop's Pass 2 skips any stored
block whose chain differs from the node's current chain, preventing
testnet blocks from being falsely orphaned after switching back to
mainnet. Legacy rows with an empty chain field fall through unchanged.
The UI shows a blue "test" / "signet" badge next to blocks from a
non-current network so operators can distinguish cross-chain history
from genuine reorg-orphaned blocks.
148 lines
4.0 KiB
Svelte
148 lines
4.0 KiB
Svelte
<script lang="ts">
|
|
import { snap } from "../stores/snapshot.svelte";
|
|
import { formatAgo, formatDifficulty, explorerBaseFor } from "../format";
|
|
|
|
const blocks = $derived.by(() => {
|
|
const b = snap.data?.recent_blocks ?? [];
|
|
return [...b].reverse();
|
|
});
|
|
|
|
const explorerBase = $derived(
|
|
explorerBaseFor(snap.data?.chain?.chain, snap.data?.mempool_base_url),
|
|
);
|
|
|
|
const currentChain = $derived(snap.data?.chain?.chain ?? "");
|
|
</script>
|
|
|
|
<section class="card">
|
|
<h2>Blocks found</h2>
|
|
{#if blocks.length === 0}
|
|
<div class="empty">No blocks found yet.</div>
|
|
{:else}
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Height</th>
|
|
<th>Hash</th>
|
|
<th class="num">Reward</th>
|
|
<th class="num">Winning share</th>
|
|
<th class="num">When</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each blocks as b (b.height + "-" + b.found_at)}
|
|
<tr class:orphaned={!!b.orphaned_at}>
|
|
<td class="mono">
|
|
{b.height}
|
|
{#if b.orphaned_at}
|
|
<span class="orphan-tag" title="Reorged out of the canonical chain at {b.orphaned_at}">orphaned</span>
|
|
{/if}
|
|
{#if b.chain && currentChain && b.chain !== currentChain}
|
|
<span class="chain-tag" title="Mined on {b.chain} — current node is {currentChain}">{b.chain}</span>
|
|
{/if}
|
|
</td>
|
|
<td class="hash-cell">
|
|
{#if b.hash}
|
|
<a
|
|
class="hash mono"
|
|
href="{explorerBase}/block/{b.hash}"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title="Open block on mempool.space"
|
|
>{b.hash}</a>
|
|
{:else}
|
|
<span class="hash mono">—</span>
|
|
{/if}
|
|
</td>
|
|
<td class="num">
|
|
{#if b.reward_btc}
|
|
<span class="reward-num">{b.reward_btc.toFixed(4)}</span>
|
|
<span class="unit">BTC</span>
|
|
{:else}
|
|
<span class="reward-num">—</span>
|
|
{/if}
|
|
</td>
|
|
<td class="num">{b.share_diff ? formatDifficulty(b.share_diff) : "—"}</td>
|
|
<td class="num">{formatAgo(new Date(b.found_at).getTime() / 1000)}</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
|
|
<style>
|
|
h2 {
|
|
margin: 0 0 1rem;
|
|
font-size: 1.05rem;
|
|
font-weight: 600;
|
|
}
|
|
.empty {
|
|
color: var(--fg-dim);
|
|
padding: 0.5rem 0;
|
|
}
|
|
.table-wrap {
|
|
overflow-x: auto;
|
|
}
|
|
.hash-cell {
|
|
/* Let the 64-char hash wrap inside the cell instead of stretching
|
|
* the whole table. */
|
|
max-width: 620px;
|
|
word-break: break-all;
|
|
}
|
|
.hash {
|
|
color: var(--fg-dim);
|
|
font-size: 0.85em;
|
|
line-height: 1.4;
|
|
}
|
|
a.hash {
|
|
color: var(--fg-dim);
|
|
text-decoration: none;
|
|
border-bottom: 1px dashed transparent;
|
|
}
|
|
a.hash:hover {
|
|
color: var(--accent);
|
|
border-bottom-color: var(--accent);
|
|
}
|
|
.reward-num {
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
tr.orphaned td {
|
|
color: var(--fg-dim);
|
|
text-decoration: line-through;
|
|
text-decoration-color: rgba(220, 80, 80, 0.55);
|
|
}
|
|
tr.orphaned .orphan-tag {
|
|
display: inline-block;
|
|
margin-left: 0.4em;
|
|
padding: 0.05em 0.4em;
|
|
border-radius: 3px;
|
|
background: rgba(220, 80, 80, 0.15);
|
|
color: rgb(220, 110, 110);
|
|
font-size: 0.7em;
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
vertical-align: middle;
|
|
}
|
|
.chain-tag {
|
|
display: inline-block;
|
|
margin-left: 0.4em;
|
|
padding: 0.05em 0.4em;
|
|
border-radius: 3px;
|
|
background: rgba(80, 160, 220, 0.15);
|
|
color: rgb(100, 170, 220);
|
|
font-size: 0.7em;
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
vertical-align: middle;
|
|
}
|
|
.unit {
|
|
color: var(--fg-dim);
|
|
font-size: 0.75em;
|
|
font-weight: 400;
|
|
margin-left: 0.25em;
|
|
}
|
|
</style>
|