igneum/docs/analysis/horizon/consensus-security.md
igneum-labs 7355d53fde Pre-public scrub, the text pass (7 October 2026, 19:5x UK): no founder name, personal login, earlier business or personal address in any tracked text file, and a gate check that keeps it so
The sweep (main's item 1): 199 tracked text files, 783 lines. The founder's full name, first name and possessive become "the founder" (sentence starts capitalised); the lowercase operating-system user name in WSL paths and commands becomes <user>; the second owner login becomes "the second owner login"; the three earlier businesses and the two other brands become "the other business", "the earlier entity", "the earlier business" and "another brand"; the Chrome profile rule names the igneum.network profile, not the profile's label. The standing commit login igneum-labs is not a founder term here: the fresh-repository step renames it in the history (docs/plans/history-rewrite.md, tools/repo/fresh-repo.sh).

The patterns never appear in plain text in the tree (a plaintext list would be the hit): tools/ci/founder-strings.b64 (perl regex, tab, a sample per row) is read by tools/ci/founder-strings-check.sh (every tracked text file, perl, known-failed first: the self-test plants each row's sample in a fixture and the hit must name the file), by tools/community/discord-hooks.mjs (the guard's founder and business rows; the test takes its fixtures from the samples) and by tools/repo/fresh-repo.sh (the business names of the rewrite rules). site/forbidden-strings.txt carries the same patterns as b64: lines, decoded case-insensitive by site/scrub.mjs and tools/ci/launch-gates-check.mjs (whose fixture now plants an encoded made-up name). The check runs in the gate's tree checks on every merge.

Not in this commit, by main's word: the 105 commit messages and 40 personal-identity commits that need the history rewrite (listed, not run), and the secrets found by gitleaks over the history (reported with owners).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 18:39:50 +00:00

62 KiB

Horizon lane 1: consensus security. What a hash majority buys on Igneum, attack by attack, with the bound and the price

Date: 6 October 2026, evening UK (written 19:40 to 21:30 UTC). Lane: consensus-security. Worktree: /Users/joshm/Projects/igneum-wt-horizon (branch horizon at 3f4f719). Companion paper: docs/analysis/51-percent.md. Models and runs: sim/horizon/consensus-security/ (README at the end of this file, section 9).

What was read before modelling: docs/spec/02-consensus.md, 03-finality.md, 04-seeds-and-vdf.md, 06-open-items.md, 07-execution.md, 08-client-security.md; docs/fud-ledger.md F1 to F25, M14, M15, M23, M24, P7, P9, P11, P12, X18 to X20, G8, G12, G13, C4, D6, E16; docs/analysis/difficulty-2026-10-03.md (section 11), difficulty-2026-10-04-oscillation.md, sim/difficulty/attacks/README.md (the seven attacked ways); sim/results_v2.md A to M; docs/benchmarks/finality-v3-2026-10-04/*.md, docs/benchmarks/round4-consensus-2026-10-04/results-final2.md; tools/finality-attacks/README.md and lib/net.mjs, tools/harness/README.md and scenarios, tools/exec-attacks/README.md; docs/review/redteam-2026-10-04.md, docs/review/round-4-reddit-2026-10-06.md; docs/plans/counter-asic-3-node.md section 6 (P2); docs/analysis/security-budget.md; docs/bench-log.md "Rental cost of hash, 6 October 2026"; vendor/igneum-node/consensus/src/processes/ghostdag/protocol.rs (main checkout); infra/fast-time/README.md and override-60x.json; CLAUDE.md's 6 October rules. Two facts from main during the lane (19:4xZ, the first later corrected): the live devnet's finality has been paused since 18:39:40Z (lane 3 confirmed the cause at c3aa502: a 42.7 percent departure held by the frozen table, not the hub outage); and run A on igneum-devnet-2 at 10 blocks/s in a star of 41 miners through one seed gave 12.4 blocks/s, 77 percent red blocks, 321 tips, max reorg 55, with the controller lowering difficulty on the low blue rate (cite as "main, 6 Oct 2026 19:4xZ, block-rate-devnet2.md run A" until the file carries the rows).

Price basis for every cost: USD 11.7 per GH/s-hour, measured on RunPod community pods on 6 October 2026 (docs/bench-log.md, "Rental cost of hash": 1,748 MH/s for USD 20.44 an hour; approximate above 2 GH/s because the market supplied no more; the live devnet was 1.16 GH/s). Costs are given per network size at 1, 10, 100 GH/s and 1 TH/s. Writing rules: no em dashes, numbers in tables, every figure labelled measured, simulated, cited or approximate.

1. Summary for the coordinator

A hash majority on Igneum buys the ordering race inside the lock latency and nothing a certificate covers, and that is measured here, not asserted. Three findings lead.

  1. The lock does the work the k-cluster cannot. The DAG simulator (ghostdag_sim.py, GHOSTDAG as protocol.rs runs it) shows a 45 to 51 percent withholder wins the selected-chain race over a 90-second hold 70 to 85 percent of the time, reorganising 32 to 46 chain blocks at 1 block/s; a 34 percent withholder wins only inside 30 to 60 s (40 percent of attempts) and never at 90 s; a 20 percent one only the last k = 18 blocks (5 to 15 percent of attempts). Finality's lock lands about 63 to 93 s after a checkpoint block (spec 03 C1, 3.11.3), so the window a majority can reorder is the lock latency, measured at 90 to 120 s, not a block count. Below two thirds of weight, no hash share reaches past a certificate (spec 03 3.11.2, sim/results_v2.md H, I, L3, M5: 0 conflicts under 1/3 in every seed).
  2. The pause is the residual, and it is cheap to buy and free to hold. Reaching the veto (1/3 of 30-day weight) at 51 percent of blocks takes 20 days and costs USD 6k at 1 GH/s, USD 5.8M at 1 TH/s in rent (cost_model.py), of which the attacker earns 51 percent back as subsidy; once held, silence costs nothing (the silent key keeps mining and earning) and pauses finality for as long as it likes (finality_horizon.py S: at 34 to 90 percent silent, 0 locks for the whole silence, 0 conflicts). During a pause the chain is proof of work with a 12-hour depth, and a 12-hour 51 percent double spend costs USD 146 at 1 GH/s and USD 146k at 1 TH/s. Tonight's pause is the departure case, confirmed by lane 3 (docs/analysis/horizon/finality-and-weight.md 3.1): 20 keys holding 42.7 percent of the frozen table stopped mining in three minutes, the signing weight fell to 53.1 percent at checkpoint 6843, the frozen table (Q5) holds the pause for a window (2 h on the devnet, 30 days on mainnet) where rule v2 would have locked after 35 minutes; certificates formed while the hub was down, so the topology hypothesis is refuted. The signed departure (LEAVE, lane 3's rank 1) is the fix.
  3. The proving pool is capturable by any block producer today, in proportion to its hash and up to most of it. Consensus checks a proof record's statement against native execution and its signature, and NOT the proof (spec 07 7.7 item 4, 7.8 item 8); the first valid record carried pays. A producer that writes a correct statement with random proof bytes into its own block is paid the shard; at 51 percent of blocks it takes at least 51 percent of the 20 percent pool (11,636 IGN an hour at full subsidy) and, because its fake lands in its next block while honest records need 9 to 11 s of proving, most of the shards outside the 10-s exclusive window. This is ledger P21 priced: the only line in the table where a hash majority earns more than it spends. The fix is proof verification in consensus (section 6, rank 1).

The ranked proposals are in section 6. Two cost nothing in liveness and close whole classes: proof verification in consensus (rank 1) and weight-gated deep fork choice (rank 2: a chain forked deeper than D seconds is a candidate only if the keys that built it hold a third of the weight table at the fork, so rented hash cannot reorg past D even during a pause). Two cost liveness and are not recommended as asked: prover attestations as a second finality leg, and any rule that re-enables locks under the frozen table after an abrupt departure, because a view cannot tell a departure from a partition.

2. Method

Model File What it does Machine, lock, seeds
GHOSTDAG withholding sim/horizon/consensus-security/ghostdag_sim.py Abstract DAG: Poisson arrivals at 1 and 10 blocks/s, 8 equal honest miners publishing at once, one uniform one-way delay d, one attacker at share H withholding (hold T then release; or selfish: release when about to lose or at lead 6), GHOSTDAG coloring and selected parent exactly as protocol.rs (k-cluster with blues_anticone_sizes, topological mergeset, blue work), 10 parents. Reports, from honest miner 0: reorg depth in chain blocks against the chain it held at release, fork age, whether the private tip became the chain, attacker blue share, honest blocks turned red Mac, with-lock.sh run nice -n 19, 20 seeds per cell; 1 bps k 18 (ghostdag_results_1bps.md, 11 s), 10 bps k 124 (ghostdag_results_10bps.md, 223 s); the star check inline (section 4.3)
Finality sweep sim/horizon/consensus-security/finality_horizon.py Imports sim/finality_v2.py unchanged (1,000 Pareto keys, 3 regions, 2-s delay, 2.2 percent outage, no DAG, perfect retarget, keys free); adds six sweeps over the adversary's share 20, 34, 51, 67, 90 percent: renter, silent set, bought keys, poisoned eclipse, partition with an equivocator under v2 and v3, abrupt departure under v2 and v3 Mac, run lock, seeds 7 and 11; finality_horizon_results.md
Signalling game sim/horizon/consensus-security/signalling.py Arithmetic on P2 (95 percent of a one-day blue-block window, floor height): binomial noise, holdout cost, forced-flip cost, signal-then-defect Mac, instant; signalling_results.md
Cost table sim/horizon/consensus-security/cost_model.py Every attack's rented hash A/(1 - A) x N, its duration from the spec's arithmetic, its cost at 1 to 1,000 GH/s, what it earns at the three IGN price inputs of security-budget.md, and the equilibrium network where rent equals subsidy Mac, instant; cost_results.md
Node harness tools/finality-attacks/run.mjs s6 --fast-time against the 0.3.14 Mac binary (vendor/igneum-node/target-0314/release, built 6 Oct 17:56), rule v3 forced on, SCALE 0.4, ports 29800+, suffix 980, /tmp/igneum-horizon-fin The 3/3 and 4/2 partitions on a real DAG Mac, run lock; result in section 4.5 (or the recorded runs if the node refused the file)

Where a number is from a recorded run and not re-run tonight it is cited by file. Nothing live was touched; no tracked file outside this lane's three paths was edited.

3. Evidence: the measured and simulated numbers

3.1 Ordering: what a withholder does to the selected chain (simulated, ghostdag_results_1bps.md section 2, 20 seeds, d = 0.67 s = the cloud devnet's p99 propagation, ledger F7)

attacker share H hold 30 s hold 60 s hold 90 s hold 120 s
20% won 10%, reorg med 0 / max 18, att blue 65% 0%, 0 / 1, 38% 0%, 0 / 1, 21% 0%, 0 / 1, 18%
34% won 40%, 0 / 18, 77% 40%, 0 / 36, 55% 5%, 0 / 39, 35% 10%, 0 / 52, 28%
45% won 70%, 10 / 17, 94% 75%, 22 / 32, 84% 70%, 33 / 46, 80% 70%, 46 / 57, 80%
51% won 85%, 10 / 15, 90% 85%, 20 / 28, 88% 80%, 32 / 44, 86% 80%, 42 / 53, 84%
67% won 100%, 8 / 13, 98% 100%, 16 / 23, 98% 100%, 26 / 31, 99% 100%, 34 / 41, 99%
90% won 100%, 2 / 4, 98% 100%, 4 / 9, 99% 100%, 6 / 13, 99% 100%, 9 / 17, 99%

"won" = the private tip became honest miner 0's selected chain; "reorg" = chain blocks removed from the chain it held at release (median / max over seeds); "att blue" = the attacker's blue blocks over its blocks in the honest view (its weight and subsidy kept). The fork age when it wins equals the hold (30 to 122 s). At d = 5 s (Kaspa's design bound) the same shares win more often at short holds (34% wins 100% at 30 s) and the same at long ones. Natural reorg depth with no attacker: p99 2, max 2 to 3 at d 0.35 to 0.67 s (the cloud devnet measured p99 3, max 5 over 37,113 removals, ledger F7); p99 15 at d = 5 s.

The arithmetic behind the table: the attacker's private chain merges honest blocks as blue until its first private block has k honest blues in its anticone, then every later honest block is red in that chain. So the private tip's blue work is R + min(k, N_h) against the honest tip's N_h, with R = H lambda T and N_h = (1 - H) lambda T: it wins whenever R + k > N_h, that is for every T when H >= 1/2 and for T < k H / ((1 - 2H) lambda) below it (6 s at 20%, 56 s at 34%, 180 s at 45%, with Poisson noise around each). The honest blocks it turns red (hon red: 25 to 46 percent of honest blocks at 45 to 51 percent and 60 to 120 s holds; 4 to 14 percent at 34 percent) pay their 80 percent to the attacker when its chain wins (spec 02 2.5, the red rule), so a sustained withholder at or above 45 percent takes honest subsidy and raises its weight share; at 20 to 34 percent its own blocks go red and it loses both.

Weight share a repeating withholder settles at, derived from the 60-s rows (the longest hold that beats the lock on every checkpoint, section 5.1), approximate: w = H x attblue / (H x attblue + (1 - H)(1 - honred)).

H 20% 34% 45% 51% 67% 90%
weight share under sustained 60-s withholding 9% 25% 48% 56% 77% 95%
blue rate the difficulty controller reads (share of true) 86% 76% 79% 80% 86% 94%

So 51 percent of hash reaches about 56 percent of weight by red-flooding the honest side, still 11 points under two thirds; the honest miners lose about a quarter of their subsidy for as long as it lasts, and the chain reorganises every minute, in public.

3.2 Ordering at 10 blocks/s (simulated, ghostdag_results_10bps.md, k = 124)

measure d = 0.35 s d = 0.67 s d = 2 s
natural reorg depth p99 / max, no attacker 11 / 15 22 / 26 99 / 109
51% hold 30 s: won, reorg med / max - 100%, 50 / 58 -
51% hold 90 s - 100%, 146 / 163 -
34% hold 30 s / 60 s - 95% (59 / 64) / 0% -
20% hold 10 s / 30 s - 45% (10 / 33) / 0% -

At 10 blocks/s the same shares reorganise ten times the chain blocks in the same seconds, and the natural reorg depth at a 2-s delay (99) is above the mainnet checkpoint depth d = 60. C1's rule that d scales with the rate (d = 60 B, ledger F7 round 2) is load-bearing; at 10 blocks/s with d = 600 the determination sits 60 s behind the checkpoint as at 1 block/s.

3.3 The star: run A reproduced (simulated inline, section 4.3 code; 10 bps, k 124, 8 miners, honest only, 120 s)

uniform relay delay d, s blocks in flight honest blocks red natural reorg p99 / max at miner 0 reading
0.67 7 0% 19 / 23 healthy
2 20 0% 78 / 99 reorgs grow with d
5 50 0% 8 / 133 still under k
10 100 31% 0 / 0 past k/lambda: miners stop switching, each on its own chain
15 150 50% 0 / 0
20 200 60% 0 / 0
30 300 67% 0 / 0 run A's 77% red sits beyond this row

Once the effective delay passes k / lambda (12.4 s at k 124 and 10 blocks/s), the DAG stops converging: every miner's own tip is heaviest in its own view, red climbs to two thirds, and the "reorg 0" rows are the absence of consensus, not its presence (run A's 321 tips). Through one hub relaying 12 blocks/s to 41 peers the effective delay is the hub's validation and relay time, which the fleet measures and this lane does not; the model says 77 percent red needs 30 s or more of it, approximate. The controller then reads the blue rate as a third of the true rate and eases, which widens the DAG further (section 4.2 attack 8). Lane 5 owns the fix; the attack bound is in 4.2.

3.4 Finality weight over the adversary's share (simulated, finality_horizon_results.md, seeds 7 and 11; the table is inserted in section 3.5 from the run)

3.5 Finality sweep results

Condensed from finality_horizon_results.md (seeds 7 and 11; the full tables are there). A = the adversary's share.

sweep 20% 34% 51% 67% 90%
R renter, signing: day it reaches 1/3 / 2/3 (sim; formula 10/A, 20/A) never / never 30.0 / never 20.0 / never 15.0 / 30.0 12.0 / 23.0 (formula 11.1 / 22.2; the dust effect of B)
S silent set keeps mining, 6 h: locks while silent, first lock after resume 100%, 0 min 0%, 0 min (720 stalled) 0%, 0 min 0%, 0 min 0%, 0 min; 0 conflicts in every row
K bought keys worth A, buyer mines 30%: veto held (days) / stalls if silent (of 86,400) never / 816 to 1,045 day 1 to 6 / 36k to 42k day 1 to 24 / 74k to 76k day 1 to 26 / 79k to 80k day 1 to 27 / 82k; share at day 30 is 30% in every row; 0 conflicts
E poisoned eclipse, 20% pool, 2 h: conflicting locks (eclipsed side holds 20% + A) 0 (40%) 0 (54%) 67 to 70 from minute 2 (71%) 30 to 32 from minute 4 (87%) not run: the attacker alone is over 2/3
P 50/50 partition with an equivocator, 150 min, v2 and v3: conflicting locks, first at 0 21 to 70, minute 14 to 78 (the knife edge) 299 to 300, minute 0 301, minute 0 293 to 300, minute 0; every pre-heal lock kept, 0 post-heal stalls, v3 = v2 in every cell
C abrupt departure (stops mining and signing): first lock, days, v2 / v3 (analytic v2 30(1 - 1/(3A))) 0.00 / 0.00 0.8 / 30.0 (0.6) 10.5 / 30.0 (10.4) 15.2 / 30.0 (15.1) 18 to 19 / 30.0 (18.9); 0 conflicts

Readings. R: the formula holds to 0.1 day; 51 percent never reaches two thirds. S: from one third upward the pause is exactly the silence, free to the silent set. K: bought weight is worth its blocks and decays; a silent 51 percent buyer pauses finality for 24 days then loses the veto. E: an eclipse cannot produce a conflict below the one-third equivocator bound whatever the pool; above it the conflict is the equivocator's, not the eclipse's. P: the bound is one third in every view under both rules, as 3.11.2 says; at 34 percent the model's 2.2 percent outage makes it intermittent (21 to 70 of 300 indices). C: under v2 the pause after a departure ends when the survivors fill two thirds of the sliding table; under v3 (the live rule since 135,200) on day 30 whatever the share; on the devnet's 2-hour window those days are minutes: 2 h after the last lock under v3, which for tonight's 18:39:40Z lock is about 20:40Z (approximate, if the departed boxes hold over a third of the frozen table and do not return).

3.6 Signalling (arithmetic, signalling_results.md)

fact value
noise on a one-day window share at p = 0.95 0.07 points; a 94.5% fleet never flips, a 95.1% fleet flips on day one (P 0.91)
6% holdout rent per day at 1 / 10 / 100 / 1,000 GH/s USD 18 / 179 / 1,792 / 17,923 (it earns 6% of the subsidy meanwhile)
forced flip, 95% of one day's blue blocks (19 N for 24 h) USD 5,335 at 1 GH/s, 53k at 10, 534k at 100, 5.3M at 1 TH/s; the market could not supply a TH/s on 6 Oct
signal then defect the defector's blocks fail PoW under the new program and are refused; cost falls on it alone

3.7 Cost of every attack (arithmetic, cost_results.md, excerpt; the full table has 12 rows x 4 network sizes)

attack share, duration rent at 1 GH/s at 100 GH/s at 1 TH/s subsidy earned meanwhile (IGN)
win the lock-latency race (90 s) 51%, 90 s USD 0.3 USD 30 USD 304 1k
12-h double spend during a pause or the first 30 days 51%, 12 h USD 146 USD 15k USD 146k 559k
orphan an hour beyond merge depth during a pause 51%, 1.5 h USD 18 USD 2k USD 18k 70k
the veto, 1/3 of weight 51%, 20 d USD 6k USD 585k USD 5.8M 22M
lock alone, 2/3 of weight 67%, 30 d USD 17k USD 1.7M USD 17.1M 44M
long-range private DAG over the window (cold start) 51%, 30 d USD 9k USD 877k USD 8.8M 34M
hold a pause once the veto is held 0 marginal 0 0 0 keeps earning
take the hands or the 8 aggregators down 0 hash DoS cost only
fake proof records as a block producer 0 extra hash 0 0 0 up to the whole 20% pool

At the rental-market equilibrium (hash joins until rent equals subsidy: 39, 156 and 780 GH/s at USD 0.005, 0.02 and 0.10) the veto nets about 48 percent of 20 days of the chain's subsidy and locking alone about 33 percent of 30 days; the 12-hour pause-time double spend costs about 12.5 hours of subsidy.

4. The attacks, by layer: what each achieves, the defence, the bound, the price

The hash shares H run 20, 34, 51, 67, 90 percent in every table; "rent" is A/(1 - A) x N x hours x USD 11.7 and the four network sizes are 1, 10, 100, 1,000 GH/s.

4.1 GHOSTDAG ordering

attack what it achieves (sim) defence bound rent (1 GH/s to 1 TH/s) earns
Selfish mining on the DAG (release when about to lose, lead 6) blue share equals hash share within 0.3 points at every H (ghostdag_results_1bps.md section 3): honest blocks are merged, not orphaned, so there is no relative gain; max reorg 1 to 3 chain blocks GHOSTDAG merges parallel blocks; a red block pays the merger 0 gain; the attacker's reds are its loss 0 extra nothing
Withholding to reorder (double spend) 20%: the last k blocks, 5 to 15% of attempts; 34%: 30 to 60 s, 40%; 45 to 51%: the whole hold, 70 to 85%, 32 to 46 chain blocks at 90 s; 67%+: every attempt the lock: a candidate tip must pass through every certified checkpoint (spec 03 F1); the checkpoint block locks 63 to 93 s after it is mined reorg depth = the lock latency, 90 to 120 s (spec 3.11.3), whatever H under 2/3 of weight; credit on the lock only (P17's four states) 90 s at 51%: USD 0.3 / 3 / 30 / 304 a deposit credited BEFORE the lock, which no conforming wallet does
Sustained red-flooding of honest blocks (repeat 60-s withholds) 45 to 51%: honest blocks 25 to 28% red, honest subsidy to the attacker, weight share 48 to 56%, chain reorganising every minute the lock bounds each hold to under 63 s (the first checkpoint inside the hold locks by then); W2 counts blues weight ceiling about 56% at 51% of hash, 77% at 67% (approx., 3.1): a 51% miner never reaches 2/3 the ordinary cost of 51% about a quarter of honest subsidy while it lasts
Balance attack (keep two honest halves balanced) needs network control, not hash: harness s3 and the redteam show a partition under merge depth heals to one chain (redteam-2026-10-04.md rows 2, 3) merge depth 3,600 s merges the sides; beyond it, blue work and finality decide one chain within merge depth; beyond it the 3.7 item 9 fork (section 4.3 finality) 0 hash nothing without a partition tool
k-cluster poisoning (make honest blocks red) the same as red-flooding: only a withholder can be in an honest block's anticone without being in its past; share bound as above k = 18 at 1 bps (Kaspa's table, delay bound 5 s) at d = 5 s a 34% withholder turns 29% of honest blocks red in a 30-s window (sim) as 51% redirected subsidy at 45%+ only
Timestamp games on ordering none on GHOSTDAG (order is by blue work and hash); on the clocks see 4.2 10-s future tolerance, parent minus 10 s (spec 02 2.3) past-median time can run at most 10 s ahead of real time: nothing against a 30-day window 0 nothing
Merge-depth games (release a chain forked over 3,600 s ago) honest blocks of the hour become unmergeable and are abandoned if the released chain is heavier: the 229-block shape of 6 Oct (CLAUDE.md 6 Oct rules) F1: a chain missing a certified checkpoint is not a candidate; any lock inside the hour kills it only during a pause or the first 30 days; depth then bounded by the finality depth, 12 h 1.5 h at 51%: USD 18 / 183 / 2k / 18k an hour of honest subsidy orphaned, none gained
Red-block flooding (publish blocks on stale parents) the attacker's blocks are red, pay the honest merger, carry no weight; honest blues unaffected W2, the red rule pure loss to the attacker nothing

4.2 The difficulty rule: the seven recorded ways and the ones to add

The seven of sim/difficulty/attacks/README.md and results.md, read not re-derived (Igneum rule v2 with the 4 October clock and floor):

# way recorded bound status
1 pool hopping (10 to 100% of the base, 24 h) +1.5% blocks per hash at most, 0.7 points over Kaspa's rule; a 60-s dwell makes the 50 and 100% hoppers lose 1.7 to 4.0% PASS under 5%, open by the letter
2 pulsed rental (50x for 10 min hourly) weight per hash 0.26 (Kaspa's rule 0.98): a pulse buys no weight; the base's blocks per hash fall 36% in the hour after PASS (M14, F14 closed with the finality run: the renter never reaches a third)
3 timestamp stretching (30 and 50% forger, earliest, latest, alternating) +0.4 to +1.1% drift after an hour (worst seed +2.7%), difficulty ratio 1.00, worst gap 10 s; on 3 igneumd nodes a 50% forger moved nothing (0.82 to 0.88 blocks/s, 0 rejected) FIXED (M23); before the fix the chain ran at a fifth of its rate at 9.9x difficulty
4 short-lane oscillation (25% square wave every 120 blocks) std 0.160 against 0.045 steady, 12% above the attacker's own square wave; the oscillator earns 1.4% less FAIL by the letter, no past-only controller can pass, no change
5 epoch games (hold dodger, hold flooders) 0.0%, +0.7%, +0.3% (worst +2.4%) PASS
6 polluted window (10x joins and leaves at the lane switch) settles 292 to 334 s, worst gap 17 s PASS (Kaspa's rule 2,910 to 3,540 s)
7 block flood (85 blocks/s of PoW-less input) the target stops at 2^128 after about 2,630 blocks, no panic FIXED (floor)

Ways not in the seven, with the bound this lane gives:

# way model bound who gains
8 Red-share gaming: a withholder turns honest blocks red, the estimator counts blue work only (spec 02 2.3 "what this section does not do"), the controller eases the 60-s rows of 3.1: the blue rate read is 0.86 of true at 20%, 0.76 at 34%, 0.79 at 45%, 0.80 at 51%, 0.86 at 67% the ease is at most 1/(blue share) - 1: 16 to 32% more blocks per real second for everyone (emission above schedule by the same factor, spec 02 2.5); no relative gain to the attacker beyond 4.1's redirected subsidy; the attacker's own reds cap it nobody relatively; everyone's emission runs 16 to 32% fast while it lasts
9 The star collapse (run A): effective delay past k / lambda, red to 67 to 77%, the controller reads a third of the rate and eases, the DAG widens 3.3's table a positive feedback with no attacker: the controller must read total work or the fleet must not be a star; lane 5 owns the rule, the fleet lib the topology an attacker who can slow the hub (DoS) gets the collapse for free
10 Clock trust after a pruning-proof sync: a header whose selected parent has no stored clock starts from the raw stamp (difficulty-2026-10-03.md section 11, Limits) not measured at most one window of bias after a sync; a forger needs to be the first blocks a syncing node sees a stretcher against fresh nodes only
11 Partition retarget: each side retargets to its share within 657 s (the 50x step-down figure), so each side keeps 1 block/s; at the heal the heavier side's targets rule and the lighter side's blocks carry less work spec 02 2.3 measured steps; finality_v2.py +daa consistent by construction; the minority's blocks merge red under merge depth nobody

4.3 The finality weight

attack what it achieves defence bound (sim) rent earns
Sybil (many keys) nothing: weight is blue blocks, every draw is by weight (W6; harness s2: dust keys zero weight, sortition by weight PASS, F17 fixed) W2, W3, F17 0 0 0
Weight capture by mining (the renter) share (t/30) A: 1/3 on day 10/A, 2/3 on day 20/A, never under A = 2/3 (results_v2.md B to 0.04 points; sweep R) the 30-day flat window 51%: veto day 20, 2/3 never while honest miners stay; 67%: day 15 and 30; 90%: 11.1 and 22.2 20 d at 51%: USD 6k / 58k / 585k / 5.8M 22M IGN of subsidy
Weight capture by buying or renting keys a bought key is worth its blocks and decays as the window slides: share = A (1 - t/30) + r t/30 (3.11.5; results_v2.md K; sweep K); keys worth 40% hold the veto from day 1 to 20, worth 20% never W2 decay, W5 succession, equivocation strips a sold key the seller still holds max(A, r) for a day, r after 30 days; a silent 40% buyer stalls 63k of 86k checkpoints then loses the veto on day 19 to 20 the price of pools' keys, not hash a pause of up to 20 days
Long-range (private DAG from an old point) a cold node with no certificate follows the heavier DAG (F5) F5's trusted certificate (designed, not implemented); the client-shipped checkpoint (proposal 11) needs more blue work than the public DAG over the window: 30 days of >50% USD 9k / 88k / 877k / 8.8M 34M IGN
Eclipse (poisoned pool) 0 conflicting locks, 0 locks on the eclipsed side at 1, 2, 4 h for a 34% attacker and a 20% pool (results_v2.md L3, F2); sweep E extends it to 51 and 67% the 2/3-of-total floor binds whatever the presence window says (3.3.2) the eclipsed side must hold 2/3 of total: a 47%+ attacker plus a 20% pool (sweep E, see 3.5) the eclipse plus the weight nothing under the bound
Partition with an equivocator 0 conflicts to 33%, conflicts from 34% (results_v2.md H at 2/3; M5 under v3); sweep P at 51, 67, 90 two certificates need 4/3 of weight in signatures (3.11.2) 1/3 of weight, every view, any partition length under one window since the last lock (v3) the 20-day veto two finalised histories across a partition, each side's deposits
Equivocation alone strips the key for 30 days, no coin penalty (F6); detection by any carrier block, agreed by every node (F23 fixed) 3.6 costs the attacker its weight, nothing else nothing
The 30-day window edges (a) the frozen table expires at exactly day 30.00 after the last lock: both sides of a long split lock alone at once (M3); (b) a departed set leaves the sliding table over 30 days and the frozen one at the cliff (M4); (c) the first 30 days have no lock at all (3.8, min_daa = window); (d) new honest cohorts are under-weighted t/60 for 30 days (G) stated in 3.7 items 2, 7, 9 a partition or departure longer than one window ends with the fork of 3.7 item 9 and a manual F5
The pause as a liveness attack a silent set at or above 1/3 pauses every lock for as long as it stays silent (J, L1; sweep S) at zero marginal cost since it keeps earning none in the rule; the node reports the pause; exchange guidance treats the chain as PoW with a 12-h depth the 1/3 veto: 20 days at 51% 0 once held nothing directly; enables the 12-h PoW double spend below
What an attacker can do during a pause plain proof of work: reorg up to the finality depth 43,200 DAA (12 h) with a heavier chain; beyond merge depth the honest blocks are abandoned (the 229-block shape); every certified checkpoint before the pause still binds finality depth; the exchange guidance of 3.9 12 h of >50% hash USD 146 / 1.5k / 15k / 146k a deposit credited at the PoW depth; 559k IGN of subsidy as a miner
Tonight's departure (confirmed, lane 3 finality-and-weight.md 3.1 and 4.1) 20 keys holding 42.7% of the frozen table stopped mining 18:27 to 18:30Z (the rehearsal job); the last lock 6842 at 18:39:40Z; 6843 determined with 53.1% of total signing and never locked; under v2 the stayers' sliding share crossed two thirds at 6912 (19:14:53Z, a 35-min pause) but Q5 held them at 57.3% of the frozen table; expected first lock when that table expires at DAA 216,402, about 20:40Z, or when 9.4 points of departed keys return. Sweep C agrees: 51% leaving pauses 10.5 days (v2) or 30.0 days (v3) at mainnet scale; at tonight's 46.9% (observer's view) 7.7 days under v2, 30 under v3 (lane 3, 4.1) by design (F21: the founder chose the pause over the fork); a view cannot tell a departure from a partition anything over 1/3 of the table leaving at once pauses finality for a window 0 0; what an attacker can do during it is the row above

4.4 Miner signalling (P2)

game model bound price
6% holdout blocks a change for ever the window share has 0.07 points of noise: 94.9% flips with P 0.09, 94.5% never the floor N6 ends it; nothing else does USD 18 a day at 1 GH/s, 17.9k at 1 TH/s; the holdout earns 6% of subsidy meanwhile, so net about zero at equilibrium
What the floor does converts the signal into a fixed height at N6: the hazard of 6 October (DAA 198,000 crossed while boxes were still updating: two-sided chain, 229-block reorg) returns for every node not on the object at N6 the floor should sit no nearer than a week past the publish on a network miners run, and the stale-box list (P1 pass rule) must be empty before it
Signal then defect a defector's blocks fail PoW under the new program and are refused (check_header_version then PoW); a pool with stale workers loses their blocks the defector pays, nobody else
The one-day window a renter at 19 N for 24 h with a patched byte forces the flip; for v4 it hurts nobody (the signalling binary is the v4 binary), for a later object it forks every node still on the old one the share of the fleet not yet on the object at the forced flip USD 5.3k at 1 GH/s to 5.3M at 1 TH/s, and the market could not supply a TH/s
Proposed require 95% on each of 7 consecutive daily windows (7x the renter's bill, a week of visible share), keep the one-day tally for display 3 hours

4.5 Proof records

attack today's rule bound who gains
Forgery of state the native-execution veto: a record whose statement differs from the node's own execution of that segment along the carrying block's chain is ignored (7.2 item 5, P11 fixed) state is never moved by a record; a soundness bug is a light-client problem (P7), a job-output problem for the precompile (D6, contained by R12) nobody
Forgery of the proof (correct statement, random bytes) NOT checked in consensus (7.7 item 4, 7.8 item 8); the first valid record per shard or segment carried pays a producer at share H takes at least H of the 20% pool and, since its fake rides its next block while an honest proof takes 9 to 11 s on a 5090 (P9 table), most shards outside the 10-s exclusive window; inside it only the H of slots it is assigned any block producer: 11,636 IGN/h at 51% of a pool paying 22,815 IGN/h
Withholding (an assignee sits on its window) after 10 DAA s anyone may prove and be paid; a segment unproven after 600 DAA pays nothing (7.8 item 7); mandatory proofs off one window of latency per absent assignee; nothing waits nobody
Grief (flood the pool with invalid records) 6,000 invalid records: 0 accepted, 0.31 to 0.68 ms each, node up (redteam row 10) CPU per record nobody
The aggregator naming itself as every prover provers committed in the proof's public values and checked (P12 fixed) 0 nobody
Record ordering race the first valid record carried wins: a producer can front-run honest provers' records in its own block (the forgery line) fixed by consensus verification (rank 1), then by aggregator sortition (O-7.3)

4.6 The execution layer

attack today's rule bound note
Snapshot poisoning over p2p p2p_snapshot_gate refuses tip 0, below the restart, at or below the own tip, and anything while the executor runs unblocked; a snapshot whose state at the restart block differs from exec_restart_state_root is refused (release-0.3.14.md) a wrong state ABOVE the restart block is not detectable by the node: headers commit to no execution root (spec 02 2.6: blocks carry transactions only), certificates sign (chain id, index, block hash) only (C2) the poisoned node's native statement then disagrees with every carried record, it pays nothing and sees every honest record as invalid; the signal exists but nothing reads it as an alarm
The pin exec_restart_number / hash / state_root / trust_daa arrive by the signed manifest on the devnet (the reddit review's admin-key finding, 1.6 item 3); on mainnet no manifest exists, so the pin is genesis-only or absent a release-key holder sets execution state on the devnet; on mainnet the same power would need the 95% signal disclose (the review's key-powers table)
Deep reorg never resets execution a reorg reloads the newest persisted generation at or below the fork (ring 2,048) else blocks loudly and asks a peer; never a genesis replay on a pruned node (0.3.14) under active finality a reorg is bounded by the lock (90 to 120 s), far inside 2,048; during a pause the 12-h depth is 43,200 blocks, 21x the ring, so a pause-time deep reorg blocks every pruned executor until a peer's snapshot arrives, which is the poisoning path above the ring should reach the finality depth (proposal 12)
Duplicate and nonce games, pgas bombs, malformed bodies exec-attacks suite 96 of 97 checks, every executed block under B_p, an over-budget transaction refused at the mempool with the pgas metered (redteam row 28) per block B_p of proving gas and B_e of execution gas; an aborted transaction pays

4.7 Peer to peer

attack what it achieves defence bound price
Eclipse of one node feed it a private chain: its difficulty eases to the attacker's hash within about 11 min (the 50x step-down takes 657 s), so 1% of the network's hash produces a plausible 1 block/s chain for the victim within 20 min; it sees no certificates and reports finality_active false the exchange guidance (treat a pause as PoW with a 12-h depth); a node that holds locks will not follow a chain missing them (F1) a victim that follows its node's finality flag loses nothing credited under a lock; one that credits at a PoW depth is Kaspa's or Monero's eclipse victim a few IPs
Eclipse or outage of the hands tonight's pause was NOT this (lane 3, 3.1: locks 6824 to 6842 formed with the hub down, the zero-aggregator fallback carried 64 of 251 certificates); the attack stands in general: a fleet that peers only through two hosts is a star, and a star with its centre down is a partition into n islands, each under 2/3, finality paused until the heal; longer than merge depth (60 min) it is the 3.7 item 9 fork aggregator fallback (any node aggregates after 15 DAA s); votes ride in blocks (Q2); neither crosses a dead hub pause for the outage; proposal 4 (peer floor) removes the star 0 hash: the DoS of two hosts
Crash a pruned node from any peer (main, 19:57Z: the hub, a pruned 0.3.14 node, panicked on an unwrap over KeyNotFound at the devnet genesis when a re-joining peer synced below its retention; consensus/src/processes/sync/mod.rs:87, fix in 0.3.15) any peer takes any pruned node down by asking for history it does not hold none today; the rule: no unwrap or expect on a path a peer's request reaches, a SyncManagerError instead, and a fuzz of the sync request space (locator low/high, antipast low/high, missing-bodies high, pruning-point anticone) against a pruned node as the CI gate zero hash; one request per node; repeated, a liveness attack on every pruned node (every mainnet node prunes) 0
Eclipse of the seeds (3 Hetzner DNS seeds) a fresh node bootstraps into attacker peers and, with no trusted certificate, follows their DAG (F5 cold start) none implemented; F5 designed the long-range attack's price (4.3) for the DAG, zero for the eclipse USD 9k to 8.8M for the DAG
Handshake refusal (params digest) a peer with another digest is refused before any flow (X18); an attacker cannot make honest peers refuse each other it is a defence; the only cost is the digest-less allowance still open on devnet and simnet 0
The p2p snapshot path 4.6
Memory under flood 269 to 780 MB per minute of flood on 0.3.4 (M30), bounded since 0.3.5 to the record window plus pruning depth

Siblings of the 19:57Z crash class, read-only grep of the fork (main checkout vendor/igneum-node, 6 Oct) for unwrap and expect on paths a peer's request reaches. The sync manager's entry points are called from the request flows (protocol/flows/src/v10/request_headers.rs, request_antipast.rs, request_block_locator.rs, request_ibd_chain_block_locator.rs, request_pruning_point_and_anticone.rs, request_block_bodies.rs) through consensus/src/consensus/mod.rs:1341 (get_hashes_between), :1624 (get_missing_block_body_hashes), :1647 (create_block_locator_from_pruning_point):

file:line what panics reached by
consensus/src/processes/sync/mod.rs:87, 88 ghostdag_store.get_blue_score(low/high).unwrap(): the hub's crash when low is below retention antipast_hashes_between from a peer's antipast or headers request
sync/mod.rs:94 ghostdag_store.get_data(current).unwrap() on the forward chain walk the same
sync/mod.rs:117 find_highest_common_chain_block(...).expect("because of the pruning rules such block has to exist"): false once the peer's low is pruned the same
sync/mod.rs:123, 125, 130, 135, 148 pruning point, selected-chain tip and index lookups .unwrap() create_virtual_selected_chain_block_locator from a peer's locator request
sync/mod.rs:162, 172, 182, 194, 196 status lookups .unwrap() along a peer-named high get_missing_block_body_hashes from a peer's IBD blocks request
sync/mod.rs:211, 221 get_blue_score(low), get_compact_data(current) .unwrap() create_block_locator_from_pruning_point from a peer's IBD chain locator request
protocol/flows/src/v10/request_headers.rs:98 hashes.last().expect("caller ensured ...") the headers request flow, after get_hashes_between
protocol/flows/src/ibd/negotiate.rs:40, 47, 112, 166, 172 locator_hashes.last().unwrap() on a locator the PEER sent the IBD negotiation (the syncee side, a hostile syncer)
protocol/flows/src/ibd/flow.rs:307, 361, 436, 440, 700, 717 async_get_header(...).unwrap(), async_validate_pruning_points(...).unwrap(), pruning_points.last()/first().unwrap() on peer-sent pruning points IBD against a hostile syncer

The rest of the hits in those files are test code (request_headers.rs:199 to 222, request_pruning_point_and_anticone.rs:197 to 280, trusted_data.rs:81 to 149, proof.rs:144 to 230) or channel sends. Bound of the class: zero hash per crash; every pruned node on the network can be taken down by one request each, repeatedly, which during a pause or the first month is a liveness attack on the chain itself and at any time on the hands. Proposal (lane 1, 4 hours): convert the sync manager's unwraps to SyncManagerError variants, make the negotiation and IBD paths return ProtocolError on a missing block, and add tools/ci fuzz sync-request-fuzz that drives the six request flows with random and below-retention hashes against a pruned fast-time node and fails on any exit; gate: 10,000 requests, node alive, 0 panics.

4.8 The harness run (real DAG, 0.3.14 binary, rule v3, fast time)

tools/finality-attacks/run.mjs s6 --fast-time, SCALE 0.4, rule v3 forced on (finality_v3_activation_daa 0), the 0.3.14 Mac binary (vendor/igneum-node/target-0314/release/igneumd, built 6 Oct 17:56) and its igneum-miner (vmine), ports 29800 to 29812, suffix 980, /tmp/igneum-horizon-fin, run lock held 19:53 to 20:03Z. The 0.3.14 node refuses the master copy of infra/fast-time/override-60x.json (unknown fields program_class_v4_activation_daa and program_class_v4_signal_window_daa, which only the ca3-v4-node binary knows), so the harness ran from a scratch copy of tools/finality-attacks and infra/fast-time with those two fields removed; nothing tracked was edited. Six vmine voters at 6 blocks/s in all (3 per side), window 120 DAA, warm 168 s, split 60 s, heal 60 s.

scenario measured reading
A, 3/3 split window DAA ~1,019 at the cut, 6 voters, max locked 34 on both sides; new locks during the 60-s split 12, the first on each side at 30 s; locks resumed after the heal; conflicting certificates 9 / 11 after the heal the recorded F21 shape at this scale: a side at 3 blocks/s advances its own DAA 3 a second, so the frozen table (one window of 120 DAA after the last common lock) expires 30 to 40 s into the split and the sliding table then locks each side alone, exactly as the redteam's row 18 (36 and 49 s) and the simulator's M3; the harness criterion (zero new locks) asserts more than the rule promises past one window
B, 4/2 split window DAA ~900, max locked 30; the 4 side locked 30 to 38 (first at 33 s), the 2 side stayed at 30 for the split; conflicting certificates 1 / 12 after the heal the 4 side holds 4/6 = two thirds of the frozen table and locks as Q3 allows (inclusive); the 2 side locks nothing while the table stands; the post-heal conflicts are the 2 side's late solo locks after its own table expired (redteam row 18 saw the same 4 late locks), the F21 residual

What it adds to the sim: the real DAG at fast time reproduces the frozen-table bound within its own DAA clock and the v2 control's shape (split50-v2.md: 3 solo locks at 126 s, 4 conflicts); nothing contradicts the simulator. What it does not test: a withholding attacker (vmine has no withhold flag) and any run longer than the window.

5. Model: the formulas and what holds

5.1 The ordering race against the lock

Inputs: lambda blocks/s (measured 1 on the devnet), k = 18 (cited, Kaspa's table), d (measured 0.34 to 2.3 s), checkpoint interval I = 30 blue score and depth d_cp = 60 (designed), lock latency after determination median 2.5 s, p99 4.6 s (simulated, results_v2.md A), 0.8 to 1.08 s on the test network (measured). A deposit at time 0 lies under the checkpoint block mined at most 30 s later, which locks at most 30 + 60 + 3 = 93 s later. An attacker forking before the deposit must win the race before that lock (after it, F1 excludes its chain). From 3.1: wins need H >= 1/2 for any T, or T < k H / ((1 - 2H) lambda) below it; at T = 90 s that is H >= 0.45 with Poisson noise (70 to 85 percent at 45 to 51 percent, 5 to 10 percent at 34 percent). Hence: a majority reorders at most the lock latency; a 34 percent miner at most about 60 s; a 20 percent miner at most the last k blocks. The honest guidance (credit on the lock) makes every case a zero.

5.2 Weight

share_renter(t) = (t/30) A (verified, B, sweep R); share_buyer(t) = A (1 - t/30) + r t/30 (verified, K, sweep K); two certificates need 4/3 of total in signatures, so the equivocator bound is 1/3 in every view (3.11.2, H at 2/3, M5 under v3); a partition side's own table reaches 2/3 on day 30 (2/3 - s)/(1 - s) under v2 (L4) and never before day 30 under v3 (M2, M3); a departed share x pauses 30 (1 - 1/(3x)) days under v2 and 30 days under v3 (L2, M4, sweep C). The weight ceiling of a withholder is 3.1's w(H) with the 60-s rows.

5.3 Rent

cost = A/(1 - A) x N x hours x 11.7 USD (measured price); earned = 0.8 x A x 31.688 x 3600 x hours IGN (spec 02 2.5); N_eq = 0.8 x 31.688 x 3600 x P / 11.7 GH/s at IGN price P (assumption inputs). At N_eq the veto nets about 0.48 x 480 h of subsidy and locking alone 0.33 x 720 h.

5.4 What holds, what breaks

claim holds evidence
No hash share under 2/3 of weight reverses a certified checkpoint yes 3.11.2, H and M5 (0 conflicts under 1/3 in every seed), the DAG sim (the race ends at the lock)
51% of hash never reaches 2/3 of weight while honest miners stay yes, with margin: 56% ceiling under red-flooding (approx.), 51% honest B, sweep R, 3.1
A majority cannot forge a proof the nodes re-execute yes for state; NO for payment: the proof itself is not checked and the pool is capturable spec 07 7.7 item 4, 7.8 item 8
A rule changes only at 95% signalling yes for the signal path; the floor is a fixed height that can cross with stale nodes P2, signalling.py
A majority loses more than it earns for the veto and the lock-alone, yes (net 48% and 33% of the period's subsidy at equilibrium); for the pause-time 12-h double spend and the fake records, NO cost_model.py
The pause is safe safe for history, costly for liveness, and buyable at zero hash by taking the hub down tonight; sweep S and C

6. Ranked proposals: the defences we do not have

rank proposal evidence model hours consequence per tier gate
1 Verify the aggregated segment proof in consensus (a record whose proof does not verify against the pinned aggregator key is invalid; the per-shard v0 record stays payout-only until then and is capped at the exclusive window) 4.5: a producer captures H to most of the 20% pool with fake records; P21 "stated, not fixed" capture = pool x (H inside the window + most outside); verification cost per record = SP1 light verifier (P3: 1.3 to 2.1 s setup, then per-proof ms, to measure) x 2 records per block 8 to 12 home miner (8/12/16 GB): its honest shard records are paid, not front-run; rig and pool: proving income real; prover: the market is honest; holder: 20% of emission is not a miner's bonus; rollup customer: a paid proof is a verified proof; node: +verify CPU per block fast-time: a correct-statement fake record is refused, an honest one paid, p95 block validation under 50 ms with 2 records
2 Weight-gated deep fork choice: among candidate tips, a tip whose fork point is older than D (say 10 min of past-median time) is a candidate only if the blocks on it since the fork were produced by keys holding at least 1/3 of the weight table at the fork point (a function of the block's past, deterministic) 4.3 "during a pause": a renter with fresh keys double-spends at the 12-h depth for USD 146 at 1 GH/s; the DAG sim's 90-s race rented hash has zero weight for 10 days (W2), so its deep chain is never a candidate; honest partition sides over 1/3 keep today's behaviour; a side under 1/3 cannot reorg the other past D, which is the desired outcome 10 to 16 (virtual processor candidate filter + weight-at-fork from the finality tables) home miner and rig: nothing changes; pool: nothing; holder and exchange: a pause-time or first-month deep reorg needs 1/3 of weight, 20 days in public, not 12 h of rent; node: one table lookup per deep candidate; rollup customer: PoW-depth credits become weight-backed fast-time: a fresh-key renter at 3x the hash forking 2 min back is refused for ever; a 40%-weight honest side forking 2 min back is adopted
3 Vote-or-burn: a block whose producer key has participation under 0.5 over the presence window in the block's own past burns 20% of its producer share 4.3 "the pause as a liveness attack": zero marginal cost the attacker's pause then costs 0.2 x A x 0.8 x 114,077 IGN/h: 7,757 IGN/h at 34% (USD 155/h at 0.02); partition-safe because the test is the block's own past (a side's keys vote their own checkpoints); honest outages (2.2%) leave participation above 0.9 6 to 8 (coinbase rule + the finality manager's participation count at the block) home miner under dust: not a voter, counts 1, unaffected; a miner whose node never revealed a vote key: loses 20% until it does (an incentive); pool: votes or pays; holder: silent weight stops being free fast-time: a 40% silent set's coinbases shrink 20%, honest ones do not, both sides of a 3/3 split unaffected
4 Peer floor and mesh for the fleet and the node: the fleet lib dials at least 3 other boxes beside the hands; the node logs an alarm and the app shows it when outbound peers fall under 3 or when no vote has been received for 2 checkpoints run A's star (321 tips, 77% red); the hands as the fleet's only peers (lane 3 refuted this as tonight's cause; the shape stands) a star with its hub down is n islands; with 3 extra peers per box the graph stays connected under any single failure 2 to 4 (fleet lib) + 3 (node alarm) every tier: finality stays up when a hand dies; home miner: sees "no peers" instead of a silent pause Devnet 2: kill the hub for 10 min, locks continue; the alarm fires on the known-bad case and not on the known-good
5 Vote over the execution root too: the vote signs (chain id, index, block hash, post_root of the checkpoint's segment); a certificate then pins the state, snapshots are checked against the last certificate, and the poisoned-snapshot node cannot join the quorum 4.6 snapshot poisoning: a wrong state above the pin is undetectable every voter is a full node that executes natively (spec 07); the cost is exec lag added to lock latency (the executor runs seconds behind the tip) 8 to 12 holder and exchange: a certified checkpoint carries its state; rollup customer and light client: one object says ordered and executed; node: a divergent executor is visible at once; miner: a vote waits for its executor (lock latency + exec lag) fast-time: three nodes agree; a fourth with a tampered snapshot votes a different root and is outvoted, alarm raised; lock latency rises by the measured exec lag only
6 Signalling over 7 consecutive daily windows, floor no nearer than 7 days past the publish, the stale-box list empty before the floor 4.4 the renter's bill x7; a week of visible share 3 pool and rig: a week more before a class change; home miner: a week to update fast-time gate: 6 of 7 days at 95% does not flip; 7 does
7 Detector-driven alarms: the share-pattern detector (counter-asic-3-status, Detector row) and the finality flag feed one node-side security_alert (correlated group over 1/3 of a day's blue blocks; pause; conflict) that wallets and the explorer show as "confirm at 12 h" 4.3, the exchange guidance exists only as text an alarm when any single party crosses the veto line in public, which the rule says takes 10 to 20 days 4 to 6 holder and exchange: a number to act on; home miner: the app shows the chain's state fast-time: a 34% silent set raises the alarm within 5 min; the honest run never does
8 The signed departure (LEAVE: lane 3's rank 1, finality-and-weight.md section 6; a leave item carried in blocks, the key out of every denominator one hour after inclusion, sent by the app and the fleet library on a clean stop) and F5's trusted certificate implemented tonight's departure (lane 3, 3.1): 42.7% left in three minutes and the frozen table held finality for a window; a view cannot tell a departure from a partition, so no automatic rule re-enables locks without reopening L4/M3 stripping lowers total; an attacker stripping stolen honest keys is K's bound (needs keys worth 1 - a/(2/3)); lane 3's sim T: first lock 1 h after a 34 to 50% departure, 0 conflicts in every partition row 6 (lane 3) + 4 pool and rig: an orderly stop keeps finality up for everyone; holder: no 30-day pause after a planned fleet move; node: the operator's certificate for the disorderly case fast-time: 45% of weight stops with exits, locks continue; without, the pause
9 Client-shipped checkpoint: each release carries the latest certified checkpoint (index, hash) and voter-table digest; a cold node refuses a DAG missing it (assumevalid's shape) 4.3 long-range, 4.7 seeds the long-range attack must then out-work the public DAG since the release, not since the window 3 every tier: a fresh install cannot be bootstrapped onto a private DAG; trust is the release key already trusted for the binary a cold node offered only a private heavier DAG refuses it
10 Exec generations spaced geometrically to the finality depth (1, 2, 4 ... 43,200 blocks: about 16 generations) so a pause-time deep reorg never needs a peer's snapshot 4.6 16 x state size on disk (about 1.8 GB today, measured 114.8 MB per snapshot) 3 node operator: disk; every tier: no blocked executor after a deep reorg fast-time: a 5,000-block reorg re-executes from a generation, no snapshot request
11 Checkpoint anchoring to proof records (the certified checkpoint's hash as a public value of the next aggregated proof; the verifier checks the certificate natively) asked by the brief buys light clients and bridges one object (certified and proven); changes nothing a full node does, since the proof chain already commits to block hashes and a reorg already needs new proofs; in-circuit BLS is 40+ hours and not worth it 6 (public-value commit) rollup customer and light client: one verification; others: nothing a light client verifies a proof carrying a certificate hash and the certificate
12 Prover attestations as a second finality leg (a lock also needs proof records from a quorum over the checkpoint) asked by the brief NOT recommended: provers are the miners (same vote keys), so no new party; proving covers 2.4% of blocks today and the pool is "not active" (reddit review 1.4), so every lock would wait on proofs and finality would pause constantly; what it would add (execution validity) rank 5 gives without the liveness cost 8 if ever every tier: lock latency becomes proof latency (20 to 60 s target, minutes today) only once coverage is 100% and rank 1 is in
13 Time-locked (vesting) weight asked by the brief NOT recommended: a bought key transfers vested weight, so K's bound is unchanged; honest new cohorts wait N days longer than G's 20 3 if ever new home miners: later vote; attacker: unchanged none
14 Any rule that keeps finality on after a large honest set leaves abruptly without a signed exit asked by the brief NOT possible safely: departure and partition are the same observation in one view; re-enabling locks under the frozen table reopens the double lock of L4 and M3 at the same day; the honest options are rank 8 (exit) and a shorter frozen expiry, which trades the partition bound one for one 0

One paragraph each on the two that matter most.

Rank 1, proof verification in consensus. Today a record is paid on a signature and a statement match; the statement is computable by every node, so a producer writes the right statement, random proof bytes and its own payout address into its own coinbase and is paid the shard or the aggregator share. The exclusive window limits it to the slots it is assigned (by weight, so H of them) for 10 DAA s; after that the first record carried wins, and the producer's block is first. The only thing that stops it is a verified proof as a condition of payment. SP1's light verifier exists (igneum-prove-host --mode verify-segment); the cost to measure is the per-proof verification time on the validation path, and if it is over a few tens of milliseconds the aggregated record (2 per block) is the one to verify in consensus while the per-shard record stays payout-only inside the window. Consequence per tier: an 8 GB home miner that proves on the patched prover is paid for what it proves; a pool's proving income is real; a holder's 20 percent of emission goes to proofs.

Rank 2, weight-gated deep fork choice. Finality's whole argument is that weight cannot be rented; fork choice today ignores weight, so during a pause or the first month a renter's heavier chain reorganises up to 12 hours. The rule: a candidate tip whose fork point is more than D of past-median time behind the node's selected tip is a candidate only if the keys that produced its chain blocks since the fork hold at least a third of the weight table at the fork block (the same voters_at the finality manager computes). It is deterministic (a function of the DAG), it leaves every reorg under D to GHOSTDAG as now, it leaves honest partition sides over a third exactly as now, and it makes the pause-time double spend cost the veto (20 days in public) instead of 12 hours of rent. What it costs: a side of a partition under a third of weight that is heavier by work cannot reorganise the other side past D at the heal, which is the outcome the certificate would have produced anyway; and a cold node with no table yet follows F5. Gate: the fast-time run in the table.

7. Open questions and what could not be run

item why
The star's effective relay delay the model needs the hub's measured relay time at 12 blocks/s to 41 peers; main's run A rows give the outcome (77% red, reorg 55) and this lane gives the curve (3.3); the fleet measures the delay
The finality sweep's R row at 90% the renter's 9x hash makes 905 honest keys fall under dust in the model (B's dust effect), so the simulated share overshoots the formula by 1 to 2 points, as B recorded
Proof verification time on the validation path not measured; P3 gives setup only; rank 1's hours depend on it
Weight-gated fork choice against the C4 certificate-driven reorg a certificate over a deep block must still force the reorg (3.5); the gate must exempt certified tips; not modelled
The DAG simulator has equal work per block, no difficulty, no bodies a withholder also controls its blocks' timestamps and the attack-side difficulty; the 10-s rules bound the clocks, the DAA lane bounds the rest
igneumd harness one s6 run on the 0.3.14 Mac binary (4.8); the node-side numbers for the other scenarios are the recorded runs cited
The live pause's end lane 3's arithmetic (4.1): the frozen table of lock 6842 expires at DAA 216,402, about 20:40Z, unless departed keys holding 9.4 points of it return first; main reads the chain

8. Summary paragraph

The ordering layer and the lock together bound a hash majority to the lock latency: the DAG simulator shows 45 to 51 percent winning the 90-second race 70 to 85 percent of the time and nothing beyond it, 34 percent winning only inside 60 s, 20 percent only the last k blocks; weight cannot be rented faster than 10 days per third, bought keys decay as the window slides, no equivocator under a third splits finality in any view, and the costs in rented hash are USD 6k to 5.8M for the veto and 17k to 17M to lock alone across 1 GH/s to 1 TH/s, of which the attacker earns back half as subsidy. Three things a hash majority does buy today: the proving pool, by writing fake records into its own blocks (the one line that earns more than it costs); a 12-hour proof-of-work double spend during a pause or the first month for USD 146 to 146k; and a pause needs no attacker at all, since a planned 43 percent departure caused tonight's and the frozen table holds it for a window (lane 3, 3.1). The three findings as numbered lines:

  1. A 51 percent withholder reorganises at most the lock latency (90 to 120 s; 32 to 46 chain blocks at 1 block/s, 80 percent success), reaches a weight ceiling of about 56 percent by red-flooding (never two thirds), and costs the honest side a quarter of its subsidy while it lasts (ghostdag_sim.py).
  2. The veto costs 20 days of 51 percent in public (USD 6k at 1 GH/s, 5.8M at 1 TH/s, half earned back) and then holds a pause for free; a pause-time 12-hour double spend costs USD 146 to 146k (cost_model.py, finality_horizon.py S and C); weight-gated deep fork choice (rank 2) makes it cost the veto instead.
  3. Any block producer captures from H to most of the 20 percent proving pool today with correct-statement fake records (11,636 IGN an hour at 51 percent), because consensus does not verify the proof (spec 07 7.7 item 4); proof verification in consensus is rank 1.

9. Files and how to run them

file run
sim/horizon/consensus-security/ghostdag_sim.py with-lock.sh run nice -n 19 python3 sim/horizon/consensus-security/ghostdag_sim.py --seeds 20 --out ghostdag_results_1bps.md; --bps 10 --k 124 --delays 0.35,0.67,2 --holds 10,30,60,90 --warm 60 --post 15 for the 10 bps grid
sim/horizon/consensus-security/finality_horizon.py with-lock.sh run nice -n 19 python3 sim/horizon/consensus-security/finality_horizon.py --out finality_horizon_results.md (imports sim/finality_v2.py)
sim/horizon/consensus-security/signalling.py python3 sim/horizon/consensus-security/signalling.py --out signalling_results.md
sim/horizon/consensus-security/cost_model.py python3 sim/horizon/consensus-security/cost_model.py --out cost_results.md
results ghostdag_results_1bps.md and .json, ghostdag_results_10bps.md and .json, finality_horizon_results.md, signalling_results.md, cost_results.md in the same directory