aere-research/execution-kernel/mi-replay-diferential.mjs
Aere Network f2ab368c36 Hostile-read repairs across the kernel artifacts: 17 confirmed over-claims fixed to honest
A 6-lens hostile panel with adversarial verification confirmed 17 attacks that
would hold in a public takedown. All repaired: the MI contract now carries the
two base-fee floor-lapse windows (12,978,617-13,087,959 and
13,596,033-13,596,141) as explicit ruleset validation exceptions, so a stranger
implementing the written rules no longer halts where our own follower did; the
honesty boundary now states the full record of reject-on-disagreement including
the windows, and why the fact stays load-bearing; the independence claim is
bounded (upstream skeletons yes, AERE ruleset has one author in both forks, so
a common-author bug passes any differential gate by construction); the harness
verdict claims only what it measures, records engine identity via
web3_clientVersion on every run, refuses same-URL endpoint pairs, and prints
its NOT MEASURED block on every run; stage 4 names the live engine (upstream
parallel processing enabled by default, idling on empty blocks) and its
oracle's model limits; 'governed registry' became 'owner-controlled' with the
single-key fact stated. The panel also confirmed the import-proof doc claimed
a two-month validation history where six days is the truth; fixed.
2026-08-15 23:28:24 +03:00

529 lines
26 KiB
JavaScript

#!/usr/bin/env node
// mi-replay-diferential.mjs
//
// MachineInterface (MI) differential replay harness, Execution Kernel treapta 1.
// Chain 2800 (AERE). See MACHINE-INTERFACE-CONTRACT.md next to this file.
//
// WHAT IT PROVES
// MI is the pure function MI(pre_state, block) -> (post_state, receipts).
// Two independent execution engines each committed a post-state and receipts for the same
// canonical blocks: Besu (the live serial producer) and Nethermind (the independent second
// client). For a range of REAL live blocks this harness fetches, from BOTH clients, the header
// commitments that fully determine an MI result:
//
// stateRoot post-state world trie root (the headline MI output)
// receiptsRoot trie root of receipts (MI output)
// logsBloom OR of all receipt blooms (MI output)
// gasUsed total gas (MI output, compared by value)
// transactionsRoot the block's tx list (MI input identity)
// hash, parentHash block identity + pre-state linkage
//
// If both engines committed the same values for the same input, two independent MI
// implementations derived the same (post_state, receipts). That is the MI determinism claim.
//
// BOTH DIRECTIONS (the D-150 rule)
// The comparison is SYMMETRIC. It takes the union of block numbers on both sides and the union
// of field keys on both sides, and flags three kinds of divergence:
// - present on one side, ABSENT on the other (a lost field, or a block only one side holds)
// - present on both, DIFFERENT value
// "Only on them" is caught with the same weight as "only on us". D-150: we verify what we lost,
// not only what we added.
//
// NEGATIVE CONTROL (why a green run means anything)
// Set the env var AERE_MI_INJECT to plant exactly one synthetic divergence into the REAL
// fetched data, then the harness runs the SAME comparison code and REQUIRES it to go red on the
// exact planted field/block. In inject mode the success condition is inverted: exit 0 only if
// the plant was caught. See the table below and section 8 of the contract.
//
// READ-ONLY BY CONSTRUCTION
// The only JSON-RPC methods it will send are eth_chainId, eth_blockNumber, eth_getBlockByNumber.
// Any other method is refused before it leaves this process. Nothing is signed, nothing is
// broadcast, no validator is touched, no state is written.
//
// NO DEPENDENCIES
// Node 18+ and its built-in http/https only. No npm, no ethers. Runnable by anyone.
//
// USAGE
// node mi-replay-diferential.mjs
// AERE_COUNT=32 node mi-replay-diferential.mjs
// AERE_START=14000000 AERE_END=14000050 node mi-replay-diferential.mjs
// AERE_BESU_RPC=https://rpc.aere.network AERE_NETH_RPC=https://client2.aere.network \
// node mi-replay-diferential.mjs
//
// Negative control (plant a divergence, require red):
// AERE_MI_INJECT=stateRoot node mi-replay-diferential.mjs # forward, value differs
// AERE_MI_INJECT=drop node mi-replay-diferential.mjs # reverse, block only on Besu
// AERE_MI_INJECT=missing:stateRoot node mi-replay-diferential.mjs # reverse, a LOST field
// AERE_MI_INJECT=stateRoot:besu node mi-replay-diferential.mjs # reverse, corrupt Besu side
//
// ENV VARS
// AERE_BESU_RPC default https://rpc.aere.network (also reads BESU_RPC)
// AERE_NETH_RPC default https://client2.aere.network (also reads NETHERMIND_RPC)
// AERE_START, AERE_END explicit inclusive range (decimal). AERE_END may be "head".
// AERE_COUNT blocks ending near head when START/END unset (default 16)
// AERE_SAFETY blocks to stay behind the min head, avoids racing the tip (default 4)
// AERE_CONCURRENCY parallel header fetches (default 6)
// AERE_MI_INJECT negative-control plant (see table above). Unset = normal differential run.
// AERE_OUT results JSON path (default results/mi-replay-<range>-<ts>.json)
//
// EXIT CODES
// normal run: 0 AGREE identical in both directions, coverage > 0
// 1 MISMATCH a field differed or a block was one-sided
// 2 NOT MEASURED no coverage / unreachable endpoint / chain-id mismatch
// inject run: 0 the gate caught the planted divergence (control PASSED)
// 1 the plant slipped through green (control FAILED) OR fatal
import http from 'node:http';
import https from 'node:https';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = path.dirname(fileURLToPath(import.meta.url));
const BESU_RPC = process.env.AERE_BESU_RPC || process.env.BESU_RPC || 'https://rpc.aere.network';
const NETH_RPC = process.env.AERE_NETH_RPC || process.env.NETHERMIND_RPC || 'https://client2.aere.network';
const CONCURRENCY = Math.max(1, parseInt(process.env.AERE_CONCURRENCY || '6', 10));
const DEFAULT_COUNT = 16;
const SAFETY = Math.max(0, parseInt(process.env.AERE_SAFETY || '4', 10));
const INJECT = process.env.AERE_MI_INJECT || '';
// The commitments that fully determine / identify an MI result. See contract section 5.
// kind 'hash' compared as lowercased hex; kind 'num' compared by integer value.
const MI_FIELDS = [
{ name: 'hash', kind: 'hash', role: 'block identity' },
{ name: 'parentHash', kind: 'hash', role: 'pre-state linkage' },
{ name: 'stateRoot', kind: 'hash', role: 'MI output: post-state' },
{ name: 'transactionsRoot', kind: 'hash', role: 'MI input: tx list' },
{ name: 'receiptsRoot', kind: 'hash', role: 'MI output: receipts' },
{ name: 'gasUsed', kind: 'num', role: 'MI output: gas' },
{ name: 'logsBloom', kind: 'hash', role: 'MI output: logs bloom' },
];
const FIELD_BY_NAME = new Map(MI_FIELDS.map((f) => [f.name, f]));
// ---- read-only JSON-RPC over the Node built-ins, no dependencies -------------------------------
const ALLOWED_METHODS = new Set(['eth_chainId', 'eth_blockNumber', 'eth_getBlockByNumber', 'web3_clientVersion']);
function rpc(endpoint, method, params, tries = 4) {
if (!ALLOWED_METHODS.has(method)) {
return Promise.reject(new Error(`method ${method} is not in the read-only allowlist`));
}
const u = new URL(endpoint);
const lib = u.protocol === 'http:' ? http : https;
const port = u.port ? parseInt(u.port, 10) : (u.protocol === 'http:' ? 80 : 443);
const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
const attempt = () => new Promise((resolve, reject) => {
const req = lib.request({
hostname: u.hostname,
port,
path: u.pathname === '/' && u.search ? u.pathname + u.search : (u.pathname || '/'),
method: 'POST',
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) },
timeout: 15000,
}, (res) => {
let d = '';
res.on('data', (c) => { d += c; });
res.on('end', () => {
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode} from ${u.hostname}`));
let j;
try { j = JSON.parse(d); } catch (_) { return reject(new Error(`non-JSON reply from ${u.hostname}: ${d.slice(0, 100)}`)); }
if (j.error) return reject(new Error(`RPC error ${JSON.stringify(j.error)}`));
resolve(j.result);
});
});
req.on('timeout', () => req.destroy(new Error('timeout')));
req.on('error', reject);
req.write(body);
req.end();
});
return (async () => {
let lastErr;
for (let t = 0; t < tries; t++) {
try { return await attempt(); } catch (e) { lastErr = e; await sleep(250 * (t + 1)); }
}
throw lastErr;
})();
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const hx = (n) => '0x' + BigInt(n).toString(16);
async function chainIdOf(endpoint) { return parseInt(await rpc(endpoint, 'eth_chainId', []), 16); }
async function headOf(endpoint) { return parseInt(await rpc(endpoint, 'eth_blockNumber', []), 16); }
// Fetch a header, no tx bodies. Returns a plain object with the MI fields, or null if the block
// is absent (the endpoint does not hold it). Pinned to an explicit number, never "latest".
async function getHeader(endpoint, number) {
const b = await rpc(endpoint, 'eth_getBlockByNumber', [hx(number), false]);
if (!b) return null;
const out = { number: parseInt(b.number, 16) };
for (const f of MI_FIELDS) if (b[f.name] !== undefined) out[f.name] = b[f.name];
return out;
}
async function mapPool(items, limit, worker) {
const results = new Array(items.length);
let next = 0;
async function run() {
for (;;) {
const i = next++;
if (i >= items.length) return;
results[i] = await worker(items[i], i);
}
}
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, run));
return results;
}
// ---- the core comparison: symmetric, both directions --------------------------------------------
function eqField(f, a, b) {
if (a == null || b == null) return a == null && b == null;
if (f.kind === 'num') { try { return BigInt(a) === BigInt(b); } catch (_) { return false; } }
return String(a).toLowerCase() === String(b).toLowerCase();
}
// besuByNum / nethByNum: Map<number, header|null>. numbers: the interval.
// Returns { divergences:[...], directions:{...}, fieldsCompared }.
// Each divergence has { block, field|null, kind, besu, nethermind }.
// kind 'block-only-besu' block held by Besu, absent on Nethermind (reverse direction)
// kind 'block-only-nethermind'block held by Nethermind, absent on Besu (forward would miss it)
// kind 'field-only-besu' field present on Besu, absent on Nethermind (a LOST field)
// kind 'field-only-nethermind'field present on Nethermind, absent on Besu
// kind 'field-differs' present on both, different value
function compareInterval(besuByNum, nethByNum, numbers) {
const divergences = [];
const directions = { forwardDiff: 0, reverseOnlyBesu: 0, reverseOnlyNeth: 0, fieldsCompared: 0 };
for (const n of numbers) {
const a = besuByNum.get(n);
const b = nethByNum.get(n);
if (a == null && b == null) continue; // neither holds it; out of coverage
if (a != null && b == null) {
divergences.push({ block: n, field: null, kind: 'block-only-besu', besu: 'present', nethermind: 'absent' });
directions.reverseOnlyBesu++;
continue;
}
if (a == null && b != null) {
divergences.push({ block: n, field: null, kind: 'block-only-nethermind', besu: 'absent', nethermind: 'present' });
directions.reverseOnlyNeth++;
continue;
}
// both hold the block: compare the UNION of field keys, so a one-sided field is caught.
const keys = new Set([...Object.keys(a), ...Object.keys(b)].filter((k) => k !== 'number' && FIELD_BY_NAME.has(k)));
for (const k of keys) {
const f = FIELD_BY_NAME.get(k);
const av = a[k];
const bv = b[k];
const aHas = av !== undefined && av !== null;
const bHas = bv !== undefined && bv !== null;
if (aHas && !bHas) { divergences.push({ block: n, field: k, kind: 'field-only-besu', besu: av, nethermind: null }); directions.forwardDiff++; continue; }
if (!aHas && bHas) { divergences.push({ block: n, field: k, kind: 'field-only-nethermind', besu: null, nethermind: bv }); directions.forwardDiff++; continue; }
if (!aHas && !bHas) continue;
directions.fieldsCompared++;
if (!eqField(f, av, bv)) divergences.push({ block: n, field: k, kind: 'field-differs', besu: av, nethermind: bv });
}
}
return { divergences, directions };
}
// Per-client pre-state linkage: parentHash(n) == hash(n-1) across the interval. Pins the
// (pre_state, block) -> (post_state) tuple as a chain, not just isolated roots. See contract sec 9.
function preStateLinkage(byNum, numbers) {
const issues = [];
let checked = 0;
for (const n of numbers) {
const cur = byNum.get(n);
const prev = byNum.get(n - 1);
if (!cur || !prev) continue;
checked++;
if (String(cur.parentHash).toLowerCase() !== String(prev.hash).toLowerCase()) {
issues.push(`block ${n} parentHash ${cur.parentHash} != hash(${n - 1}) ${prev.hash}`);
}
}
return { checked, issues };
}
// ---- negative control: plant exactly one synthetic divergence -----------------------------------
// spec grammar: field[@index][:side] | drop[@index][:side] | missing:field[@index][:side]
// bare "1"/"on"/"true" => stateRoot at the middle index on the nethermind side.
function parseInject(spec, numbers) {
let s = spec.trim();
if (s === '1' || s === 'on' || s === 'true') s = 'stateRoot';
let side = 'nethermind';
const sideM = s.match(/:(besu|nethermind|neth)$/i);
if (sideM) { side = sideM[1].toLowerCase() === 'besu' ? 'besu' : 'nethermind'; s = s.slice(0, sideM.index); }
let index = Math.floor(numbers.length / 2);
const idxM = s.match(/@(\d+)$/);
if (idxM) { index = Math.min(numbers.length - 1, parseInt(idxM[1], 10)); s = s.slice(0, idxM.index); }
let op = 'corrupt';
let field = s;
if (s === 'drop') { op = 'drop'; field = null; }
else if (s.startsWith('missing:')) { op = 'missing'; field = s.slice('missing:'.length); }
if (op === 'corrupt' && !FIELD_BY_NAME.has(field)) {
throw new Error(`AERE_MI_INJECT: unknown field "${field}". Use one of: ${MI_FIELDS.map((f) => f.name).join(', ')}, or drop, or missing:<field>`);
}
const block = numbers[index];
return { op, field, side, index, block };
}
function flipHashNibble(v) {
// XOR the last hex nibble with 1, so a valid-shaped but different 32-byte hash is produced.
const last = v[v.length - 1];
const flipped = (parseInt(last, 16) ^ 1).toString(16);
return v.slice(0, -1) + flipped;
}
// Mutate `byNum` in place (a Map). Returns the expected divergence the gate MUST report.
function applyInject(byNumBesu, byNumNeth, plan) {
const target = plan.side === 'besu' ? byNumBesu : byNumNeth;
const h = target.get(plan.block);
if (!h) throw new Error(`inject target block ${plan.block} not present on ${plan.side}`);
if (plan.op === 'drop') {
target.set(plan.block, null);
return { block: plan.block, field: null, expectKind: plan.side === 'besu' ? 'block-only-nethermind' : 'block-only-besu' };
}
if (plan.op === 'missing') {
delete h[plan.field];
// deleting on nethermind => field present on besu only => field-only-besu, and vice versa.
return { block: plan.block, field: plan.field, expectKind: plan.side === 'besu' ? 'field-only-nethermind' : 'field-only-besu' };
}
// corrupt: change the value but keep the shape
const f = FIELD_BY_NAME.get(plan.field);
if (f.kind === 'num') h[plan.field] = hx(BigInt(h[plan.field]) + 1n);
else h[plan.field] = flipHashNibble(String(h[plan.field]));
return { block: plan.block, field: plan.field, expectKind: 'field-differs' };
}
// ---- range resolution ---------------------------------------------------------------------------
function resolveRange(head) {
let start;
let end;
if (process.env.AERE_START || process.env.AERE_END) {
end = process.env.AERE_END && process.env.AERE_END !== 'head' ? parseInt(process.env.AERE_END, 10) : head;
start = process.env.AERE_START ? parseInt(process.env.AERE_START, 10) : end;
} else {
const count = parseInt(process.env.AERE_COUNT || String(DEFAULT_COUNT), 10);
end = head;
start = Math.max(0, head - count + 1);
}
if (start > end) [start, end] = [end, start];
return { start, end };
}
// ---- main ---------------------------------------------------------------------------------------
async function main() {
const report = {
harness: 'execution-kernel/mi-replay-diferential.mjs',
contract: 'MACHINE-INTERFACE-CONTRACT.md',
generatedAt: new Date().toISOString(),
besuRpc: BESU_RPC,
nethermindRpc: NETH_RPC,
mode: INJECT ? 'negative-control' : 'differential',
inject: INJECT || null,
chainId: null,
range: null,
heads: null,
blocksCovered: 0,
blocksUncovered: 0,
directions: null,
preStateLinkage: null,
divergences: [],
honesty: 'A matching header root served over RPC proves both endpoints serve the same bytes; '
+ 'the load-bearing fact is that Nethermind validates on processing and rejects on '
+ 'disagreement. Consensus is classical secp256k1 ECDSA QBFT plus Falcon; MI is execution '
+ 'only and this is not a post-quantum-consensus claim. Not a benchmark. Both endpoints are '
+ 'operated by the same party (the trust root does not move; what is removed is the class '
+ 'of lies needing only one client implementation). State older than the ~512-block RPC '
+ 'state window is not probed. Engine identity of each endpoint is recorded below.',
engines: null,
verdict: null,
};
console.log('MachineInterface (MI) differential replay harness, Execution Kernel treapta 1');
console.log(` contract: ${report.contract}`);
console.log(` Besu RPC: ${BESU_RPC}`);
console.log(` Nethermind: ${NETH_RPC}`);
console.log(` mode: ${report.mode}${INJECT ? ` (AERE_MI_INJECT=${INJECT})` : ''}`);
// endpoint-identity gate: one endpoint agreeing with itself measures nothing
if (BESU_RPC.replace(/\/+$/, '') === NETH_RPC.replace(/\/+$/, '')) {
report.verdict = 'NOT MEASURED: both endpoints are the same URL; agreement of an endpoint with itself is not a measurement';
console.log('RESULT: ' + report.verdict);
return finish(report, 2);
}
// engine identity, recorded so "who answered" travels with every report
try {
const [ebesu, eneth] = await Promise.all([
rpc(BESU_RPC, 'web3_clientVersion', []), rpc(NETH_RPC, 'web3_clientVersion', [])]);
report.engines = { besu: ebesu, nethermind: eneth };
console.log(' engines: ' + ebesu + ' | ' + eneth);
if (String(ebesu).toLowerCase().split('/')[0] === String(eneth).toLowerCase().split('/')[0]) {
console.log(' WARNING: both endpoints report the SAME engine family; the cross-client claim does not hold for this run');
}
} catch (e) { report.engines = { error: String(e.message) }; }
// NOT MEASURED, printed on every run (the contract's non-claims, stated by the tool itself):
console.log(' NOT MEASURED by this harness: post-quantum consensus; independent derivation');
console.log(' (proven only via the follower validate-on-processing behavior); throughput or');
console.log(' latency; who operates the endpoints (same party today); state older than the');
console.log(' ~512-block RPC window; gas accounting beyond the gasUsed commitment.');
// chain-id gate on both endpoints
const [besuChain, nethChain] = await Promise.all([chainIdOf(BESU_RPC), chainIdOf(NETH_RPC)]);
report.chainId = { besu: besuChain, nethermind: nethChain };
if (besuChain !== 2800) console.warn(` WARNING: Besu chainId ${besuChain}, expected 2800`);
if (nethChain !== besuChain) {
report.verdict = `NOT MEASURED: chainId mismatch (Besu ${besuChain} vs Nethermind ${nethChain})`;
return finish(report, 2);
}
// heads: compare only up to the lower head, minus a safety margin off the tip
const [besuHead, nethHead] = await Promise.all([headOf(BESU_RPC), headOf(NETH_RPC)]);
report.heads = { besu: besuHead, nethermind: nethHead };
const safeHead = Math.max(0, Math.min(besuHead, nethHead) - SAFETY);
console.log(` heads: Besu ${besuHead}, Nethermind ${nethHead} (compare at/below ${safeHead})`);
let { start, end } = resolveRange(safeHead);
if (end > safeHead) end = safeHead;
if (start > end) {
report.verdict = 'NOT MEASURED: no blocks in range are held by both clients';
return finish(report, 2);
}
report.range = { start, end };
const numbers = [];
for (let n = start; n <= end; n++) numbers.push(n);
// one extra predecessor for the pre-state linkage check, if available
const linkNumbers = start > 0 ? [start - 1, ...numbers] : numbers.slice();
console.log(` range: [${start} .. ${end}] (${numbers.length} blocks)\n`);
// fetch both sides
const [besuArr, nethArr] = await Promise.all([
mapPool(linkNumbers, CONCURRENCY, (n) => getHeader(BESU_RPC, n).catch(() => null)),
mapPool(linkNumbers, CONCURRENCY, (n) => getHeader(NETH_RPC, n).catch(() => null)),
]);
const besuByNum = new Map();
const nethByNum = new Map();
linkNumbers.forEach((n, i) => { besuByNum.set(n, besuArr[i]); nethByNum.set(n, nethArr[i]); });
// negative control: plant exactly one divergence into the REAL fetched data
let expected = null;
if (INJECT) {
const plan = parseInject(INJECT, numbers);
expected = applyInject(besuByNum, nethByNum, plan);
console.log(' NEGATIVE CONTROL: planted a synthetic divergence into fetched data');
console.log(` plant: op=${plan.op} field=${plan.field ?? '(whole block)'} side=${plan.side} block=${plan.block}`);
console.log(` require: the gate reports kind "${expected.expectKind}" at block ${expected.block}` +
`${expected.field ? ` field ${expected.field}` : ''}\n`);
}
// coverage accounting (before injection semantics): a block held by neither is uncovered
for (const n of numbers) {
const held = (besuByNum.get(n) != null) || (nethByNum.get(n) != null);
if (held) report.blocksCovered++; else report.blocksUncovered++;
}
// the core comparison (SAME code for real run and negative control)
const { divergences, directions } = compareInterval(besuByNum, nethByNum, numbers);
report.divergences = divergences;
report.directions = directions;
// pre-state linkage, per client
report.preStateLinkage = {
besu: preStateLinkage(besuByNum, numbers),
nethermind: preStateLinkage(nethByNum, numbers),
};
// sample line so the roots are visibly real, not asserted
const sampleN = numbers[0];
const sB = besuByNum.get(sampleN);
const sN = nethByNum.get(sampleN);
if (sB && sN) {
console.log('Sample (both clients, block ' + sampleN + '):');
console.log(` besu stateRoot ${sB.stateRoot}`);
console.log(` nethermind stateRoot ${sN.stateRoot}`);
console.log('');
}
console.log('Directions (both, per D-150):');
console.log(` fields compared on blocks held by both: ${directions.fieldsCompared}`);
console.log(` forward (value differs / one-sided field): ${directions.forwardDiff}`);
console.log(` reverse (block only on Besu): ${directions.reverseOnlyBesu}`);
console.log(` reverse (block only on Nethermind): ${directions.reverseOnlyNeth}`);
const link = report.preStateLinkage;
console.log(` pre-state linkage: besu ${link.besu.issues.length ? 'FAIL' : 'ok'} (${link.besu.checked} links), ` +
`nethermind ${link.nethermind.issues.length ? 'FAIL' : 'ok'} (${link.nethermind.checked} links)`);
console.log('');
// verdicts
if (INJECT) {
const caught = divergences.find((d) => d.block === expected.block && d.kind === expected.expectKind &&
(expected.field == null || d.field === expected.field));
if (caught) {
report.verdict = `NEGATIVE CONTROL PASSED: the gate went red on the planted ${expected.expectKind}` +
` at block ${expected.block}${expected.field ? ` field ${expected.field}` : ''}.`;
console.log('RESULT: ' + report.verdict);
console.log(' (a green run therefore means something: this gate can and did reject.)');
return finish(report, 0);
}
report.verdict = `NEGATIVE CONTROL FAILED: the planted ${expected.expectKind} at block ${expected.block}` +
`${expected.field ? ` field ${expected.field}` : ''} was NOT caught. The gate is decoration.`;
console.log('RESULT: ' + report.verdict);
return finish(report, 1);
}
const linkFail = link.besu.issues.length > 0 || link.nethermind.issues.length > 0;
if (report.blocksCovered === 0) {
report.verdict = 'NOT MEASURED: no block in the range was held by either client';
console.log('RESULT: ' + report.verdict);
return finish(report, 2);
}
if (divergences.length === 0 && !linkFail) {
report.verdict = `AGREE: Besu and Nethermind committed identical MI fields ` +
`(stateRoot, receiptsRoot, transactionsRoot, logsBloom, gasUsed, hash, parentHash) in BOTH ` +
`directions for all ${report.blocksCovered} covered blocks, and the pre-state chain links on ` +
`both clients. What this proves: both endpoints serve identical canonical MI commitments ` +
`(the same bytes) for every covered block. Independent derivation is NOT proven by this ` +
`run alone; it rests on the follower's validate-on-processing behavior, stated in the ` +
`honesty field, and on the recorded engine identities.`;
console.log('RESULT: AGREE across ' + report.blocksCovered + ' blocks (both directions, 0 divergences).');
finish(report, 0);
} else {
report.verdict = `MISMATCH: ${divergences.length} divergence(s)` +
(linkFail ? ' plus a pre-state linkage failure' : '') + ` across ${report.blocksCovered} blocks.`;
console.log(`RESULT: MISMATCH, ${divergences.length} divergence(s):`);
for (const d of divergences.slice(0, 12)) {
console.log(` block ${d.block} ${d.field ? `field ${d.field} ` : ''}[${d.kind}] besu=${trunc(d.besu)} neth=${trunc(d.nethermind)}`);
}
if (linkFail) {
[...link.besu.issues, ...link.nethermind.issues].slice(0, 6).forEach((s) => console.log(' linkage: ' + s));
}
finish(report, 1);
}
}
function trunc(v) { const s = String(v); return s.length > 22 ? s.slice(0, 22) + '..' : s; }
function finish(report, code) {
try {
const outDir = path.join(HERE, 'results');
fs.mkdirSync(outDir, { recursive: true });
const tag = report.range ? `${report.range.start}-${report.range.end}` : 'norange';
const outPath = process.env.AERE_OUT || path.join(outDir, `mi-replay-${report.mode}-${tag}-${Date.now()}.json`);
fs.writeFileSync(outPath, JSON.stringify(report, null, 2));
console.log(`\nReport written: ${outPath}`);
} catch (e) {
console.warn('could not write report: ' + e.message);
}
process.exit(code);
}
main().catch((e) => {
console.error('FATAL: ' + e.message);
process.exit(2);
});