feat(attribution): carry a miner's name across chains by reward preimage
Live telemetry names the miners who win often and leaves everyone else as a hex string. Measured on mainnet: of 22 miners, 2 were named and 14 had no name available anywhere — but 6 already had one on Planck, under the same reward preimage. Those 6 are exactly the small operators the naming is worst for, because a name needs a block whose first reporter the feed could separate and they win few blocks. The preimage is derived from a secret only its owner holds, so the same preimage on two chains is the same operator. That is an inference the site can stand behind, unlike guessing from timing — and it is the only lever that works without waiting on the feed at all. It is a separate `AttributionSource` and renders as one. `Carried` names carry no confidence and no votes, because they assert nothing about this chain: only that this is who the operator was last known to be. Any live attribution here outranks one, and the row shows an `elsewhere` chip rather than the telemetry `node` chip so the two can never be read as the same claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jp6a8EDar9ueEhAxzep4V5
This commit is contained in:
+2
-1
@@ -247,7 +247,8 @@ export default function App() {
|
||||
Node names are a best-effort mapping, inferred from which node reported a block to
|
||||
telemetry first — never a claim the miner made. A <code>node?</code> tag marks one resting
|
||||
on a single block or a narrow lead; later blocks correct it. The reward preimage beside it
|
||||
is the only identity the chain itself asserts.
|
||||
is the only identity the chain itself asserts. An <code>elsewhere</code> tag means the
|
||||
name was earned by the same reward preimage on another chain, not observed on this one.
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
* inference from who reported a block first, not a claim the miner made, and
|
||||
* the UI must not present the two identically.
|
||||
*/
|
||||
export type AttributionSource = "preimage" | "telemetry";
|
||||
export type AttributionSource = "preimage" | "telemetry" | "carried";
|
||||
|
||||
@@ -29,6 +29,11 @@ function Row({
|
||||
onSelect: (miner: string) => void
|
||||
}) {
|
||||
const named = row.attribution === 'telemetry'
|
||||
// A name this operator earned on another chain, under the same reward
|
||||
// preimage. Shown because a preimage tells a miner nothing about themselves,
|
||||
// but never dressed as a live observation: nothing here says the node is on
|
||||
// this chain, only that this is who the operator was last known to be.
|
||||
const carried = row.attribution === 'carried'
|
||||
// Thin evidence: a single supporting block, or a lead the rest of the votes
|
||||
// do not back. Either way the mapping is a guess that a later block may undo.
|
||||
const tentative = named && (row.attribution_votes < 3 || row.confidence < 0.6)
|
||||
@@ -47,16 +52,27 @@ function Row({
|
||||
</button>
|
||||
<a
|
||||
href={`#/miner/${row.miner}`}
|
||||
className={named ? 'miner-name' : 'miner-name anonymous'}
|
||||
className={named || carried ? 'miner-name' : 'miner-name anonymous'}
|
||||
title={row.miner}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
onSelect(row.miner)
|
||||
}}
|
||||
>
|
||||
{named ? row.display : shortMiner(row.miner)}
|
||||
{named || carried ? row.display : shortMiner(row.miner)}
|
||||
</a>
|
||||
{mine && <span className="chip chip-you">You</span>}
|
||||
{carried && (
|
||||
<span
|
||||
className="chip chip-named chip-guess"
|
||||
title={
|
||||
'Name carried from another chain, matched on the same reward preimage — ' +
|
||||
'this operator has not yet been resolved by telemetry here.'
|
||||
}
|
||||
>
|
||||
elsewhere
|
||||
</span>
|
||||
)}
|
||||
{named && (
|
||||
<span
|
||||
// An attributed name is an inference from who reported the block
|
||||
|
||||
Reference in New Issue
Block a user