From 4967dae3b1d95c095025b006a8a4b0941ebcbc12 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 01:17:45 +0000 Subject: [PATCH] FUD ledger sweep, round 5: s5 and s4 on the finality-fixes build (F1 burster locks nothing alone, F3 vote-dropping adds 0 ms), F2 floor note, final counts Co-Authored-By: Claude Fable 5.1 --- docs/fud-ledger.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/fud-ledger.md b/docs/fud-ledger.md index cb2c72582..d18744201 100644 --- a/docs/fud-ledger.md +++ b/docs/fud-ledger.md @@ -152,7 +152,7 @@ Evidence: `docs/bench-log.md`, two-card table. ### F1. Finality is attackable for the first month "Vote weight is 30 days of blocks. At genesis there are zero days. For the first weeks weight equals hashrate share, so anyone with two thirds of a tiny launch hashrate locks checkpoints alone. You say 'no certificate in the first hour'. An hour." -Status: Rule implemented and measured; launch month simulated (5 October 2026 sweep). Rule: `min_daa = weight_window` (branch `fin-fixes`, 4 October 2026, merged into `devnet-v4`; 2,592,000 DAA s on mainnet, 7,200 on devnet, a unit test pins the equality); `evaluate` never locks and `ingest_certificate` refuses any certificate while the checkpoint's DAA score is under it; the node reports "window filling, N of M". Measured on the fork: harness s5 before the fix locked checkpoints 1 to 10 by one 20-s burster alone; after, 0 locks under `min_daa` and the first lock by 5 of 6 voters at 84.7% (`docs/bench-log.md`, "finality fixes F17 and F1"); the live devnet's first lock came at checkpoint 242, two hours after genesis, once the 7,200 window was full. Launch month under the gate (arithmetic, `docs/review/ledger-sweep-2026-10-05.md`): no certificate exists before day 30, and on day 30 an attacker producing share s of all blocks from day k holds s (31 - k) / 30 of the window, so the critic's 75% from day 2 holds 72.5% and would lock alone on day 30, 51% from day 1 holds 51% and cannot, and the share needed to lock alone on day 30 is 69% from day 2 and 95% from day 10; without the gate the old active-weight denominator crossed two thirds on day 9. The gate therefore turns the first month into the standing bound (an attacker over two thirds of all hashrate, which no proof-of-work rule resists) and nothing weaker. The v1 model's zero-history ramp (`sim/finality_sim.py --scenarios A`) is re-run in the sweep's batch. The litepaper statement is public text (testnet-prep). Was: Conceded, not yet stated in the litepaper. Zero-history ramp of the v1 model (`python3 sim/finality_sim.py --scenarios A --floors 1,100`, 5 October 2026): the network's total weight first reaches 99% of a full window on day 41 (floor 1) or day 35 (floor 100), so the gate's day 30 is the day the window is full by construction and the earliest a lock is possible. +Status: Rule implemented and measured; launch month simulated (5 October 2026 sweep). Rule: `min_daa = weight_window` (branch `fin-fixes`, 4 October 2026, merged into `devnet-v4`; 2,592,000 DAA s on mainnet, 7,200 on devnet, a unit test pins the equality); `evaluate` never locks and `ingest_certificate` refuses any certificate while the checkpoint's DAA score is under it; the node reports "window filling, N of M". Measured on the fork: harness s5 before the fix locked checkpoints 1 to 10 by one 20-s burster alone; after, 0 locks under `min_daa` and the first lock by 5 of 6 voters at 84.7% (`docs/bench-log.md`, "finality fixes F17 and F1"); the live devnet's first lock came at checkpoint 242, two hours after genesis, once the 7,200 window was full. Launch month under the gate (arithmetic, `docs/review/ledger-sweep-2026-10-05.md`): no certificate exists before day 30, and on day 30 an attacker producing share s of all blocks from day k holds s (31 - k) / 30 of the window, so the critic's 75% from day 2 holds 72.5% and would lock alone on day 30, 51% from day 1 holds 51% and cannot, and the share needed to lock alone on day 30 is 69% from day 2 and 95% from day 10; without the gate the old active-weight denominator crossed two thirds on day 9. The gate therefore turns the first month into the standing bound (an attacker over two thirds of all hashrate, which no proof-of-work rule resists) and nothing weaker. The v1 model's zero-history ramp (`sim/finality_sim.py --scenarios A`) is re-run in the sweep's batch. The litepaper statement is public text (testnet-prep). Was: Conceded, not yet stated in the litepaper. Zero-history ramp of the v1 model (`python3 sim/finality_sim.py --scenarios A --floors 1,100`, 5 October 2026): the network's total weight first reaches 99% of a full window on day 41 (floor 1) or day 35 (floor 100), so the gate's day 30 is the day the window is full by construction and the earliest a lock is possible. Live confirmation on the current node line (5 October 2026, 01:11 UTC, `tools/finality-attacks/run.mjs s5 --fast-time` on the `finality-fixes` build, ports 29300 to 29302, `min_daa` = window = 120 DAA, 126 s): the 10x burster's weight share was 27.5% against a block share of 31.8% (ratio 0.864, no amplification), it stayed under the floor and locked nothing alone, 0 locks carried by fewer than 2 votes, 0 conflicting certificates. PASS; the harness's own result text still names the 56.7% floor and needs the 2/3 wording. Answer: Correct, and this is the most dangerous entry in the ledger. With the active-weight denominator and a one-day honest head start, an attacker producing 75% of blocks from day 2 holds 0.75k/(1+k) of weight after k days and crosses two thirds on day 9 of the chain's life; an attacker arriving in hour two crosses it the same day. The window is empty, so the defence is absent. The 30-day emission ramp (10% to 100%) lowers the incentive without removing the attack. The design doc's answer, the one-hour merge-depth bound protecting the first month, bounds the damage of a reorg and does nothing about a bad lock. The rule under consideration: no certificate may form until the window has 30 days of history, so the chain runs plain GHOSTDAG under the one-hour merge-depth bound for its first month, exchanges are told to treat it so, and listings follow launch in any case. This goes to the gate 3 simulation and the litepaper will state it either way. @@ -161,7 +161,7 @@ Evidence: arithmetic above (not yet in `sim/`); design doc, hostile review table ### F2. The two-hour presence window is an eclipse vector "Cut the big pools' vote gossip for two hours, not their blocks, and the remaining keys become 100% of active weight. A faction with a fifth of the weight locks alone. You chose liveness over safety and called it a feature." -Status: Closed by rule (3 October 2026). Spec section 3.3.2: the 56.7%-of-total floor in Q3 is the answer; 0 conflicting locks at 1, 2 and 4 h against a 34% attacker (`sim/results_v2.md` F2); the devnet eclipse of O-3.7 is confirmation only. Was: Open, experiment scheduled. +Status: Closed by rule (3 October 2026). Spec section 3.3.2: the 56.7%-of-total floor in Q3 is the answer; 0 conflicting locks at 1, 2 and 4 h against a 34% attacker (`sim/results_v2.md` F2); the devnet eclipse of O-3.7 is confirmation only. Was: Open, experiment scheduled. Sweep (5 October 2026): the floor named here (56.7% of total) was raised to two thirds of total on 4 October 2026 (O-3.15, spec 3.3 Q3); the eclipse scenario was re-run at the new floor (`sim/results_v2.md` L3 and F at 2/3: 0 conflicting locks and 0 locks on the eclipsed side in every seed and length), so the closure stands at the higher floor. Answer: Correct that the presence window trades safety for liveness. The design doc says so: "any event that keeps honest keys from signing (eclipse, partition, targeted DoS) shrinks the denominator and lets a smaller faction lock," and the simulation recommends the fail-safe all-keys denominator while the design chose the presence window as the working default. The mitigation in the rule is that a lock requires the certificate's unscaled weight to reach two thirds of active weight, so an eclipsed set still has to be out for most of the 240 checkpoints before the denominator moves far, and votes travel in blocks as well as as their own messages. That is an argument, not a measurement. The gate 3 experiment is a devnet with regional latency and a single-node eclipse recording whether conflicting locks appear, plus the choice between a 2-hour and a 7-day silent-key rule. Until it runs, this entry stays open. @@ -170,7 +170,7 @@ Evidence: `sim/results.md` table E and "What the simulation cannot tell us"; des ### F3. Participation grinding through the bitmap "The aggregator picks which votes go in the certificate once quorum is reached. A block producer carries the certificate it likes. Drop a rival pool's votes from your bitmaps for two hours and its participation factor falls, your share rises." -Status: Closed by rule (3 October 2026), parameter open. Spec section 3.3 Q2 and 3.4.1: participation is computed from every vote seen in blocks over the presence window (votes are block payload), never from the certificates an aggregator chose; the per-block vote bound is O-3.3. Was: Open, experiment scheduled. Sweep (5 October 2026): the block reading is exercised on the fork by harness s3 (block-carried votes give participation; 35 of 35 locked on each of 3 nodes) and s4 (a producer dropping the finality section from 40% of its blocks adds 0 ms to lock latency, 35 locks), bench-log "finality v2 attack harness"; the per-block vote bound and the hostile-aggregator simulation of O-3.3 are still owed. +Status: Closed by rule (3 October 2026), parameter open. Spec section 3.3 Q2 and 3.4.1: participation is computed from every vote seen in blocks over the presence window (votes are block payload), never from the certificates an aggregator chose; the per-block vote bound is O-3.3. Was: Open, experiment scheduled. Sweep (5 October 2026): the block reading is exercised on the fork by harness s3 (block-carried votes give participation; 35 of 35 locked on each of 3 nodes) and s4 (a producer dropping the finality section from 40% of its blocks adds 0 ms to lock latency, 35 locks), bench-log "finality v2 attack harness"; the per-block vote bound and the hostile-aggregator simulation of O-3.3 are still owed. Re-run on the `finality-fixes` build under fast time (5 October 2026, 01:13 UTC, `run.mjs s4 --fast-time`, 2 nodes, 223 s): node B locked 14 checkpoints with a 40% vote-dropping producer, median lock latency 1,042 ms against 1,042 ms for the control, 0 ms added. PASS. Answer: This is a new finding from writing this ledger and it is not in the design doc. The participation factor is "observed certified votes over P divided by expected votes". If "observed" means votes present in certificates carried in blocks, then whoever aggregates and whoever produces blocks can shape it. Candidate fixes: participation computed from all votes seen in blocks (votes as transactions, not only certificates), or a certificate must include every valid vote the aggregator received above a size bound, or participation falls only on checkpoints where the key's vote appears in no certificate and no block. Goes to the cryptographer for phase 1 and to the gate 3 simulation. @@ -1127,7 +1127,7 @@ Evidence: spec 2.1; `docs/design/execution-layer.md` 5.4. Review id R3.4. ### F14. Weight in blocks over a window in blocks under a lagging retarget "Your day counts assume the block supply is capped at one a second. It is not during a retarget lag, and weight is counted in blocks." -Status: Rule written (3 October 2026): spec 3.1 W2 and 3.3 Q1 are denominated in past-median time, weight as a key's share of each 60-s bucket summed over 30 days; the simulated DAA-score form is kept beside it. O-3.14 (added the same evening) confirms or reverts it at gate 3. Was: Open, rule change proposed. Sweep (5 October 2026): the simulated DAA-score form shows no amplification under either controller (`sim/difficulty/attacks` scenario 2: weight per hash 0.26 under Igneum, 0.98 under Kaspa; harness s5 on the fork: 0.999), so the median-time form is defence in depth whose own run, both forms with the DAA in the loop (O-3.14), is still owed. +Status: Rule written (3 October 2026): spec 3.1 W2 and 3.3 Q1 are denominated in past-median time, weight as a key's share of each 60-s bucket summed over 30 days; the simulated DAA-score form is kept beside it. O-3.14 (added the same evening) confirms or reverts it at gate 3. Was: Open, rule change proposed. Sweep (5 October 2026): the simulated DAA-score form shows no amplification under either controller (`sim/difficulty/attacks` scenario 2: weight per hash 0.26 under Igneum, 0.98 under Kaspa; harness s5 on the fork: 0.999), so the median-time form is defence in depth whose own run, both forms with the DAA in the loop (O-3.14), is still owed. On the current node line (s5 under fast time, 5 October 2026): weight share over block share 0.864, below 1, so the window bought the pulse nothing. Answer: Correct; this is the finality half of M14. Spec W2 counts blue blocks over a window of 2,592,000 DAA seconds, and DAA seconds are blocks, so a burst both inflates a key's count and ages the window. Proposed change: denominate the vote window (W2) and the presence window (Q1) in past-median time, and define weight as a key's share of the blue blocks in each 60-second median-time bucket summed over the trailing 30 days, so 50x the blocks in one minute is one minute of weight. The simulation of M14 decides. @@ -1839,8 +1839,8 @@ Evidence: the commits above. Experiment: `curl https://igneum.network/api/live` `docs/review/ledger-sweep-2026-10-05.md` holds the runs, the commands and the running table. Every Open, Proposed, Unmeasured or pending entry was read against its experiment line; the status lines above carry the evidence inline, marked "Sweep (5 October 2026)" where a note was added and "Was:" where the status changed. Items owned by the other two night branches (F23, F24, G12, X18, the flood memory growth; the base-fee floor, testnet parameters, G13, G14, public text) were left to them. - **Moved to Fixed or Rolled out, from evidence that already existed and had not reached the ledger:** M15 (cheap checks before the PoW engine, merged and live), M17 (hot swap, measured on the live devnet across three vendors), M19 (the census cells filled, 16 loads a Definition), M24 (rule v2 activated at DAA 33,000), P20 (the buffered save confirmed on the third run), X16 (the evidence page exists), G11 (the spec is public). -- **Moved to Answered with evidence:** F1 (the first-month gate implemented and measured; the launch month is arithmetic now), F7 (reorg depth p50 1, p99 3, max 5 on a 12-node, 5-region network), F19 (bought keys are worth their blocks; scenario K at both floors), E12 (the simulation half), E15 (the security-budget model: the floor is crossed in year 7, 11 or never by price, and no fee level moves it). +- **Moved to Answered with evidence:** F1 (the first-month gate implemented, measured on 4 October and re-confirmed tonight on the finality-fixes build: the burster locks nothing alone; the launch month is arithmetic now), F7 (reorg depth p50 1, p99 3, max 5 on a 12-node, 5-region network), F19 (bought keys are worth their blocks; scenario K at both floors), E12 (the simulation half), E15 (the security-budget model: the floor is crossed in year 7, 11 or never by price, and no fee level moves it). - **Fix built on a branch, pending merge or rollout:** M26, M27, X21 (`miner-reliability` `aea5ac6d`, with the app half on `release-0.3.5`), F22 (rule v3, fast-time network), part of X22. -- **Open with new evidence and a fix row (fud-fixes section 2.5):** M20 (the stub is still in the pruning-proof path on `devnet-v4`; it goes live the moment the devnet passes its pruning depth), M21 (the k table from the fork's own function), M28, X20, P15, X29 (half fixed: file modes), X14 (hashing concentration measured), M25 (confirmed live in the sweep: a mismatched day length is rejected as `BlockInvalid` with no reason named, 0 of 4 against 7 of 7), M14 and F14 (no amplification in the chain model under rule v2 or Kaspa's rule; the finality-with-DAA run still owed). +- **Open with new evidence and a fix row (fud-fixes section 2.5):** M20 (the stub is still in the pruning-proof path on `devnet-v4`; it goes live the moment the devnet passes its pruning depth), M21 (the k table from the fork's own function), M28, X20, P15, X29 (half fixed: file modes), X14 (hashing concentration measured), M25 (confirmed live in the sweep: a mismatched day length is rejected as `BlockInvalid` with no reason named, 0 of 4 against 7 of 7), M14 and F14 (no amplification in the chain model under rule v2 or Kaspa's rule, nor on the finality-fixes node under fast time, s5 ratio 0.864; the finality-with-DAA run still owed). - **Open, needs hardware, a person or the devnet:** M1, M11 (a rig), M16 (the 5090), M22, X19 (the node line; `faketime`), X23 to X28 (the relay owner and PC 1; the token is rotated, the run-task binding is not), E13, E14, E16, E17 (the draw lines), F16, F20 (the devnet test), C4 (module off), P3, P14, P16, P17, P21, P22, X13, X15, X17, G10, L1 to L5, L8, D5, D6. - **Nothing became worse.** Two things are closer than they look: M20 becomes a live failure for every fresh node once the devnet's pruning point leaves genesis, which at the devnet's 1.05 DAA/s (DAA 33,000 at 17:37 UTC on 4 October) is between DAA 108,000 (`PRUNING_DURATION`, about 14:00 UTC on 5 October) and DAA 151,200 (the first finality point a full pruning depth below the tip, about 01:00 UTC on 6 October), approximate; X23's operational half (a run task on the PCs needs only the relay token) is unchanged after the rotation.