tools/verify-anchor.mjs: H_hdr per chain; a header without the proposer seal at or above H_hdr is a FAIL (D-401)

This commit is contained in:
Aere Network 2026-10-31 21:32:37 +02:00
parent 528f88b53f
commit ab9848ab24

View File

@ -31,8 +31,9 @@
// round r (2026-09-25, D-406), on the most recent headers (PROPOSER_HEADERS, default 20);
// and, as the nodes judge every header from H_hdr, the round is a canonical integer
// at most 1024 and the coinbase is the proposer of some round 0..r at that height.
// Where no header carries it (chain 2800 below its stage 5 height) the tool says so and this leg does not
// change the verdict.
// Where no header carries it BELOW the chain's stage 5 height H_hdr the tool says so and this leg does not
// change the verdict; from H_hdr (25,080,000 on 2800, 3,131,000 on 28001; HEADER_SEAL_BLOCK=<height|none>)
// a header without it is a FAIL, as the nodes refuse it (2026-10-31, D-401).
// 7. ROUND (1(b), D-470, learned 2026-10-07; active on testnet 28001 since block 6,157,024 and on chain 2800 since
// block 24,148,752, 2026-10-24) from a chain's round height H_r the certificate is
// v3, RLP[3, round, [[scheme, idx, sig], ...]] under "AERE-PQ-ANCHOR-3", and every seal in it is over
@ -156,6 +157,16 @@ const ANCHOR_V2_BLOCK = process.env.ANCHOR_V2_BLOCK === undefined ? ANCHOR_V2_DE
if (ANCHOR_V2_BLOCK !== null && !(Number.isInteger(ANCHOR_V2_BLOCK) && ANCHOR_V2_BLOCK > 0)) { console.error('FAIL: ANCHOR_V2_BLOCK must be a positive block height or "none"'); stop(1); }
// H_r is above the v2 height on every chain that has one; both clients refuse any other configuration at startup (ROUND-01)
if (COMMIT_ROUND_BLOCK !== null && (ANCHOR_V2_BLOCK === null || COMMIT_ROUND_BLOCK <= ANCHOR_V2_BLOCK)) { console.error('FAIL: COMMIT_ROUND_BLOCK=' + COMMIT_ROUND_BLOCK + ' needs the v2 height below it (ANCHOR_V2_BLOCK is ' + (ANCHOR_V2_BLOCK === null ? 'unset' : ANCHOR_V2_BLOCK) + '); both clients refuse that configuration at startup'); stop(1); }
// H_hdr per chain (2026-10-31, D-401): AIP-22 stage 5, the height from which every header carries element 6 (the proposer's Falcon-512
// seal) and both clients refuse a header without it. Testnet 28001 from block 3,131,000 (2026-09-23), chain 2800 from block 25,080,000
// (2026-10-30). Until this date this tool did not know H_hdr and counted a header without element 6 as "before stage 5" at ANY height;
// element 6 is outside the block hash, so an endpoint that cut it changed nothing the hash checks, and on chain 2800 above H_hdr the
// tool printed "stage 5 is not active at these heights" and exited 0. HEADER_SEAL_BLOCK overrides (a height, or "none"); another chain
// has none.
const HEADER_SEAL_DEFAULT = CHAIN_ID === 2800 ? 25080000 : CHAIN_ID === 28001 ? 3131000 : null;
const HEADER_SEAL_BLOCK = process.env.HEADER_SEAL_BLOCK === undefined ? HEADER_SEAL_DEFAULT
: (/^(none|unset|)$/i.test(process.env.HEADER_SEAL_BLOCK) ? null : Number(process.env.HEADER_SEAL_BLOCK));
if (HEADER_SEAL_BLOCK !== null && !(Number.isInteger(HEADER_SEAL_BLOCK) && HEADER_SEAL_BLOCK > 0)) { console.error('FAIL: HEADER_SEAL_BLOCK must be a positive block height or "none"'); stop(1); }
// the form the certificate of anchor a must have (PROIECT-1B 2.1, certificateFormAt in both clients): 3 from H_r, 2 from V, 1 below V
const certificateFormAt = (a) => (COMMIT_ROUND_BLOCK !== null && a >= COMMIT_ROUND_BLOCK ? 3 : ANCHOR_V2_BLOCK !== null && a >= ANCHOR_V2_BLOCK ? 2 : 1);
const POP_DOMAIN = 'AERE-PQ-POP-1';
@ -1100,7 +1111,11 @@ let propOk = 0, propBad = 0, propUnmeasured = 0, propAbsent = 0, propReproposed
const b = rec.b;
const ex = hex(b.extraData);
const items = rlpRead(ex, 0).item;
if (items.length < 7) { propAbsent++; continue; }
if (items.length < 7) {
// D-401 (2026-10-31): from H_hdr a header without element 6 is refused by both clients, so here it is a FAIL, never "before stage 5"
if (HEADER_SEAL_BLOCK !== null && h >= HEADER_SEAL_BLOCK) { propBad++; console.log(h + ' FAIL: AIP-22 stage 5 is in force on chain ' + CHAIN_ID + ' from block ' + HEADER_SEAL_BLOCK + ', and this header carries no proposer seal (element 6 is missing); the nodes refuse such a header: not admitted'); continue; }
propAbsent++; continue;
}
const onchain = keccak(headerRlp(b, rlpEncode([nested(items[0]), nested(items[1]), nested(items[2]), Buffer.alloc(0), []])));
if (!onchain.equals(hex(b.hash))) {
propUnmeasured++;