igneum/sim/horizon/consensus-security/finality_horizon_results.md
igneum-labs 2e405311a4 Horizon: lane 1 (consensus-security) lands, with the 51 percent paper
docs/analysis/51-percent.md: what a 51 percent attacker can and cannot do on Igneum, with
numbers (the selected-chain race over a 90-s hold, the lock as the reorder bound, the veto
at 1/3 of weight and its rental cost, the departure case and the LEAVE item, the p2p surface).
docs/analysis/horizon/consensus-security.md: the attack catalogue across GHOSTDAG ordering,
the difficulty rule, the finality weight, miner signalling, proof records, the exec layer and
p2p, each with the bound and the rental cost at the measured USD 11.7 per GH/s-hour; the
pruned-node unwrap class with its sibling list in the sync and IBD flows; fourteen ranked
defences, three of them not recommended with the reason.

Models and results: sim/horizon/consensus-security/ (ghostdag_sim.py, finality_horizon.py,
cost_model.py, signalling.py, result files).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 20:07:43 +00:00

7.1 KiB

Finality weight attacks over the adversary's share (20, 34, 51, 67, 90 percent)

Generated by sim/horizon/consensus-security/finality_horizon.py on 2026-10-06 19:42 UTC, importing sim/finality_v2.py unchanged; seeds 7,11; rule active/cert + floor 1.00 (a lock needs 66.7% of total); inter-region delay 2.0 s. Model assumptions and limits: sim/results_v2.md (no DAG, perfect retarget, keys free, 2.2% outage).

R. Renter with hash share A from a warm 30-day state, signing every checkpoint (32 days, seed 7)

Formula (spec 03 3.1, results_v2.md B): share(t) = (t/30) A; the veto (1/3) on day 10/A, locking alone (2/3) on day 20/A, never when A < 2/3.

attacker hash share A share day 10, sim / formula share day 20, sim / formula reaches 1/3 (veto), day sim / formula reaches 2/3 (locks alone), day sim / formula stalled checkpoints conflicting locks
20% 6.7% / 6.7% 13.3% / 13.3% never / > 32 never / never 0 0
34% 11.3% / 11.3% 22.6% / 22.7% 30.0 / 29.4 never / never 0 0
51% 17.0% / 17.0% 34.0% / 34.0% 20.0 / 19.6 never / never 0 0
67% 22.4% / 22.3% 44.7% / 44.7% 15.0 / 14.9 30.0 / 29.9 0 0
90% 30.0% / 30.0% 60.0% / 60.0% 12.0 / 11.1 23.0 / 22.2 0 0

S. Silent set holding A of weight stops signing and keeps mining, then resumes (seeds 7,11)

silent weight A hours picked first lock while silent, min stalled checkpoints locked while silent longest gap, min first lock after resume, min conflicting locks
20% 1 20% 0 0 100% 1 0 0
20% 6 20% 0 0 100% 1 0 to 0 0
34% 1 34% never 122 to 123 0% 60 0 0
34% 6 34% never 720 to 722 0% 360 0 to 0 0
51% 1 51% never 122 to 123 0% 60 0 0
51% 6 51% never 720 to 722 0% 360 0 to 0 0
67% 1 67% never 122 to 123 0% 60 0 0
67% 6 67% never 720 to 722 0% 360 0 to 0 0
90% 1 83 to 90% never 122 to 123 0% 60 0 0
90% 6 83 to 90% never 720 to 722 0% 360 0 to 0 0

Reading: at A >= 1/3 the pause lasts exactly as long as the silence (the floor needs two thirds of total signing), the first lock comes 0 minutes after the resume, and no row conflicts. A silent set that keeps mining keeps its subsidy, so the pause costs it nothing.

K. Bought keys worth A of the window, attacker mining at 30% of the network, 30 days (seeds 7,11)

Formula (spec 03 3.11.5): share(t) = A (1 - t/30) + 0.30 t/30. Sellers keep their rigs under fresh keys. 'signs' = the buyer votes; 'silent' = it withholds votes (the pause attack with bought weight).

bought weight A buyer picked peak share share at day 30 holds at least 1/3 (veto) stalled checkpoints of 86400 conflicting locks
20% signs 20% 30% 30% never 0 0
20% silent 20% 30% 30% never 816 to 1045 0
34% signs 34% 34% 30% day 1 to 6 0 0
34% silent 34% 34% 30% day 1 to 6 36334 to 41588 0
51% signs 51% 51% 30% day 1 to 24 0 0
51% silent 51% 51% 30% day 1 to 24 74391 to 75691 0
67% signs 67% 66% 30% day 1 to 26 0 0
67% silent 67% 66% 30% day 1 to 26 79408 to 79841 0
90% signs 83 to 90% 82 to 89% 30% day 1 to 27 0 0
90% silent 83 to 90% 82 to 89% 30% day 1 to 27 81759 to 82557 0

E. Poisoned eclipse: an attacker holding A of weight feeds a 20% pool a private fork for 2 h and signs both sides (seeds 7,11)

The eclipsed side holds 20% + A of total; the honest side 80% - A. Rule v2 (floor 2/3 of total) as results_v2.md L3; the floor binds whatever the presence window does. A = 90% is not run: the attacker alone is over two thirds and locks alone everywhere, the 20-day public event of spec 03 3.1.

attacker weight A eclipsed side holds conflicting locks first conflict, min locks on the eclipsed side (2 h) locks on the honest side (2 h) stalls in 3 h after the heal
20% 40% 0 never 0 141 to 149 0
34% 54% 0 never 0 108 to 110 0
51% 71% 67 to 70 2 172 to 173 67 to 70 0
67% 87% 30 to 32 4 to 5 208 to 209 30 to 32 0
90% 110% not run (attacker alone over 2/3) - - - -

P. 50/50 honest partition for 150 min with an equivocator holding A of total reaching both sides (seeds 7,11)

Each side holds (1 - A)/2 + A of total: 60% at A = 20%, 67% at 34%, 75.5% at 51%, 83.5% at 67%, 95% at 90%. Two conflicting certificates need two thirds each, so A >= 1/3 is the bound (spec 03 3.11.2). v2 = the rule as specified with view-local weights; v3 = plus the frozen table (F21).

equivocator A each side holds rule conflicting locks first conflict, min first lock per side, min every pre-heal lock kept first lock after heal, min stalls in 3 h after
20% 60.0% v2 0 never never / never yes 0 0
20% 60.0% v3 0 never never / never yes 0 0
34% 67.0% v2 21 to 70 14 to 77 11 to 48 / 0 to 76 yes 0 0
34% 67.0% v3 21 to 69 14 to 78 12 to 50 / 0 to 77 yes 0 0
51% 75.5% v2 299 to 300 0 0 / 0 yes 0 to 0 0
51% 75.5% v3 299 to 300 0 0 / 0 yes 0 to 0 0
67% 83.5% v2 301 0 0 / 0 yes 0 to 0 0
67% 83.5% v3 301 0 0 / 0 yes 0 to 0 0
90% 95.0% v2 293 to 300 0 0 to 0 / 0 to 0 yes 0 to 0 0
90% 95.0% v3 293 to 300 0 0 to 0 / 0 to 0 yes 0 to 0 0

C. Abrupt departure (tonight's pause): keys holding A of weight stop mining AND signing at once; survivors inherit the block supply (31 days, seeds 7,11)

v2 analytic: the survivors hold 1 - A (30 - t)/30 of the sliding table on day t and reach two thirds on day 30 (1 - 1/(3A)) (never for A <= 1/3). v3: the frozen table at the last certified checkpoint holds the departed keys until it expires one window after that checkpoint, so the first lock comes on day 30 whatever A (results_v2.md M4). On the devnet the window is 7,200 DAA (2 h), so divide the days by 360.

departed weight A rule picked first lock after the departure, days analytic stalled checkpoints live share of total at the end conflicting locks
20% v2 20% 0.00 never 39 to 213 100% 0
20% v3 20% 0.00 never 39 to 213 100% 0
34% v2 34% 0.77 to 0.85 0.6 4265 to 4727 100% 0
34% v3 34% 30.00 30.0 86395 to 86472 100% 0
51% v2 51% 10.46 to 10.52 10.4 31312 to 31739 100% 0
51% v3 51% 30.00 30.0 86326 to 86336 100% 0
67% v2 67% 15.15 to 15.24 15.1 44464 to 45047 100% 0
67% v3 67% 30.00 30.0 86361 to 86411 100% 0
90% v2 83 to 90% 17.92 to 18.88 18.9 51897 to 55628 100% 0
90% v3 83 to 90% 30.00 to 30.00 30.0 86340 to 86481 100% 0

(592 s in all)