diff --git a/infra/fast-time/vote-or-burn.mjs b/infra/fast-time/vote-or-burn.mjs index 900ed318f..121281850 100644 --- a/infra/fast-time/vote-or-burn.mjs +++ b/infra/fast-time/vote-or-burn.mjs @@ -231,15 +231,17 @@ try { if (blk.miner && !bridge.address[bkey]) bridge.address[bkey] = String(blk.miner).toLowerCase(); } for (const row of allRows) { - const blk = await evmCall(a.evmPort, 'eth_getBlockByHash', [`0x${row.chain_block}`, false]); + // the pairing (read live on 7 October 2026, 09:5x UK): the execution layer credits a chain block in ITS OWN record + // (the segment of chain block N holds N and its non-chain blues, not its selected parent), while the UTXO coinbase + // of N pays N's selected parent; so the parent's UTXO payment in N's coinbase is compared with the parent's own EVM + // record, where its miner's entry is its own credit (a non-chain blue of the same key in that record would make + // two entries; such rows are skipped) + const prec = await evmCall(a.evmPort, 'eth_getBlockByHash', [`0x${row.parent_hash}`, false]); const addr = bridge.address[row.parent_key]; - if (!blk || !addr) continue; - // the paid block's entry: of the reward entries for its miner's address (a chain block that merges its own key's - // block carries two, its own and the parent's, in the segment's order), the parent's is the smaller credit under - // ledger N8's rule (the older block on the launch ramp) and either one before it (both at the chain block's DAA) - const entries = (blk.igneum?.rewards || []).filter(e => String(e.miner).toLowerCase() === addr).map(e => BigInt(e.wei)); - if (!entries.length) continue; - const entry = { wei: entries.reduce((m, w) => (w < m ? w : m)).toString() }; + if (!prec || !addr) continue; + const entries = (prec.igneum?.rewards || []).filter(e => String(e.miner).toLowerCase() === addr).map(e => BigInt(e.wei)); + if (entries.length !== 1) continue; + const entry = { wei: entries[0].toString() }; bridge.rows++; const wei = BigInt(entry.wei), want = BigInt(row.producer) * WEI_PER_SOMPI; // the EVM credit per key, for the bonus reading on the EVM side (ramp-neutral: a ratio of medians) @@ -268,8 +270,11 @@ const checks = { a_pool_is_a_quarter: poolA != null && medA && Math.abs(poolA / medA - 0.25) <= TOL, // ledger N7: the EVM side pays a silent key the bonus share too (B's credit over A's reads the rule's figure) evm_bonus_ratio_matches: bridge.evm_a.length >= PAIRS && bridge.evm_b.length >= PAIRS && evmRatio != null && Math.abs(evmRatio - want.producer) <= TOL && !bridge.error, - // ledger N8: with the per-block rule on, every pair row's EVM credit equals the UTXO coinbase's payment exactly - ...(SUBSIDY_PER_BLOCK ? { bridge_identity_exact: bridge.rows >= PAIRS && bridge.disagree.length === 0 && !bridge.error } : {}), + // the bridge identity on the selected chain: every pair row's EVM credit equals the UTXO coinbase's payment exactly, + // with or without ledger N8's rule (a chain block is credited at its own DAA in its own record on both ledgers; the + // rule changes the non-chain blues a chain block merges, which this harness's linear chain never has: the N8 case + // needs wide mergesets, the 10-bps profile, and is owed there) + bridge_identity_exact: bridge.rows >= PAIRS && bridge.disagree.length === 0 && !bridge.error, }; if (REPLAY) { checks.replay_late_node_holds_every_block = !!replay?.synced;