+ Proof of Work
+ Block #{share.height.toLocaleString()} · {formatDifficulty(share.sdiff)} share
+
+
+
+
+
+ {#each CHAPTERS as title, i}
+
+
+ {title}
+
+ {/each}
+
+
+
+ {#if step.kind === "chapter"}
+
+
{step.ch + 1} / {CHAPTERS.length}
+
{CHAPTERS[step.ch]}
+
+ {:else if step.kind === "merkle-init"}
+
+
The coinbase transaction this pool built, hashed twice, gives its txid:
+
{step.txid}
+
internal byte order — the form used while hashing
+
+ {:else if step.kind === "merkle-fold"}
+
+
+ Level {step.i} of {step.total} — join with the branch hash and hash the pair
+
+
+
current{step.left}
+
branch {step.i}{step.right}
+
SHA-256d ↓
+
result{step.result}
+
+
{@html MATH_MERKLE}
+
+ each branch hash b stands in for every other transaction in its subtree
+
+
+ {:else if step.kind === "merkle-done"}
+
+
Merkle root reproduced from the coinbase alone:
+
{step.root}
+
+ {step.matches
+ ? "matches the merkle root committed in the header"
+ : "does NOT match the header's merkle root"}
+
+
+ {:else if step.kind === "padding"}
+
+
+ 80 header bytes, a 0x80 marker, 39 zero bytes and the
+ 640-bit length — exactly two 512-bit blocks
+
+ {#each padBlocks as blk, bi}
+
+
message block {bi + 1} · 512 bits
+
+ {#each blk as s, i (i)}{s.hex}{/each}
+
+
+ {/each}
+
+ {#each fields as f (f.key)}
+ {f.name}
+ {/each}
+ padding
+ message length
+
+
+ the merkle root straddles the boundary — SHA-256 takes
+ 64 bytes at a time, indifferent to where fields begin and end
+
+
+ {:else if step.kind === "init-h"}
+
+
Eight 32-bit registers, seeded from the SHA-256 constants:
+
+ {#each INIT_H as v, i (i)}
+
H{i}{v}
+ {/each}
+
+
+ the fractional parts of the square roots of the first eight primes — "nothing up
+ my sleeve" numbers, chosen so nobody could have picked them to hide a weakness
+
+
+ {:else if step.kind === "schedule"}
+
+
+ Block {step.block} of {step.blocks} expands into sixteen 32-bit words
+
+
+ {#each step.words as w, i (i)}
+
w{i}{w}
+ {/each}
+
+
{@html MATH_SCHEDULE}
+
+ rounds 17–64 consume words expanded from the first sixteen by this recurrence
+
+ {#each "abcdefgh".split("") as n, i (i)}{n}{/each}
+
+
+
+ bit is 1
+ bit is 0
+ flipped on this round
+ rows are registers a–h, 32 bits each
+
+
+ {#each step.hexRegs as r, i (i)}
+
+ {"abcdefgh"[i]}{r}
+
+ {/each}
+
+
+
+
{@html MATH_ROUND}
+
{@html MATH_UPDATE}
+
+
+
{@html MATH_DEFS}
+
+
+
+ every lit cell above is a real bit of this share's working state
+
+
+ {:else if step.kind === "add-h"}
+
+
+ 64 rounds done — the result is added into the running state, chaining
+ this block into the next
+
+
+ {#each step.state as v, i (i)}
+
H{i}{v}
+ {/each}
+
+
+ addition, not replacement — the Davies–Meyer construction, which is what makes
+ the compression one-way
+
+
+ {:else if step.kind === "digest"}
+
+
{step.label}
+
{step.hex}
+
+ {:else if step.kind === "reverse"}
+
+
Bitcoin prints hashes in reverse byte order
+
{digest2Hex}
+
↓ reverse all 32 bytes
+
+ {#each finalChars as ch}{ch.c}{/each}
+
+
+ leading zeros — the visible sign of the work done
+
+
+ {:else if step.kind === "target"}
+
+
+
+ your hash
+ {#each finalChars as ch}{ch.c}{/each}
+
+
+ share target{shareTarget}
+
+
+ network target{netTarget}
+
+
+
+ leading zeros of your hash
+ a hash counts when it is numerically below the target
+
+
{@html MATH_TARGET}
+
+ the hash is below the share target — that is what made it a
+ {formatDifficulty(share.sdiff)} share. The network target sits another
+ {targetGapX >= 1000 ? formatDifficulty(targetGapX) : targetGapX.toFixed(1)}×
+ lower: clear that, and this exact header would have been block
+ #{share.height.toLocaleString()}.
+
+
+ {:else}
+
+
+ {allOk ? "Proof of work verified" : "Verification failed"}
+
+
+ {#each finalChars as ch}{ch.c}{/each}
+
+
+ {#each checks as c, i (c.name)}
+
+ {c.ok ? "✓" : "✗"}
+ {c.name}{c.detail}
+
+ {/each}
+
+
+
+ {/if}
+
+
+
+
+
+
diff --git a/ui/src/lib/SharePowPage.svelte b/ui/src/lib/SharePowPage.svelte
index c65c632..4e4001e 100644
--- a/ui/src/lib/SharePowPage.svelte
+++ b/ui/src/lib/SharePowPage.svelte
@@ -1,8 +1,10 @@
@@ -698,6 +295,20 @@
share hash. No trust in this dashboard required.
+
+ This is what turns a claim into proof.
+ A screenshot of a big difficulty number proves nothing — anyone can edit one, and
+ no one can tell from a picture whether the work behind it was ever done. These 80 bytes
+ can't be faked that way. They are the exact preimage of the share hash, so anyone can
+ hash them and see for themselves both that the hash is genuine and that finding it took
+ the work claimed. The header also commits to the
+ previous block, fixing when the work happened, and
+ through the merkle root to the coinbase
+ transaction, fixing who it pays — so the same proof can't be lifted from another
+ miner's share or replayed from a different block. Publish this, and the claim stands on
+ its own.
+
+
{#each fields as f (f.key)}{f.raw}{/each}
@@ -712,190 +323,17 @@
type="button"
class="verify-btn"
class:ok={allOk}
- disabled={animating}
- onclick={startVerify}
+ onclick={() => (showOverlay = true)}
>
- {animating ? "Verifying…" : verified ? (allOk ? "Verified ✓" : "Verification failed") : "Verify in this browser"}
+ {verified ? (allOk ? "Verified \u2713 — watch again" : "Verification failed — replay") : "Verify in this browser"}
- replays the full verification — merkle proof, padding, every SHA-256 stage
- — from the real computation, locally
+ takes over the screen and replays the whole verification — merkle proof,
+ padding, every SHA-256 round — from the real computation, locally
- T1 = h + Σ1(e) + Ch(e,f,g) + K[t] + W[t] · T2 = Σ0(a) + Maj(a,b,c)
- → a = T1+T2, e = d+T1, rest shift down
-
- {:else if step.kind === "add-h"}
-
- 64 rounds done — the result is added into the running state
- (Davies–Meyer), chaining block {step.block} forward:
-
-
- {#each step.state as v, i (i)}
-
- H{i}
- {v}
-
- {/each}
-
- {:else if step.kind === "digest"}
-
{step.label}:
-
{step.hex}
- {:else if step.kind === "reverse"}
-
{digest2Hex}
-
↓ reverse the 32 bytes — Bitcoin displays hashes little-endian
-
- {#each finalHashChars as ch}{ch.c}{/each}
-
- {:else if step.kind === "target"}
-
-
- your hash
-
- {#each finalHashChars as ch}{ch.c}{/each}
-
-
-
- share target
- {shareTargetHex}
-
-
- network target
- {netTargetHex}
-
-
-
- hash < share target — that's what made it a
- {formatDifficulty(pow.sdiff)} share. The network target is another
- {targetGapX >= 1000 ? formatDifficulty(targetGapX) : targetGapX.toFixed(1)}×
- lower: beat it, and this header would have been block #{pow.height.toLocaleString()}.
-
{finalHash === pow.hash
? "= the recorded share hash — proof of work is genuine"
- : "≠ the recorded share hash!"}
+ : "\u2260 the recorded share hash!"}