From 37f603d06fa35bb8a2b11584627f44aab7ea7f4b Mon Sep 17 00:00:00 2001 From: Aere Network Date: Sun, 11 Oct 2026 10:16:40 +0300 Subject: [PATCH] notarization verifier: the covering anchor is the first one STRICTLY after the block (an anchor's certificate signs its parent); 32 zero bytes are not a digest; the certificate form is named (v1, v2 scheme-tagged, v3 round-bound) and an unknown form fails the sealed link --- notarizare/verifica-certificat.mjs | 64 +++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/notarizare/verifica-certificat.mjs b/notarizare/verifica-certificat.mjs index 961daca..deb1931 100644 --- a/notarizare/verifica-certificat.mjs +++ b/notarizare/verifica-certificat.mjs @@ -32,6 +32,7 @@ * Exit: 0 every measured link passed, 1 something failed, 2 nothing could be measured. */ import fs from 'node:fs'; +import { pathToFileURL } from 'node:url'; // ---------- Keccak-256, inlined so this file needs nothing installed ---------- const RC = [1n,0x8082n,0x800000000000808an,0x8000000080008000n,0x808bn,0x80000001n,0x8000000080008081n, @@ -106,6 +107,47 @@ const areTextASCII = (u8, s) => { const t = Array.from(u8).map((x) => String.fromCharCode(x)).join(''); return t.includes(s); }; +// A 32-byte vanityData of all zeros is NOT a digest: the Nethermind-derived client writes 32 zero bytes where it anchors nothing +// (measured 2026-09-11 on chain 2800, at the admission of the tenth validator), so "32 bytes, no 'besu'" alone called every one of +// its blocks an anchor. The anchor digest is a keccak256 output; zero is refused here as it is in every gate of the house. +const esteZero = (u8) => Array.from(u8).every((b) => b === 0); +// Minimal RLP reader (lists and strings), enough to name the FORM of the certificate in element 6 of extraData. +function citesteRLP(u8, pos) { + const p = u8[pos]; + if (p < 0x80) return { list: false, bytes: u8.slice(pos, pos + 1), next: pos + 1 }; + if (p < 0xb8) { const n = p - 0x80; return { list: false, bytes: u8.slice(pos + 1, pos + 1 + n), next: pos + 1 + n }; } + if (p < 0xc0) { const k = p - 0xb7; let n = 0; for (let j = 0; j < k; j++) n = n * 256 + u8[pos + 1 + j]; return { list: false, bytes: u8.slice(pos + 1 + k, pos + 1 + k + n), next: pos + 1 + k + n }; } + let k = 0, n; + if (p < 0xf8) n = p - 0xc0; else { k = p - 0xf7; n = 0; for (let j = 0; j < k; j++) n = n * 256 + u8[pos + 1 + j]; } + const start = pos + 1 + k, end = start + n, items = []; let c = start; + while (c < end) { const it = citesteRLP(u8, c); items.push(it); c = it.next; } + return { list: true, items, next: end }; +} +// The certificate's form, dispatched on its FIRST element exactly as both clients and the public verifier do (2026-10-07, 1(b)): +// a list is v1 (Falcon-512 rows), the scalar 2 is v2 (scheme-tagged, AERE-PQ-ANCHOR-2), the scalar 3 is v3 (round-bound: every seal +// is over the parent's commit message for the ONE round the certificate names, AERE-PQ-ANCHOR-3). Anything else is named unknown, +// never read as one of them. This tool does not verify the seals (it installs nothing); it names what the anchor carries. +// Exported: this is a second copy of the form (this file stays dependency-free on purpose), so an equivalence probe compares it with +// the shared certificate decoder row by row on the 1(b) vectors. +export function formaCertificatului(extra) { + let top; try { top = citesteRLP(extra, 0); } catch { return { forma: 'unreadable', text: 'extraData does not decode as RLP' }; } + if (!top.list) return { forma: 'unreadable', text: 'extraData is not an RLP list' }; + if (top.items.length < 6) return { forma: 'none', text: 'no certificate element (extraData has ' + top.items.length + ' elements)' }; + const el = top.items[5]; + if (!el.list) return { forma: 'unknown', text: 'element 6 is not a list' }; + if (el.items.length === 0) return { forma: 'none', text: 'element 6 is the empty list (no certificate)' }; + const first = el.items[0]; + if (first.list) return { forma: 'v1', text: 'certificate v1, ' + el.items.length + ' Falcon-512 rows (no scheme tags, no round)' }; + const scalar = (it) => (it.list ? null : Array.from(it.bytes).reduce((a, b) => a * 256 + b, 0)); + if (first.bytes.length === 1 && first.bytes[0] === 2 && el.items.length === 2 && el.items[1].list) return { forma: 'v2', text: 'certificate v2 (scheme-tagged, AERE-PQ-ANCHOR-2), ' + el.items[1].items.length + ' rows' }; + // v3: the round is a SCALAR (a list there is an unknown form, as in the house decoder; found 2026-10-07 by the decoder-equivalence + // probe on the form vector vrsf-03-runda-lista, which this copy read as "v3, round null") + if (first.bytes.length === 1 && first.bytes[0] === 3 && el.items.length === 3 && !el.items[1].list && el.items[2].list) { + const r = scalar(el.items[1]); + return { forma: 'v3', round: r, text: 'certificate v3, round ' + r + ' (round-bound seals, AERE-PQ-ANCHOR-3), ' + el.items[2].items.length + ' rows' }; + } + return { forma: 'unknown', text: 'certificate of UNKNOWN form (opens with ' + (first.list ? 'a list' : '0x' + Array.from(first.bytes).map((b) => b.toString(16).padStart(2, '0')).join('')) + ', ' + el.items.length + ' elements)' }; +} // ---------- report, three verdicts only ---------- const linii = []; @@ -127,7 +169,9 @@ async function rpc(method, params) { return j.result; } -(async () => { +// Run only as a program: imported (by the decoder-equivalence probe) this file exports formaCertificatului and does nothing else. +const RULAT_DIRECT = !!process.argv[1] && pathToFileURL(fs.realpathSync(process.argv[1])).href.toLowerCase() === import.meta.url.toLowerCase(); +if (RULAT_DIRECT) (async () => { if (!CALE || !fs.existsSync(CALE)) { console.error('folosire: node verifica-certificat.mjs certificat.json [--fisier document] [--rpc URL]'); process.exitCode = 2; return; @@ -222,7 +266,10 @@ async function rpc(method, params) { NM('sealed', 'the block is covered by the chain post-quantum validator certificate', 'this certificate carries no anchorBlock / anchorInterval, and this check will not fall back to a hard-coded schedule: those numbers change when the validator set changes. Re-issue the certificate with them, or verify the anchor by hand.'); } else { - const urm = nr <= start ? start : start + Math.ceil((nr - start) / ancora) * ancora; + // the covering anchor is the first one STRICTLY after the block: an anchor's certificate signs its PARENT (2026-10-01; until + // then "at or after", so a record notarized in an anchor block itself was reported covered by that anchor) + const dupa = nr + 1; + const urm = dupa <= start ? start : start + Math.ceil((dupa - start) / ancora) * ancora; const blk = await rpc('eth_getBlockByNumber', ['0x' + urm.toString(16), false]); if (!blk) NM('sealed', 'the block is covered by the chain post-quantum validator certificate', 'the covering anchor at ' + urm.toLocaleString('en-US') + ' is not on chain yet; re-run later'); else { @@ -238,14 +285,21 @@ async function rpc(method, params) { const marime = de_baza === null ? 'header ' + oct + ' bytes (plain-header baseline NOT MEASURED: the neighbouring header did not load)' : 'header ' + oct + ' bytes against a measured plain header of ' + de_baza + ' bytes at ' + (urm - 1).toLocaleString('en-US') + ', so ' + (oct - de_baza) + ' bytes of validator certificate'; + const forma = formaCertificatului(octeti(blk.extraData)); if (!vanity) { NM('sealed', 'the block is covered by the chain post-quantum validator certificate', 'extraData at anchor ' + urm.toLocaleString('en-US') + ' did not decode as an RLP list, so nothing is claimed either way'); + } else if (vanity.length === 32 && esteZero(vanity)) { + FAIL('sealed', 'the first anchor after this block carries a post-quantum validator certificate', + 'anchor ' + urm.toLocaleString('en-US') + ' carries 32 ZERO bytes as vanityData: not a digest (a client writes zeros where it anchors nothing); ' + forma.text + '; ' + marime); + } else if (vanity.length === 32 && !areTextASCII(vanity, 'besu') && !['v1', 'v2', 'v3'].includes(forma.forma)) { + FAIL('sealed', 'the first anchor after this block carries a post-quantum validator certificate of a known form', + 'anchor ' + urm.toLocaleString('en-US') + ' has a 32-byte vanityData ' + hex(vanity) + ' but ' + forma.text + ' (known forms: v1, v2 scheme-tagged, v3 round-bound); ' + marime); } else if (vanity.length === 32 && !areTextASCII(vanity, 'besu')) { - PASS('sealed', 'the anchor at or after this block carries a post-quantum validator certificate whose digest is inside the block hash preimage, so rewriting this block later needs post-quantum forgeries', - 'anchor ' + urm.toLocaleString('en-US') + ', vanityData is a 32-byte digest ' + hex(vanity) + '; ' + marime); + PASS('sealed', 'the first anchor after this block carries a post-quantum validator certificate whose digest is inside the block hash preimage, so rewriting this block later needs post-quantum forgeries', + 'anchor ' + urm.toLocaleString('en-US') + ', vanityData is a 32-byte digest ' + hex(vanity) + '; ' + forma.text + '; ' + marime); } else { - FAIL('sealed', 'the anchor at or after this block carries a post-quantum validator certificate', + FAIL('sealed', 'the first anchor after this block carries a post-quantum validator certificate', 'anchor ' + urm.toLocaleString('en-US') + ' carries ordinary vanityData (' + vanity.length + ' bytes' + (areTextASCII(vanity, 'besu') ? ", client string containing 'besu'" : '') + '), not an anchor digest; ' + marime); } }