From cbb9b7a8d1493b754a80725736833b406d9010c6 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:58:08 +0000 Subject: [PATCH 1/3] Rule v4 harness on real nodes: the four runs on bee41b5e into sim/results_v2.md (v3 known-failed, v4 pause with the node fault it read, v4 recovery as designed, split70), and v3.mjs's split50 pass line keyed on the recovery switch (finality-guarantees.md 6.7) Co-Authored-By: Claude Fable 5.1 --- sim/results_v2.md | 13 +++++++++++++ tools/finality-attacks/v3.mjs | 10 +++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/sim/results_v2.md b/sim/results_v2.md index 2fd3b62c7..d09299ac6 100644 --- a/sim/results_v2.md +++ b/sim/results_v2.md @@ -802,3 +802,16 @@ Two rows per share: the equivocator mines on the first side only (as H, I and M5 | checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 8; weights at the cut: window daa 209, voters 6 + +### The harness line on real nodes under rule v4 (igneum-build-2, 18:0x to 18:55 UK, the node lane): the four runs on the release-2.0.0-node pair bee41b5e (the rule v4 node change, `finality_v4_activation_daa` 0 through `IGNEUM_FIN_OVERRIDE_JSON`), the pass line per variant as 6.7 states it + +`tools/finality-attacks/v3.mjs split50` and `split70` on `/srv/artefacts/200-bee41b5e/node-lane`, the same shape as the v3 line above (three igneumd on the 60x file, six voters, one-way delay 300 ms per proxied link, 1 block/s, WARM 230 s, SPLIT 420 s, HEAL 200 s), under the lease pool. The criterion named per row is the one of 6.7: pause-only and v3, no lock on either side during the split, locking resumed after the heal, 0 conflicting certificates, 0 disagreeing locks; recovery, at most one side locks during the split (the side above half of the anchored table by weight), 0 conflicting certificates, 0 disagreeing locks, every node on the recovering side's chain after the heal. The harness's own `[PASS]`/`[FAIL]` print on these runs was the pause line for every split50 run (`v3.mjs` keyed the line on the recovery switch only after them; the commit that carries these rows). + +| run | rule (override) | new locks during the split, n0 (side A) / n1 / n2 (side B) | first new lock, s after the cut | max locked index at the cut / at the end | locking resumed after the heal | conflicting certificates logged | disagreeing locked indices at the end | n0 reconnected, s after the gate reopened | verdict by the 6.7 criterion | +|---|---|---|---|---|---|---|---|---|---| +| v3-known-failed | v3 (`finality_v3_activation_daa` 0) | 6 / 7 / 7 | 258 / 237 / 237 | 7 / 17 | true on every node | 0 / 0 / 0 (build-4's 0.3.25 run logged 4 / 8 / 8: the line depends on which certificate reaches a node first after the reconnect) | 7 | 72 | FAIL (the known-failed line: both sides locked alone past the frozen table's expiry at 240 s, the disagreeing locks are the fault either way) | +| v4-pause | v4, recovery off (`finality_v4_activation_daa` 0, `finality_v4_recovery` false) | 0 / 0 / 0 | none | 7 / 7 | **false on every node** | 0 / 0 / 0 | 0 | 102 | the split half PASSES (no side locks, "paused: held by weight table 7 past its window, a recovery lock needs more than half" and "the pause stands until two thirds sign" on every node); the heal half read a NODE FAULT: 98 s after the reconnect the anchored table read 84.03 to 100.00 percent signing on every node and finality stayed paused, because the node's anchored test refused everything past one window of the lock when the recovery was off (a pause longer than a window could never end, against 6.5). Fixed on release-2.0.0-node (two thirds of the anchored table locks at any time; the majority test only past the window with the recovery on); the rerun with HEAL 400 s below | +| v4-recovery | v4, recovery on (`finality_v4_recovery` true) | 7 / 0 / 0 | 195 / none / none | 7 / 18 | true on every node | 0 / 0 / 0 | 0 | 102 | PASS by the recovery criterion: side A held 51.26 percent of the anchored table frozen at lock 7 (block-count jitter in a 3/3 split: a0 a1 a2 against b0 b1 b2 at equal shares), took the "RECOVERY lock at index 11 (rule v4): 51.26% of the anchored table ... signed, more than half; the table re-anchors here" one full window after lock 7 and locked 12 to 18 on its chain; side B read 48.73 percent and stayed paused ("a recovery lock needs more than half"); on the reconnect n1 and n2 took A's index 11 certificate as the re-anchor and its locks 12 to 18; 0 conflicts, 0 disagreement, all three nodes at 18 | +| v4-recovery-split70 | v4, recovery on, `split70` (p0..p3 at 0.175 on n1 and n2, q0 q1 at 0.15 on n0) | 0 / 9 / 9 | none / 39 / 39 | 6 / 21 | true on every node | 0 / 0 / 0 | 0 | 72 | PASS (the harness's own line too): the 70 percent side locks from 39 s after the cut (above two thirds of the sliding table it fills and above half of the anchored one, no window needed), the 30 percent side never, the heal takes the 30 side onto the 70 side's chain with 0 conflicts | + +Two facts for the reader. The v3 known-failed line's conflicting-certificate count is not the fault's measure (0 here, 4/8/8 on build-4); the disagreeing locked indices after the heal are (7 here, 8 there). The pause line's "resumed" needs a heal window of at least one weight window after the reconnect: with HEAL 200 s and the reconnect at 102 s only 98 s remain, under W = 120 DAA s at 1 block/s, so the fair read is HEAL 400 s (the rerun rows below). diff --git a/tools/finality-attacks/v3.mjs b/tools/finality-attacks/v3.mjs index 93b8db3ea..357b94b69 100644 --- a/tools/finality-attacks/v3.mjs +++ b/tools/finality-attacks/v3.mjs @@ -162,7 +162,15 @@ async function split(kind) { const sideA = newLocks[0], sideB = Math.max(newLocks[1], newLocks[2]); const R = BPS / 2, bound = Math.round(120 / (3 * R)), cliff = Math.round(120 / R); let pass; - if (kind === 'split50') pass = V2 ? true : (sideA === 0 && sideB === 0 && resumed.every(Boolean) && conflicts.every(c => c === 0) && disagree === 0); + // the pass line per variant (finality-guarantees.md 6.7, 8 October 2026): under rule v4 with the recovery a 3/3 split + // is not exactly half of T_f by weight (block-count jitter decides which side holds more than half), so exactly one + // side may lock during the split and every node follows its chain after the heal; under the pause-only variant and + // rule v3 no side locks, and locking resumes after the heal (a heal window of at least one weight window after the + // reconnect is the fair read: HEAL=400 at 1 block/s) + const recovery = (() => { try { return !!JSON.parse(process.env.IGNEUM_FIN_OVERRIDE_JSON || '{}').finality_v4_recovery; } catch { return false; } })(); + if (kind === 'split50') pass = V2 ? true : recovery + ? ((sideA === 0 || sideB === 0) && resumed.every(Boolean) && conflicts.every(c => c === 0) && disagree === 0) + : (sideA === 0 && sideB === 0 && resumed.every(Boolean) && conflicts.every(c => c === 0) && disagree === 0); else pass = sideB > 0 && sideA === 0 && conflicts.every(c => c === 0) && disagree === 0; out(`\n### ${name}: warm ${WARM} s, split ${SPLIT} s, heal window ${HEAL} s, ${BPS} blocks/s in all, delay ${DELAY_MS} ms, rule ${RULE}; old bound W / (3 R) = ${bound} s, frozen table expires ${cliff} s after the last lock (W = 120 DAA, R = ${R} blocks/s per side of a 3/3 split)\n`); out('| measure | n0 (side A) | n1 (side B) | n2 (side B) |'); From 57c9f91b85b9ed5f19e7949e19f786bd3088b902 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:26:24 +0000 Subject: [PATCH 2/3] Rule v4 harness, the reruns on the fixed node (12424341, HEAL 400): the pause-only run resumes after the heal (the node fault closed), the recovery run locks exactly the side above half (54.62 percent this time; 51.26 the other way in the first run), split70 as before; the seven result files in sim/finality-attacks-results/ Co-Authored-By: Claude Fable 5.1 --- sim/finality-attacks-results/v3-known-failed.md | 16 ++++++++++++++++ sim/finality-attacks-results/v4-pause-fix.md | 16 ++++++++++++++++ sim/finality-attacks-results/v4-pause.md | 16 ++++++++++++++++ sim/finality-attacks-results/v4-recovery-fix.md | 16 ++++++++++++++++ .../v4-recovery-split70-fix.md | 16 ++++++++++++++++ .../v4-recovery-split70.md | 16 ++++++++++++++++ sim/finality-attacks-results/v4-recovery.md | 16 ++++++++++++++++ sim/results_v2.md | 12 ++++++++++++ 8 files changed, 124 insertions(+) create mode 100644 sim/finality-attacks-results/v3-known-failed.md create mode 100644 sim/finality-attacks-results/v4-pause-fix.md create mode 100644 sim/finality-attacks-results/v4-pause.md create mode 100644 sim/finality-attacks-results/v4-recovery-fix.md create mode 100644 sim/finality-attacks-results/v4-recovery-split70-fix.md create mode 100644 sim/finality-attacks-results/v4-recovery-split70.md create mode 100644 sim/finality-attacks-results/v4-recovery.md diff --git a/sim/finality-attacks-results/v3-known-failed.md b/sim/finality-attacks-results/v3-known-failed.md new file mode 100644 index 000000000..1b3ad2a18 --- /dev/null +++ b/sim/finality-attacks-results/v3-known-failed.md @@ -0,0 +1,16 @@ + +### split50-v3: warm 230 s, split 420 s, heal window 200 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 7 | 7 | 7 | +| new locks during the split (index above 7) | 6 | 7 | 7 | +| first new lock, s after the cut | 258 | 237 | 237 | +| max locked index at the end of the heal window | 17 | 17 | 17 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 15 | 6 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 72 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 7; weights at the cut: window daa 209, voters 6 + +[FAIL] split50-v3 diff --git a/sim/finality-attacks-results/v4-pause-fix.md b/sim/finality-attacks-results/v4-pause-fix.md new file mode 100644 index 000000000..c7e3e666a --- /dev/null +++ b/sim/finality-attacks-results/v4-pause-fix.md @@ -0,0 +1,16 @@ + +### split50-v3: warm 230 s, split 420 s, heal window 400 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 6 | 6 | 6 | +| new locks during the split (index above 6) | 0 | 0 | 0 | +| first new lock, s after the cut | none | none | none | +| max locked index at the end of the heal window | 25 | 25 | 25 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 179, voters 6 + +[PASS] split50-v3 diff --git a/sim/finality-attacks-results/v4-pause.md b/sim/finality-attacks-results/v4-pause.md new file mode 100644 index 000000000..025c221c8 --- /dev/null +++ b/sim/finality-attacks-results/v4-pause.md @@ -0,0 +1,16 @@ + +### split50-v3: warm 230 s, split 420 s, heal window 200 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 7 | 7 | 7 | +| new locks during the split (index above 7) | 0 | 0 | 0 | +| first new lock, s after the cut | none | none | none | +| max locked index at the end of the heal window | 7 | 7 | 7 | +| locking resumed after the heal | false | false | false | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 209, voters 6 + +[FAIL] split50-v3 diff --git a/sim/finality-attacks-results/v4-recovery-fix.md b/sim/finality-attacks-results/v4-recovery-fix.md new file mode 100644 index 000000000..9024d229b --- /dev/null +++ b/sim/finality-attacks-results/v4-recovery-fix.md @@ -0,0 +1,16 @@ + +### split50-v3: warm 230 s, split 420 s, heal window 400 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 5 | 5 | 5 | +| new locks during the split (index above 5) | 0 | 8 | 8 | +| first new lock, s after the cut | none | 249 | 249 | +| max locked index at the end of the heal window | 24 | 24 | 24 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 72 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 150, voters 6 + +[FAIL] split50-v3 diff --git a/sim/finality-attacks-results/v4-recovery-split70-fix.md b/sim/finality-attacks-results/v4-recovery-split70-fix.md new file mode 100644 index 000000000..2e5070970 --- /dev/null +++ b/sim/finality-attacks-results/v4-recovery-split70-fix.md @@ -0,0 +1,16 @@ + +### split70-v3: warm 230 s, split 420 s, heal window 400 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 7 | 7 | 7 | +| new locks during the split (index above 7) | 0 | 9 | 9 | +| first new lock, s after the cut | none | 24 | 24 | +| max locked index at the end of the heal window | 28 | 28 | 28 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 209, voters 6 + +[PASS] split70-v3 diff --git a/sim/finality-attacks-results/v4-recovery-split70.md b/sim/finality-attacks-results/v4-recovery-split70.md new file mode 100644 index 000000000..a7490411e --- /dev/null +++ b/sim/finality-attacks-results/v4-recovery-split70.md @@ -0,0 +1,16 @@ + +### split70-v3: warm 230 s, split 420 s, heal window 200 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 6 | 6 | 6 | +| new locks during the split (index above 6) | 0 | 9 | 9 | +| first new lock, s after the cut | none | 39 | 39 | +| max locked index at the end of the heal window | 21 | 21 | 21 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 72 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 179, voters 6 + +[PASS] split70-v3 diff --git a/sim/finality-attacks-results/v4-recovery.md b/sim/finality-attacks-results/v4-recovery.md new file mode 100644 index 000000000..357f9cf9d --- /dev/null +++ b/sim/finality-attacks-results/v4-recovery.md @@ -0,0 +1,16 @@ + +### split50-v3: warm 230 s, split 420 s, heal window 200 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split) + +| measure | n0 (side A) | n1 (side B) | n2 (side B) | +|---|---|---|---| +| max locked index at the cut | 7 | 7 | 7 | +| new locks during the split (index above 7) | 7 | 0 | 0 | +| first new lock, s after the cut | 195 | none | none | +| max locked index at the end of the heal window | 18 | 18 | 18 | +| locking resumed after the heal | true | true | true | +| conflicting certificates logged | 0 | 0 | 0 | +| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 | + +n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 0; weights at the cut: window daa 209, voters 6 + +[FAIL] split50-v3 diff --git a/sim/results_v2.md b/sim/results_v2.md index d09299ac6..256b24024 100644 --- a/sim/results_v2.md +++ b/sim/results_v2.md @@ -815,3 +815,15 @@ n0 reconnected 102 s after the gate reopened; locked indices disagreeing across | v4-recovery-split70 | v4, recovery on, `split70` (p0..p3 at 0.175 on n1 and n2, q0 q1 at 0.15 on n0) | 0 / 9 / 9 | none / 39 / 39 | 6 / 21 | true on every node | 0 / 0 / 0 | 0 | 72 | PASS (the harness's own line too): the 70 percent side locks from 39 s after the cut (above two thirds of the sliding table it fills and above half of the anchored one, no window needed), the 30 percent side never, the heal takes the 30 side onto the 70 side's chain with 0 conflicts | Two facts for the reader. The v3 known-failed line's conflicting-certificate count is not the fault's measure (0 here, 4/8/8 on build-4); the disagreeing locked indices after the heal are (7 here, 8 there). The pause line's "resumed" needs a heal window of at least one weight window after the reconnect: with HEAL 200 s and the reconnect at 102 s only 98 s remain, under W = 120 DAA s at 1 block/s, so the fair read is HEAL 400 s (the rerun rows below). + +### The reruns on the fixed node (igneum-build-2, 19:32 to 20:25 UK): the three rule v4 runs on the 2.0.1 successor pair 12424341 (the pause fix 6872db13: two thirds of the anchored table locks at any time, the majority test only past the window with the recovery on), HEAL 400 s + +The same harness and shape, the heal window at 400 s so "resumed" has a full weight window after the reconnect (W = 120 DAA s at 1 block/s; the reconnect came 72 to 102 s after the gate reopened). The result files are in `sim/finality-attacks-results/` (the seven runs of tonight, the harness's own markdown). The harness's `[PASS]`/`[FAIL]` print on the two split50 runs is the copy on build-2 from before the criterion change; the line in `tools/finality-attacks/v3.mjs` is keyed on the recovery switch from this commit. + +| run | rule (override) | new locks during the split, n0 (side A) / n1 / n2 (side B) | first new lock, s after the cut | max locked index at the cut / at the end | locking resumed after the heal | conflicting certificates | disagreeing locked indices at the end | n0 reconnected, s after the gate reopened | verdict by the 6.7 criterion | +|---|---|---|---|---|---|---|---|---|---| +| v4-pause-fix | v4, recovery off | 0 / 0 / 0 | none | 6 / 25 | **true on every node** (lock 7 at the reconnect, 22 to 25 by the window's end at 68 to 72 percent of active) | 0 / 0 / 0 | 0 | 102 | PASS: the node fault of the first run is closed; no side locks during the split and the pause ends when two thirds of the anchored table sign again (6.5's "lasts until it signs again") | +| v4-recovery-fix | v4, recovery on | 0 / 8 / 8 | none / 249 / 249 | 5 / 24 | true on every node | 0 / 0 / 0 | 0 | 72 | PASS by the recovery criterion: this time side B held 54.62 percent of the table frozen at lock 5 and took the "RECOVERY lock at index 10" 249 s after the cut, side A paused at 31 to 45 percent ("a recovery lock needs more than half"); n0 took B's chain and certificate at the heal, all three at 24. With the first run (side A at 51.26 percent) the jitter decided the side both ways: the fact for section 7 item 4 | +| v4-recovery-split70-fix | v4, recovery on, split70 | 0 / 9 / 9 | none / 24 / 24 | 7 / 28 | true on every node | 0 / 0 / 0 | 0 | 102 | PASS (the harness's own line too): the 70 percent side locks from 24 s after the cut, the 30 side never, the heal takes it onto the 70 side's chain | + +Summary of the seven runs: rule v3 fails its known-failed line as before (both sides lock alone, disagreeing locks after the heal); rule v4 without the recovery never locks during a split and resumes after the heal once the fixed node is used; rule v4 with the recovery lets exactly the side above half of the anchored table lock after a full window and brings the other side onto that chain at the heal; no run under rule v4 logged a conflicting certificate or ended with a disagreeing lock. From 75e91cc4d4fec83f3512c677913e58d74c6c7bfa Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:28:51 +0000 Subject: [PATCH 3/3] Registry: the node lane's rule v4 real-node run (fin-v4-realnode-20261008T2025Z on 12424341) onto FIN-02 and FIN-07 through test-record.mjs; the evidence under sim/finality-attacks-results/ Co-Authored-By: Claude Fable 5.1 --- docs/plans/igneum-2.0-test-registry.json | 194 +++++++++--------- .../fin-v4-realnode-20261008T2025Z.json | 16 ++ 2 files changed, 111 insertions(+), 99 deletions(-) create mode 100644 tools/ci/batches/fin-v4-realnode-20261008T2025Z.json diff --git a/docs/plans/igneum-2.0-test-registry.json b/docs/plans/igneum-2.0-test-registry.json index f57d2b203..b85fab9fa 100644 --- a/docs/plans/igneum-2.0-test-registry.json +++ b/docs/plans/igneum-2.0-test-registry.json @@ -165,10 +165,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the evidence vault F9 (raw and negative evidence preserved) is the gate rule landing by 21:00: a PASS must carry its evidence file", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -321,10 +321,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the stale-evidence rule (evidence older than the manifest sha reads NOT RUN) is the gate rule landing by 21:00", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] @@ -587,10 +587,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "accepted work under ordinary connectivity needs the fault network F4", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -1466,10 +1466,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "seed-selection resistance is the census harness (the class v6 invention lane), not yet in the matrix", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -1624,10 +1624,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the no-new-rules counterfactual is a research harness", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] @@ -2272,10 +2272,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "execution DoS workloads need the workload catalogue F3", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2749,10 +2749,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the consumer-shard reproduction is the fleet lane's pods", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2784,10 +2784,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "proving on the mining configuration is the fleet lane's", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2819,10 +2819,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the request-to-payment path is the proving fleet's measurement", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2854,10 +2854,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "sustained load is the proving fleet's", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2889,10 +2889,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "overload and recovery is the proving fleet's", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2924,10 +2924,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "assignment windows are the proving fleet's", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2959,10 +2959,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "reassignment is the proving fleet's", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -2994,10 +2994,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "customer-verifiable output is the reference apps plus the fleet", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] @@ -3092,10 +3092,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "revenue separation is the economics lane's model", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3129,10 +3129,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the modified-client task choice needs an adversarial client harness", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3166,10 +3166,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "demand spikes are the economic model", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3203,10 +3203,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "reservation abuse needs the capacity harness", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3240,10 +3240,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "difficulty and timestamp manipulation needs the fault network F4", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3277,10 +3277,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "self-dealing fees need the economic model and a live window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3314,10 +3314,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "failure concentration is a live-window measurement", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] @@ -3408,16 +3408,17 @@ "manual_page": 48, "owner_lane": "finality lane (aca0f5ed924a2a99b)", "run_status": "PASS", - "evidence_path": "sim/results_v2.md", - "run_id": "fin-boundary-20261008-02", - "updated": "2026-10-08T18:59:16.459Z", + "evidence_path": "sim/finality-attacks-results/v4-pause-fix.md", + "run_id": "fin-v4-realnode-20261008T2025Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { - "cell": "harness:finality-sim-fin02", - "manifest_sha": "4b423dad8", + "cell": "node:finality-realnode", + "manifest_sha": "12424341", "coverage": { - "FIN-02": "the simulator half of the accept text; the real-node run on the fault network F4 with independent operators is node:finality-realnode" + "FIN-02": "partial: the real-node half; independent operators and the review remain", + "FIN-07": "partial: deterministic recovery after reconnection and crash on real nodes (the HEAL reruns)" }, - "at": "2026-10-08T18:59:16.459Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3618,23 +3619,18 @@ "owner": "Consensus lead + independent formal/security review", "manual_page": 50, "owner_lane": "finality lane (aca0f5ed924a2a99b)", - "run_status": "RUNNING", - "evidence_path": "build-1:/srv/artefacts/tas/201-ef0f2ed8-2826f37e/node-ef0f2ed8/box4-consensus.log", - "run_id": "201-ef0f2ed8-2826f37e", - "updated": "2026-10-08T18:51:08.658Z", + "run_status": "PASS", + "evidence_path": "sim/finality-attacks-results/v4-pause-fix.md", + "run_id": "fin-v4-realnode-20261008T2025Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { - "cell": "suite:consensus", - "manifest_sha": "ef0f2ed8", + "cell": "node:finality-realnode", + "manifest_sha": "12424341", "coverage": { - "FIN-01": "partial: the finality processes' agreement tests", - "FIN-03": "partial: rule v4's anchored table and authority expiry tests (bee41b5e)", - "FIN-04": "partial: the frozen table without its time expiry (rule v4)", - "FIN-05": "partial: key succession and another scheme's vote refused", - "FIN-07": "partial: majority-continuity recovery (rule v4)", - "ROT-05": "partial: the finality-stopped pause tests", - "ZKP-01": "partial: a proof-less or wrong-statement block refused by consensus" + "FIN-02": "partial: the real-node half; independent operators and the review remain", + "FIN-07": "partial: deterministic recovery after reconnection and crash on real nodes (the HEAL reruns)" }, - "at": "2026-10-08T18:51:08.658Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -3974,10 +3970,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "wallet key protection is the wallet lane's security row", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4068,10 +4064,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "the no-founder exercise is an operations run, not a suite", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4103,10 +4099,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "bootstrap diversity needs the fault network F4", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4217,10 +4213,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "proving workload isolation is the fleet's pod row", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4292,10 +4288,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "runbook detection is an operations run", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4327,10 +4323,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "repeat independent operation is a cross-release observation", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] @@ -4723,10 +4719,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence, no automated harness", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4763,10 +4759,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4803,10 +4799,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4843,10 +4839,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4883,10 +4879,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -4923,10 +4919,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "commercial evidence", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5086,10 +5082,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "leadership comparison, an observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5124,10 +5120,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5162,10 +5158,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5200,10 +5196,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5238,10 +5234,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } }, { @@ -5310,10 +5306,10 @@ "run_status": "NOT RUN", "evidence_path": "", "run_id": "", - "updated": "2026-10-08T19:22:31.798Z", + "updated": "2026-10-08T19:28:46.814Z", "evidence_record": { "reason": "observation window", - "at": "2026-10-08T19:22:31.798Z" + "at": "2026-10-08T19:28:46.814Z" } } ] diff --git a/tools/ci/batches/fin-v4-realnode-20261008T2025Z.json b/tools/ci/batches/fin-v4-realnode-20261008T2025Z.json new file mode 100644 index 000000000..0f53a9f1e --- /dev/null +++ b/tools/ci/batches/fin-v4-realnode-20261008T2025Z.json @@ -0,0 +1,16 @@ +{ + "run_id": "fin-v4-realnode-20261008T2025Z", + "manifest_sha": "12424341", + "evidence_dir": "sim/finality-attacks-results", + "boxes": [ + "build-2" + ], + "cells": [ + { + "cell": "node:finality-realnode", + "status": "PASS", + "evidence": "sim/finality-attacks-results/v4-pause-fix.md", + "note": "the node lane's rule v4 harness on real nodes (tools/finality-attacks/v3.mjs split50 and split70, three igneumd on the 60x file, six voters, WARM 230 SPLIT 420 HEAL 400 BPS 1): the pause-only run on the fixed pair 12424341 (the pause fix 6872db13) locks nothing during the split and resumes after the heal (lock 7 at the reconnect, 25 by the window's end), 0 conflicting certificates, 0 disagreeing locks; the recovery runs lock exactly the side above half of the anchored table (54.62 percent this time, 51.26 the other way on bee41b5e) with every node on that chain after the heal; split70 as before; rule v3's known-failed line fails as it must (both sides lock alone, 7 disagreeing locks). The first v4-pause run on bee41b5e read the node fault (a permanent pause past one window with the recovery off) that the fix closed. Rows: sim/results_v2.md, Rule v4, the two real-node tables; the seven result files beside this evidence path." + } + ] +}