diff --git a/infra/fast-time/class-v5-signal.mjs b/infra/fast-time/class-v5-signal.mjs index 36f3eed80..7da90ee02 100644 --- a/infra/fast-time/class-v5-signal.mjs +++ b/infra/fast-time/class-v5-signal.mjs @@ -4,23 +4,24 @@ // the v4 FLOOR at --v4-floor (default 120, epoch 2), the class v5 floor at --floor (default 100000: far away, so v5 is // ENABLED and the signal decides; `never` turns the object off), the signal window at --window (default 60 DAA, one // epoch; seven windows = 420 DAA, so the first epoch whose seed block has seven full windows below it is epoch 8 at -// DAA 480), each node's object byte by IGNEUM_CLASS_SIGNAL (--signal a,b,c), each node's exec RPC on its own loopback +// DAA 480), each node's object byte by IGNEUM_CLASS_SIGNAL (--signal a,b,c; since the 0.3.20 node line the amended +// class v4 is object byte 5 and class v5 is 6), each node's exec RPC on its own loopback // port and each CPU miner's --exec-rpc pointing at its node (the state stream after each epoch's seed block). A fourth // node runs with --evm-disable (--stateless-node, default on): it mines until the flip and then serves no class v5 // template, which the harness counts. Ports 29760 and up, network igneum-devnet-976, data /tmp/igneum-fast-time-v5s. // // The cases and the known-failed cases: -// --signal 5,5,4 --expect no-flip two of three signal v5: 67 percent, the class must stay v4 (run 11 epochs) -// --signal 5,5,5 --expect flip all three: v5 from epoch 8, the first boundary with seven full windows; every +// --signal 6,6,5 --expect no-flip two of three signal v5: 67 percent, the class must stay v4 (run 11 epochs) +// --signal 6,6,6 --expect flip all three: v5 from epoch 8, the first boundary with seven full windows; every // miner's v5 id equals the CLI's --program-class v5 id and differs from the same // seed's v4 id; the stateless node's miner accepts 0 blocks after the flip; 0 rejected -// --signal 5,5,5 --floor never --expect no-flip the object off: byte 5 counts as v4 only -// --signal 4,4,4 --floor 600 --expect floor nobody signals v5: it flips at the floor (epoch 10) and not before -// --signal 5,5,5 --expect flip --stale 2 proof of following's known-failed case: miner 2 keeps the first stream it +// --signal 6,6,6 --floor never --expect no-flip the object off: byte 6 counts as v4 only +// --signal 5,5,5 --floor 600 --expect floor nobody signals v5: it flips at the floor (epoch 10) and not before +// --signal 6,6,6 --expect flip --stale 2 proof of following's known-failed case: miner 2 keeps the first stream it // fetched (--freeze-state); from the epoch after the flip every block it mines is // rejected (accepted after the first refresh 0, rejected above 0); PASS means the // chain threw the stale miner off -// --signal 5,5,4 --expect flip the known-failed case of the harness itself: it must report FAIL (no flip) +// --signal 6,6,5 --expect flip the known-failed case of the harness itself: it must report FAIL (no flip) // // node infra/fast-time/class-v5-signal.mjs --signal a,b,c --expect flip|no-flip|floor [--floor |never] // [--v4-floor 120] [--window 60] [--v3-activation 60] [--secs 780] [--epochs 11] [--stale ] [--no-stateless-node] @@ -55,7 +56,7 @@ const WINDOW = flag('window', 60); const STALE = sflag('stale') == null ? null : +sflag('stale'); const STATELESS_NODE = !args.includes('--no-stateless-node'); const WINDOWS = 7; -const SIGNAL = (sflag('signal') || '5,5,5').split(',').map(Number); +const SIGNAL = (sflag('signal') || '6,6,6').split(',').map(Number); const EXPECT = sflag('expect') || 'flip'; if (!['flip', 'no-flip', 'floor'].includes(EXPECT) || SIGNAL.length !== 3) { console.error('usage: --signal a,b,c --expect flip|no-flip|floor'); process.exit(2); } const started = []; @@ -219,7 +220,8 @@ const BOUNDARY = firstV4 ? firstV4.epoch * EPOCH : Infinity; const before = blocks.filter(b => b.daa < BOUNDARY), after = blocks.filter(b => b.daa >= BOUNDARY); // the signal bytes on the chain: the share of blocks whose version high byte is 4 const versionBytes = blocks.reduce((m, b) => { const v = b.version >> 8; m[v] = (m[v] || 0) + 1; return m; }, {}); -const signalShareOnChain = blocks.length ? Math.round(10000 * (blocks.filter(b => (b.version >> 8) >= 5).length) / blocks.length) : 0; +const V5_BYTE = 6; +const signalShareOnChain = blocks.length ? Math.round(10000 * (blocks.filter(b => (b.version >> 8) >= V5_BYTE).length) / blocks.length) : 0; const programs = new Map(); for (const i of nodes.map(n => n.i)) for (const l of minerLog(i)) { @@ -376,7 +378,7 @@ const summary = { sinks, block_counts: counts, signal_lines: signalLines, floor_lines: floorLines, samples, }; writeFileSync(`${TMP}/summary.json`, JSON.stringify(summary, null, 2)); -log(`SUMMARY ${pass ? 'PASS' : 'FAIL'} (expect ${EXPECT}, signals ${SIGNAL.join('/')}, stale ${STALE ?? 'none'}): ${firstV4 ? `v5 from epoch ${firstV4.epoch} at DAA ${firstV4.daa}` : 'no v5 epoch'}; epochs seen ${[...epochs.entries()].map(([e, v]) => `e${e}:${v.class}:${v.bps5}bps:d${v.day}`).join(' ')}; chain bytes ${JSON.stringify(versionBytes)} (${signalShareOnChain} bps at byte 5+); blocks ${before.length} / ${after.length}; rejected miners ${rejectedMiner.map(r => r.length).join('/')} nodes ${rejectedNode.map(r => r.length).join('/')}; sinks ${sinks.join(' ')} (${checks.sinks_agree ? 'agree' : 'DIFFER'}); counts ${counts.join('/')}; signal lines ${signalLines.filter(Boolean).length}/3 (epochs ${signalEpochs.join('/')}, shares ${signalShares.join('/')}); floor lines ${floorLines.filter(Boolean).length}/3; state roots at the flip ${rootsAtFlip.map(r => String(r).slice(0, 18)).join(' ')}; stateless node accepted after the flip ${statelessAcceptedAfterFlip} (refusal lines ${statelessRefusals}); stale miner accepted after the first refresh ${staleAcceptedAfterRefresh ?? 'n/a'} rejected ${staleRejected ?? 'n/a'}; days ${daysSeen.join('/')}`); +log(`SUMMARY ${pass ? 'PASS' : 'FAIL'} (expect ${EXPECT}, signals ${SIGNAL.join('/')}, stale ${STALE ?? 'none'}): ${firstV4 ? `v5 from epoch ${firstV4.epoch} at DAA ${firstV4.daa}` : 'no v5 epoch'}; epochs seen ${[...epochs.entries()].map(([e, v]) => `e${e}:${v.class}:${v.bps5}bps:d${v.day}`).join(' ')}; chain bytes ${JSON.stringify(versionBytes)} (${signalShareOnChain} bps at byte ${V5_BYTE}+); blocks ${before.length} / ${after.length}; rejected miners ${rejectedMiner.map(r => r.length).join('/')} nodes ${rejectedNode.map(r => r.length).join('/')}; sinks ${sinks.join(' ')} (${checks.sinks_agree ? 'agree' : 'DIFFER'}); counts ${counts.join('/')}; signal lines ${signalLines.filter(Boolean).length}/3 (epochs ${signalEpochs.join('/')}, shares ${signalShares.join('/')}); floor lines ${floorLines.filter(Boolean).length}/3; state roots at the flip ${rootsAtFlip.map(r => String(r).slice(0, 18)).join(' ')}; stateless node accepted after the flip ${statelessAcceptedAfterFlip} (refusal lines ${statelessRefusals}); stale miner accepted after the first refresh ${staleAcceptedAfterRefresh ?? 'n/a'} rejected ${staleRejected ?? 'n/a'}; days ${daysSeen.join('/')}`); for (const r of idRows) log(`PROGRAM ID epoch ${r.epoch} seed ${r.seed}: miners ${r.miners_id} (${r.miners} of 3) cli v5 ${r.cli_v5} cli v4 ${r.cli_v4}; state root ${r.state_root} (${r.state_records} records)`); for (const [k, v] of Object.entries(checks)) if (!v) log(`FAILED CHECK ${k}`); log(`summary: ${TMP}/summary.json`); diff --git a/tools/class-v5/harness-remote.sh b/tools/class-v5/harness-remote.sh index daa378112..0f0d6bd55 100755 --- a/tools/class-v5/harness-remote.sh +++ b/tools/class-v5/harness-remote.sh @@ -7,8 +7,8 @@ # no build slot and no measure hold (the Mac rule's `run` kind). # # tools/class-v5/harness-remote.sh -- -# tools/class-v5/harness-remote.sh failed-case -- --signal 5,5,4 --expect flip -# BASE=29830 tools/class-v5/harness-remote.sh flip-stale -- --signal 5,5,5 --expect flip --stale 2 (another port base per +# tools/class-v5/harness-remote.sh failed-case -- --signal 6,6,5 --expect flip +# BASE=29830 tools/class-v5/harness-remote.sh flip-stale -- --signal 6,6,6 --expect flip --stale 2 (another port base per # case, so two cases never share ports; the harness kills the previous run's pids of its own data dir first) set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"