What a stranger sees: paste a chain block hash on /proof, the page downloads the captured proof bytes (1,272,897 bytes), hashes them in the tab against the proof_hash the signed record carries, parses the 328-byte public values out of the SP1 container and checks keccak against the record's statement and the decoded fields against the block (site/lib/proof.mjs, no library). The STARK is verified by this site's node (the observer runs igneum-prove-host --mode verify with the pinned key on each capture: 29 ms verify, 197 ms key setup on the fixture proof); the page says so and labels the in-browser STARK verifier as coming. docs/plans/explorer.md section 8 carries the size and time numbers and the two routes (Groth16 wrap plus sp1-verifier in wasm, or the compressed verifier ported to wasm32). Observer: a sample of pool proofs captured through igneum_getProofBytes while the node holds them (PROOF_CAPTURE_EVERY_MS, PROOF_BYTES_KEEP), checked and verified, written to live_proof_bytes; every live_proofs row carries the record (key_hash, payout, statement, proof_hash); getBlockTemplate.powEpoch read every 10 s into live_state.pow_epoch. RPC load: wrpc 230 to 248 per minute against 222 to 224 before, evm unchanged. P17: the node release 0.3.13 (bb43e9a8) does not carry the state field (it is on ledger-fixes-0311 fbb0082a), so the explorer cuts the one word from the observer's tables by the design 2.4 rule and takes the node's word per transaction when the fork answers one. A block that left the selected chain reads included with a note, never reorged out. C46: /api/stats algorithm reads "class v3 / generator 3 (epoch 55; ...)" from the node's epoch line, v4 when the node reports 4, "unknown" before the observer has read it; new lottery field. Tests: site/lib/proof.test.mjs (the real tail of block 59199's proof reproduces the host's statement), site/api/verify.test.mjs, tools/observer/proof-capture.test.mjs (the native verifier refusing a pre-pin proof), site/api/public-stats.test.mjs. Dry run on the fixture proof of block 56 through the local preview: VERIFIED, 5.8 ms of checks and 139 ms of download in the browser, STARK 29 ms on the node. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
385 lines
39 KiB
HTML
385 lines
39 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<title>Verify an Igneum proof</title>
|
|
<meta name="description" content="Check a block's shard proof without Igneum software: the proof bytes against the signed record, the public values against the statement, the statement against the block, and the native SP1 verdict.">
|
|
<link rel="canonical" href="https://igneum.network/proof">
|
|
<meta name="robots" content="noindex">
|
|
<meta name="theme-color" content="#0C0C0E">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:site_name" content="Igneum">
|
|
<meta property="og:title" content="Verify an Igneum proof">
|
|
<meta property="og:description" content="Check a block's shard proof in your browser: the bytes against the signed record, the statement against the block, and the native SP1 verdict.">
|
|
<meta property="og:url" content="https://igneum.network/proof">
|
|
<meta property="og:image" content="https://igneum.network/og-small.png?v=3">
|
|
<meta property="og:image:width" content="256">
|
|
<meta property="og:image:height" content="256">
|
|
<meta property="og:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
|
<meta name="twitter:card" content="summary">
|
|
<meta name="twitter:title" content="Verify an Igneum proof">
|
|
<meta name="twitter:description" content="Check a block's shard proof in your browser: the bytes against the signed record, the statement against the block, and the native SP1 verdict.">
|
|
<meta name="twitter:image" content="https://igneum.network/og-small.png?v=3">
|
|
<meta name="twitter:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
|
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
|
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
|
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
|
<link rel="manifest" href="/site.webmanifest">
|
|
<!-- head:start -->
|
|
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
|
|
<style>
|
|
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
|
|
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
|
|
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-sans-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-sans-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-sans-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
|
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
|
|
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
|
|
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
|
|
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
|
|
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
|
|
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
|
|
html{-webkit-text-size-adjust:100%}
|
|
h1,h2,h3{text-wrap:balance}
|
|
p,li,dd,figcaption{text-wrap:pretty}
|
|
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
|
|
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
|
|
.skip:focus{top:12px}
|
|
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
|
|
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
|
|
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
|
|
.nav a{color:inherit;text-decoration:none}
|
|
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
|
|
.brand .mark{display:block;flex:0 0 auto}
|
|
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
|
|
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
|
|
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
|
|
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
|
|
.nav .brand:hover{color:var(--ui-ink)}
|
|
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
|
|
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
|
|
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
|
|
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
|
|
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
|
|
@media (max-width:940px){
|
|
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
|
|
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
|
|
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
|
|
.nav .links a.cta{margin-top:8px;min-height:48px}
|
|
.nav .links.open{display:flex}
|
|
.burger{display:inline-flex}
|
|
}
|
|
/* footer, the same on every page */
|
|
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
|
|
.foot .wrap{padding-block:48px 32px}
|
|
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
|
|
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
|
|
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
|
|
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
|
|
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
|
|
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
|
|
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
|
|
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
|
|
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
|
|
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
|
|
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
|
|
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
|
|
</style>
|
|
<!-- head:end -->
|
|
<style>
|
|
/* the explorer pages share this block with explorer.html, block.html and address.html; same tokens as live.html */
|
|
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;--blue:#5CB8FF;--green:#3FD39A;
|
|
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
|
|
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
|
|
--fs-h1:clamp(32px,5.5vw,56px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
|
|
*{box-sizing:border-box}
|
|
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
|
|
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
|
|
a:focus-visible,button:focus-visible,input:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
|
|
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
|
|
.mono{font-family:var(--f-mono)}
|
|
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
|
|
h1,h2{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
|
|
h1{font-weight:900;font-size:var(--fs-h1);letter-spacing:-.01em;overflow-wrap:anywhere}
|
|
h2{font-weight:700;font-size:var(--fs-h3)}
|
|
p{margin:0}
|
|
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0;margin-top:var(--gap)}
|
|
.head{padding-block:var(--head) var(--gap);display:flex;flex-direction:column;gap:14px}
|
|
.head p{color:var(--ink-2);max-width:64ch;font-size:16px}
|
|
.search{display:flex;gap:10px;max-width:760px;margin-top:4px}
|
|
.search input{flex:1;min-width:0;min-height:48px;padding:10px 14px;border-radius:10px;border:1px solid var(--line-2);background:var(--graphite);color:var(--bone);font:500 15px/1.3 var(--f-mono)}
|
|
.search input::placeholder{color:var(--ash)}
|
|
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;font-family:var(--f-sans)}
|
|
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
|
|
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
|
|
.btn.small{min-height:36px;padding:6px 14px;font-size:14px}
|
|
.msg{font-family:var(--f-mono);font-size:13px;color:var(--molten);min-height:18px}
|
|
.strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap-tile)}
|
|
@media (max-width:700px){.strip{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
.cell{background:var(--graphite);border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
.cell .k{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash)}
|
|
.cell .v{font-family:var(--f-display);font-weight:700;font-size:var(--fs-tile);line-height:1.1;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
.cell .v small{font-family:var(--f-mono);font-weight:500;font-size:13px;color:var(--ash);margin-left:4px;letter-spacing:.04em}
|
|
@media (max-width:700px){.cell .v{font-size:19px}}
|
|
.cell .s{font-family:var(--f-mono);font-size:12px;color:var(--ash);text-wrap:balance}
|
|
.cell.good .v{color:var(--green)}.cell.bad .v{color:var(--ember)}.cell.warm .v{color:var(--molten)}
|
|
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
|
|
.tbl{overflow-x:auto;margin-top:12px;-webkit-overflow-scrolling:touch}
|
|
table{border-collapse:collapse;width:100%;font-size:14px}
|
|
th,td{padding:10px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top}
|
|
th{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
|
|
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}
|
|
tr:last-child td{border-bottom:0}
|
|
tbody tr:hover td{background:rgba(255,255,255,.025)}
|
|
.id{font-family:var(--f-mono);color:var(--molten)}
|
|
.hash{font-family:var(--f-mono)}
|
|
.chip{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);margin-right:6px}
|
|
.chip.hot{border-color:var(--molten);color:var(--molten)}.chip.ok{border-color:var(--green);color:var(--green)}.chip.bad{border-color:var(--ember);color:var(--ember)}
|
|
.empty{color:var(--ash);font-size:14px;padding:12px 0}
|
|
.note{font-size:13px;color:var(--ash);margin-top:14px;max-width:80ch}
|
|
dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 20px;margin:12px 0 0;font-size:14px}
|
|
dt{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding-top:3px}
|
|
dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
|
|
@media (max-width:640px){dl{grid-template-columns:1fr;gap:2px 0}dd{margin-bottom:8px}}
|
|
.two{display:grid;gap:var(--gap);grid-template-columns:1fr}
|
|
@media (min-width:900px){.two{grid-template-columns:1fr 1fr}.two .card{margin-top:0}}
|
|
.two-wrap{margin-top:var(--gap)}
|
|
.checks{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:10px;font-size:14px}
|
|
.checks li{display:grid;grid-template-columns:22px 1fr;gap:10px;align-items:start}
|
|
.checks .mark{width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-family:var(--f-mono);font-size:12px;font-weight:700;margin-top:2px}
|
|
.checks .ok{background:rgba(63,211,154,.15);color:var(--green);border:1px solid var(--green)}
|
|
.checks .bad{background:rgba(242,84,27,.15);color:var(--ember);border:1px solid var(--ember)}
|
|
.checks .wait{background:rgba(255,179,92,.12);color:var(--molten);border:1px solid var(--molten)}
|
|
.checks .how{display:block;font-family:var(--f-mono);font-size:12px;color:var(--ash);overflow-wrap:anywhere}
|
|
.layers{display:grid;gap:12px;margin-top:12px}
|
|
.layer{border:1px solid var(--line);border-radius:12px;padding:14px 16px;font-size:14px}
|
|
.layer b{font-family:var(--f-mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--ash);display:block;margin-bottom:4px}
|
|
main{padding-bottom:var(--sec)}
|
|
.foot{margin-top:0}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- nav:start -->
|
|
<a class="skip" href="#main">Skip to content</a>
|
|
<nav class="nav" aria-label="Main">
|
|
<div class="wrap">
|
|
<a href="/" class="brand" aria-label="Igneum home">
|
|
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
|
|
<span class="word">IGNEUM</span>
|
|
</a>
|
|
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
|
|
<div class="links" id="nav-links">
|
|
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
|
|
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
|
|
<a href="/bench" data-nav="bench">Engineering log</a>
|
|
<a href="/miner" data-nav="miner">Miner</a>
|
|
<a href="/wallet" data-nav="wallet">Wallet</a>
|
|
<a href="/evidence" data-nav="evidence">Evidence</a>
|
|
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
|
|
<a href="/miner" class="cta">The miner</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<script>
|
|
(function(){
|
|
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
|
|
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
|
|
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
|
|
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
|
|
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
|
|
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
|
|
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
|
|
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
|
|
})();
|
|
</script>
|
|
<!-- nav:end -->
|
|
<main id="main" class="wrap">
|
|
<div class="head">
|
|
<div class="eyebrow"><a href="/explorer">Explorer</a> · verify a proof</div>
|
|
<h1>Verify a block's proof</h1>
|
|
<p>Paste a chain block hash or number. This page fetches the shard record the chain carries and the proof bytes the node handed out, hashes the bytes in your browser against the signed record, parses the public values out of the proof and checks them against the statement and the block. The SP1 proof itself is verified by this site's node; the in-browser verifier is coming.</p>
|
|
<form class="search" id="search" role="search">
|
|
<input id="q" type="search" autocomplete="off" spellcheck="false" placeholder="chain block hash or number" aria-label="Block to verify">
|
|
<button class="btn primary" type="submit">Verify</button>
|
|
</form>
|
|
<div class="msg" id="msg" aria-live="polite"></div>
|
|
</div>
|
|
<div id="latest" hidden>
|
|
<div class="card"><div class="viz-head"><h2>Newest captured proofs</h2><div class="eyebrow" id="latest-eyebrow"></div></div><div class="tbl" id="latest-rows"></div><p class="note">The observer captures one pool proof every few minutes while the node still holds its bytes (about ten minutes after the block). Click one to run the checks on it.</p></div>
|
|
</div>
|
|
<div id="body" hidden>
|
|
<div class="strip" aria-live="polite">
|
|
<div class="cell" id="c-verdict"><div class="k">Verdict</div><div class="v" id="verdict">checking</div><div class="s" id="verdict-s"></div></div>
|
|
<div class="cell"><div class="k">Time</div><div class="v" id="time">0<small>ms</small></div><div class="s" id="time-s">in this browser</div></div>
|
|
<div class="cell"><div class="k">Block state</div><div class="v" id="state">n/a</div><div class="s" id="state-s"></div></div>
|
|
<div class="cell"><div class="k">Pinned program</div><div class="v hash" id="program">n/a</div><div class="s" id="program-s"></div></div>
|
|
</div>
|
|
<div class="card"><div class="viz-head"><h2 class="hash" id="title">Block</h2><div class="eyebrow" id="shards-eyebrow"></div></div><div id="shards"></div></div>
|
|
<div class="two two-wrap">
|
|
<div class="card"><div class="viz-head"><h2>Checks in this browser</h2><div class="eyebrow" id="checks-eyebrow"></div></div><ul class="checks" id="checks"></ul><p class="note" id="checks-note"></p></div>
|
|
<div class="card"><div class="viz-head"><h2>The statement</h2><div class="eyebrow">public values, 328 bytes</div></div><dl id="statement"></dl></div>
|
|
</div>
|
|
<div class="card"><div class="viz-head"><h2>Who verified what</h2><div class="eyebrow">three layers</div></div><div class="layers" id="layers"></div></div>
|
|
</div>
|
|
</main>
|
|
<!-- footer:start -->
|
|
<footer class="foot">
|
|
<div class="wrap">
|
|
<div class="foot-grid">
|
|
<div class="foot-brand">
|
|
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
|
|
<p>Mined by GPUs. Proven by fire.</p>
|
|
</div>
|
|
<nav class="foot-col" aria-label="Read">
|
|
<div class="eyebrow">Read</div>
|
|
<a href="/litepaper">Litepaper</a>
|
|
<a href="/litepaper#limits">What Igneum does not claim</a>
|
|
<a href="/litepaper#randomx">Igneum vs RandomX</a>
|
|
<a href="/litepaper#shoulders">Built on the shoulders</a>
|
|
<a href="/bench">Engineering log</a>
|
|
<a href="/evidence">Evidence</a>
|
|
</nav>
|
|
<nav class="foot-col" aria-label="Run">
|
|
<div class="eyebrow">Run</div>
|
|
<a href="/miner">The miner</a>
|
|
<a href="/wallet">The wallet</a>
|
|
<a href="/miners">GPU bench table</a>
|
|
<a href="/miner#fee">The dev fee</a>
|
|
<a href="/faucet">Testnet faucet</a>
|
|
</nav>
|
|
<nav class="foot-col" aria-label="Follow">
|
|
<div class="eyebrow">Follow</div>
|
|
<a href="/live">Live devnet</a>
|
|
<a href="/explorer">Explorer</a>
|
|
<a href="/#journey">Journey</a>
|
|
<a href="/wallet">Add Igneum to MetaMask</a>
|
|
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
|
|
<a href="/miner#get">Miner downloads: public testnet</a>
|
|
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
|
|
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
|
|
</nav>
|
|
</div>
|
|
<div class="foot-base">
|
|
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
|
|
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
|
|
<span class="mono">igneum.network</span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
<!-- footer:end -->
|
|
<script type="module">
|
|
import { ign, ignFromWei, shortHash, rel, int, utc } from '/lib/explorer.mjs';
|
|
import { checkProof, fromHex, toHex } from '/lib/proof.mjs';
|
|
const $ = id => document.getElementById(id);
|
|
const esc = s => String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
const link = h => `<a class="hash" href="/block/${esc(h)}" title="${esc(h)}">${esc(shortHash(h, 12, 8))}</a>`;
|
|
const dl = pairs => pairs.filter(p => p).map(([k, v]) => `<dt>${esc(k)}</dt><dd>${v}</dd>`).join('');
|
|
const ms = x => x === null || x === undefined ? 'n/a' : (x < 10 ? x.toFixed(1) : Math.round(x).toLocaleString('en-GB')) + ' ms';
|
|
const chip = (s, cls = '') => `<span class="chip ${cls}">${esc(s)}</span>`;
|
|
const stateCls = s => s === 'finalised' ? 'ok' : s === 'proven' ? 'ok' : s === 'executed' ? 'hot' : '';
|
|
const id = decodeURIComponent(location.pathname.split('/').pop() || '');
|
|
const wanted = id && id !== 'proof' ? id : '';
|
|
$('search').addEventListener('submit', e => { e.preventDefault(); const q = $('q').value.trim().replace(/^0x/i, ''); if (!q) return; location.href = '/proof/' + encodeURIComponent(q); });
|
|
|
|
async function loadLatest() {
|
|
const r = await fetch('/api/verify?latest=1'); const j = await r.json();
|
|
$('latest').hidden = false;
|
|
if (!j.ok) { $('latest-rows').innerHTML = `<div class="empty">${esc(j.error || 'The API did not answer.')}</div>`; return; }
|
|
$('latest-eyebrow').textContent = `${j.captured.total} captured in the last day, ${j.captured.with_bytes} with bytes${j.network ? ', ' + j.network : ''}`;
|
|
$('latest-rows').innerHTML = j.latest.length ? `<table><thead><tr><th>Block</th><th class="n">Number</th><th class="n">Shard</th><th>Prover</th><th class="n">Bytes</th><th>Checks</th><th>Native verifier</th><th>Captured</th></tr></thead><tbody>${j.latest.map(c => `<tr><td><a class="hash" href="${esc(c.href)}">${esc(shortHash(c.block, 10, 6))}</a></td><td class="n">${int(c.number)}</td><td class="n">${c.shard}</td><td class="id">${esc(c.prover || '')}</td><td class="n">${int(c.bytes)}${c.has_bytes ? '' : ' <span style="color:var(--ash)">(dropped)</span>'}</td><td>${c.checks_ok ? chip('3 of 3', 'ok') : chip('failed', 'bad')}</td><td>${c.native_verified === true ? chip('verified', 'ok') : c.native_verified === false ? chip('not verified', 'bad') : chip('not run')}</td><td>${rel(Date.now() - new Date(c.captured_at).getTime())}</td></tr>`).join('')}</tbody></table>` : '<div class="empty">No proof captured yet. The observer captures one while the node holds it; the provers may be off.</div>';
|
|
$('msg').textContent = '';
|
|
}
|
|
|
|
function layers(j, s, browser) {
|
|
const v = j.verifier;
|
|
const native = s && s.proof ? s.proof.native : null;
|
|
const nativeLine = !s ? 'no shard chosen' : !s.proof ? 'this shard\'s proof was not captured, so the native verifier did not see it' : native.verified === true ? `VERIFIED in ${ms(native.verify_ms)} (key setup ${ms(native.setup_ms)}, process ${ms(native.total_ms)}) with the pinned key${native.ours === true ? '; the proof names the pinned program id' : ''}` : native.verified === false ? `NOT VERIFIED: ${esc(native.note || 'the verifier rejected the proof')}${native.program_id ? ` (the proof names program id ${esc(shortHash(native.program_id, 10, 6))}, pinned ${esc(shortHash(native.pinned_id || v.pinned.shard.program_id, 10, 6))})` : ''}` : `not run: ${esc(native.note || 'no verifier configured on the observer')}`;
|
|
const nodeLine = v.node === null ? esc(v.node_note) : v.node === 'Off' ? `the observer's node runs with its own SP1 verifier off; it accepts a shard as proven when a later chain block carried and paid the record (${s && s.carried_by ? 'this one was carried by ' + link(s.carried_by) + (s.carrier_number !== null ? ', chain block ' + int(s.carrier_number) : '') : 'not carried yet'})` : `the observer's node verifies pool proofs (${esc(v.node)})${s && s.node_verified !== null ? ': this one ' + (s.node_verified ? 'verified' : 'NOT verified') : ''}`;
|
|
$('layers').innerHTML = [
|
|
['In this browser', browser ? `${browser.ok ? '3 of 3 checks passed' : `${browser.checks.filter(c => c.ok).length} of ${browser.checks.length} checks passed`} in ${ms(browser.ms)} (${int(browser.bytes)} bytes fetched in ${ms(browser.fetch_ms)}): SHA-256 of the bytes against the record's proof hash, keccak-256 of the public values against its statement, the statement's fields against this block. No Igneum software, no server trust: the record is what miners signed and the chain carried.` : 'no captured bytes to check; the record the chain carries is shown above'],
|
|
['By this site\'s node', `${nativeLine}. The STARK itself (SP1 ${esc(v.pinned.sp1_crate_version)}, circuit ${esc(v.pinned.sp1_circuit_version)}, compressed proof of ${s && s.proof ? int(s.proof.bytes) + ' bytes' : 'about 1.27 MB'}) is verified by this site's node, not in your browser: the in-browser verifier is coming (what it needs is in docs/plans/explorer.md).`],
|
|
['By the network', `${nodeLine}. The pinned program id every node checks proofs against: <span class="hash">${esc(v.pinned.shard.program_id)}</span>, verifying key sha256 <span class="hash">${esc(v.pinned.shard.vk_sha256)}</span>, pinned ${esc(v.pinned.pinned_at)}.`],
|
|
].map(([t, body]) => `<div class="layer"><b>${esc(t)}</b>${body}</div>`).join('');
|
|
}
|
|
|
|
async function load() {
|
|
if (!wanted) { $('msg').textContent = 'Loading the newest captured proofs.'; await loadLatest(); return; }
|
|
const query = /^\d{1,12}$/.test(wanted) ? 'height=' + wanted : 'block=' + encodeURIComponent(wanted.replace(/^0x/i, ''));
|
|
$('msg').textContent = 'Fetching the record.';
|
|
const r = await fetch('/api/verify?' + query); const j = await r.json();
|
|
if (!j.ok) { $('msg').textContent = j.error || 'Not found.'; await loadLatest().catch(() => {}); return; }
|
|
const b = j.block;
|
|
document.title = `Verify block ${shortHash(b.hash, 8, 6)}`;
|
|
$('title').innerHTML = `<a class="hash" href="/block/${esc(b.hash)}">${esc(b.hash)}</a>`;
|
|
$('body').hidden = false;
|
|
$('state').innerHTML = chip(b.state, stateCls(b.state)); $('state').style.fontSize = '18px';
|
|
$('state-s').textContent = (b.failure ? `failure: ${b.failure}; ` : '') + (b.note || (b.number !== null ? `chain block ${int(b.number)}` : 'not a chain block'));
|
|
$('program').textContent = shortHash(j.verifier.pinned.shard.program_id, 10, 6); $('program').title = j.verifier.pinned.shard.program_id;
|
|
$('program-s').textContent = `shard guest pinned ${j.verifier.pinned.pinned_at.slice(0, 10)}, SP1 ${j.verifier.pinned.sp1_crate_version}`;
|
|
const sh = j.shards;
|
|
$('shards-eyebrow').textContent = b.chain ? `${sh.length} shard${sh.length === 1 ? '' : 's'} planned` : 'not a chain block: no shards';
|
|
$('shards').innerHTML = sh.length ? `<div class="tbl"><table><thead><tr><th class="n">Shard</th><th>State</th><th>Prover</th><th>Statement</th><th>Proof hash</th><th>Carried by</th><th>Captured</th><th>Native verifier</th><th></th></tr></thead><tbody>${sh.map(s => `<tr><td class="n">${s.i} of ${s.n}</td><td>${chip(s.state, s.state === 'paid' ? 'ok' : s.state === 'verified' ? 'hot' : '')}</td><td class="id">${esc(s.prover || '')}</td><td class="hash" title="${esc(s.statement || '')}">${s.statement ? esc(shortHash(s.statement, 10, 6)) : '<span style="color:var(--ash)">not seen</span>'}</td><td class="hash" title="${esc(s.proof_hash || '')}">${s.proof_hash ? esc(shortHash(s.proof_hash, 10, 6)) : '<span style="color:var(--ash)">not seen</span>'}</td><td>${s.carried_by ? link(s.carried_by) : ''}</td><td>${s.proof ? `${int(s.proof.bytes)} bytes${s.proof.has_bytes ? '' : ' (dropped)'}` : '<span style="color:var(--ash)">no</span>'}</td><td>${s.proof ? (s.proof.native.verified === true ? chip('verified ' + ms(s.proof.native.verify_ms), 'ok') : s.proof.native.verified === false ? chip('not verified', 'bad') : chip('not run')) : ''}</td><td>${s.proof && s.proof.has_bytes ? `<a class="hash" href="${esc(s.proof.url)}" download="igneum-block-${int(b.number).replace(/,/g, '')}-shard-${s.i}.bin">download</a>` : ''}</td></tr>`).join('')}</tbody></table></div>` : `<div class="empty">${b.chain ? 'No shard plan recorded for this chain block.' : 'Proofs cover chain blocks; this block is merged by one. ' + (b.merged_by ? 'See ' + link(b.merged_by) + '.' : '')}</div>`;
|
|
// the shard to check: the first with captured bytes, else the first with a capture, else none
|
|
const s = sh.find(x => x.proof && x.proof.has_bytes) || sh.find(x => x.proof) || null;
|
|
if (!s) {
|
|
$('c-verdict').className = 'cell warm'; $('verdict').textContent = sh.some(x => x.state === 'paid' || x.state === 'verified') ? 'not captured' : 'not proven yet';
|
|
$('verdict-s').textContent = sh.some(x => x.state === 'paid' || x.state === 'verified') ? 'the chain carries the record; its bytes were not captured' : 'no shard of this block has a proof record yet';
|
|
$('time').innerHTML = '0<small>ms</small>'; $('checks').innerHTML = ''; $('checks-note').textContent = j.capture_note; $('statement').innerHTML = dl([['Block', link(b.hash)], ['Number', b.number === null ? 'n/a' : int(b.number)]]);
|
|
$('checks-eyebrow').textContent = 'nothing to check'; layers(j, sh[0] || null, null); $('msg').textContent = ''; return;
|
|
}
|
|
$('statement').innerHTML = s.proof.statement_decoded ? statementRows(s.proof.statement_decoded, b) : '<dd class="empty">The observer could not decode the public values.</dd>';
|
|
if (!s.proof.has_bytes) {
|
|
$('c-verdict').className = 'cell warm'; $('verdict').textContent = 'bytes dropped';
|
|
$('verdict-s').textContent = `captured ${rel(Date.now() - new Date(s.proof.captured_at).getTime())}; the observer keeps the newest bytes only`;
|
|
$('checks').innerHTML = s.proof.checks ? [['proof bytes hash to the record', s.proof.checks.proof_hash], ['public values hash to the statement', s.proof.checks.statement], ['statement names this block, shard and payout', s.proof.checks.fields]].map(([n, ok]) => `<li><span class="mark ${ok ? 'ok' : 'bad'}">${ok ? '✓' : '✗'}</span><span>${esc(n)}<span class="how">as the observer checked them at capture; the bytes are no longer served</span></span></li>`).join('') : '';
|
|
$('checks-eyebrow').textContent = 'observer\'s record'; layers(j, s, null); $('msg').textContent = ''; return;
|
|
}
|
|
$('msg').textContent = `Fetching ${int(s.proof.bytes)} bytes of proof.`;
|
|
$('checks').innerHTML = `<li><span class="mark wait">…</span><span>fetching the proof bytes</span></li>`;
|
|
const t0 = performance.now();
|
|
const br = await fetch(s.proof.url, { cache: 'force-cache' });
|
|
if (!br.ok) { $('c-verdict').className = 'cell bad'; $('verdict').textContent = 'no bytes'; $('verdict-s').textContent = `the API answered ${br.status}`; $('msg').textContent = ''; layers(j, s, null); return; }
|
|
const bytes = new Uint8Array(await br.arrayBuffer());
|
|
const fetchMs = performance.now() - t0;
|
|
$('msg').textContent = 'Hashing and parsing in this tab.';
|
|
const t1 = performance.now();
|
|
const result = await checkProof(bytes, { block_hash: b.hash, number: b.number, shard: s.i, statement: s.statement, proof_hash: s.proof_hash, payout: s.payout });
|
|
const checkMs = performance.now() - t1;
|
|
const browser = { ...result, ms: checkMs, fetch_ms: fetchMs };
|
|
$('checks').innerHTML = result.checks.map(c => `<li><span class="mark ${c.ok ? 'ok' : 'bad'}">${c.ok ? '✓' : '✗'}</span><span>${esc(c.name)}<span class="how">${esc(c.how)}: got ${esc(c.got)}${c.ok ? '' : ', want ' + esc(c.want)}</span></span></li>`).join('');
|
|
$('checks-eyebrow').textContent = `${ms(checkMs)} here, ${int(bytes.length)} bytes in ${ms(fetchMs)}`;
|
|
$('checks-note').textContent = `The record (statement, proof hash, vote key ${s.prover || ''}) is what the prover signed and chain block ${s.carrier_number !== null ? int(s.carrier_number) : '?'} carried in its coinbase; the bytes come from this site's node. SP1 version in the file: ${result.version || 'n/a'}.`;
|
|
if (result.statement) $('statement').innerHTML = statementRows(result.statement, b);
|
|
const native = s.proof.native;
|
|
const verified = result.ok && native.verified === true;
|
|
$('time').innerHTML = `${esc(ms(checkMs + fetchMs).replace(' ms', ''))}<small>ms</small>`;
|
|
$('time-s').textContent = `${ms(checkMs)} of checks, ${ms(fetchMs)} of download; STARK ${native.verified === true ? ms(native.verify_ms) + ' on the node' : 'not verified here'}`;
|
|
if (verified) { $('c-verdict').className = 'cell good'; $('verdict').textContent = 'VERIFIED'; $('verdict-s').textContent = 'binding checked in this browser; the STARK verified by this site\'s node'; }
|
|
else if (!result.ok) { $('c-verdict').className = 'cell bad'; $('verdict').textContent = 'NOT VERIFIED'; $('verdict-s').textContent = 'the bytes do not match the record the chain carries'; }
|
|
else if (native.verified === false) { $('c-verdict').className = 'cell bad'; $('verdict').textContent = 'NOT VERIFIED'; $('verdict-s').textContent = native.note || 'the native verifier rejected the proof'; }
|
|
else { $('c-verdict').className = 'cell warm'; $('verdict').textContent = 'binding only'; $('verdict-s').textContent = 'checks passed here; no native verdict for this proof'; }
|
|
layers(j, s, browser);
|
|
$('msg').textContent = '';
|
|
}
|
|
function statementRows(st, b) {
|
|
const same = String(st.block_hash).toLowerCase() === String(b.hash).toLowerCase();
|
|
return dl([
|
|
['Block', `${link(st.block_hash)} ${same ? chip('this block', 'ok') : chip('ANOTHER BLOCK', 'bad')}`], ['Number', int(st.number)], ['Chain id', int(st.chain_id)],
|
|
['Shard', `${st.shard}, transactions ${st.tx_start} to ${st.tx_start + st.tx_count - 1 < st.tx_start ? st.tx_start : st.tx_start + st.tx_count - 1} (${st.tx_count})`],
|
|
['Executed, skipped', `${int(st.executed)}, ${int(st.skipped)}`], ['Gas, pgas', `${int(st.gas_used)}, ${int(st.pgas_used)}`],
|
|
['Pre root', `<span class="hash">${esc(st.pre_root)}</span>`], ['Post root', `<span class="hash">${esc(st.post_root)}</span>`], ['Receipts root', `<span class="hash">${esc(st.receipts_root)}</span>`],
|
|
['Link in, out', `<span class="hash">${esc(shortHash(st.link_in, 10, 6))} → ${esc(shortHash(st.link_out, 10, 6))}</span>`], ['Tx acc in, out', `<span class="hash">${esc(shortHash(st.tx_acc_in, 10, 6))} → ${esc(shortHash(st.tx_acc_out, 10, 6))}</span>`],
|
|
['Payout', `<a class="hash" href="/address/${esc(st.prover)}">${esc(st.prover)}</a>`],
|
|
]);
|
|
}
|
|
load().catch(e => { $('msg').textContent = 'The API did not answer: ' + e.message; });
|
|
</script>
|
|
</body>
|
|
</html>
|