igneum/docs/analysis/51-percent.md
igneum-josh eec2cd75ff 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 21:07:43 +01:00

15 KiB

What a 51 percent attacker can and cannot do on Igneum

6 October 2026. For the litepaper and the ledger. Written for a reader who maintains Monero or Kaspa and does not take a finality claim on trust. Every number names its model or its measurement; "approximate" marks the rest. Models and runs: sim/horizon/consensus-security/ (ghostdag_sim.py, finality_horizon.py, cost_model.py, signalling.py), the finality simulator sim/finality_v2.py with sim/results_v2.md, the specification docs/spec/02-consensus.md and 03-finality.md. The long form is docs/analysis/horizon/consensus-security.md.

Price basis: USD 11.7 per GH/s-hour, measured on rented pods on 6 October 2026 (docs/bench-log.md, "Rental cost of hash": 1,748 MH/s for USD 20.44 an hour; the market supplied no more than about 2 GH/s that evening, so every figure above that is a list-price extrapolation). An attacker holding share A of the total hash rents A/(1 - A) times the honest network N.

1. What 51 percent buys, and at what price

Igneum orders blocks by GHOSTDAG (a rusty-kaspa fork, k = 18 at 1 block/s) and locks a checkpoint every 30 blue blocks when two thirds of the trailing 30 days of blue blocks, per vote key, have signed it (spec 03, Q3). The lock lands 63 to 93 s after the checkpoint block (determined 60 blue score later, certified about 3 s after; sim/results_v2.md A).

A majority of hash buys the ordering race up to that lock. The DAG simulator (ghostdag_sim.py, GHOSTDAG as vendor/igneum-node/consensus/src/processes/ghostdag/protocol.rs runs it, 20 seeds per cell, one-way delay 0.67 s = the cloud devnet's p99 propagation) gives, at 1 block/s:

attacker share hold 30 s hold 60 s hold 90 s hold 120 s
20% wins 10%, reorg median 0 / max 18 0%, 0 / 1 0%, 0 / 1 0%, 0 / 1
34% 40%, 0 / 18 40%, 0 / 36 5%, 0 / 39 10%, 0 / 52
51% 85%, 10 / 15 85%, 20 / 28 80%, 32 / 44 80%, 42 / 53
67% 100%, 8 / 13 100%, 16 / 23 100%, 26 / 31 100%, 34 / 41

"wins" = the private chain became the honest selected chain on release; "reorg" = chain blocks removed. The arithmetic: a private chain merges honest blocks as blue until its first block has k in its anticone, then every later honest block is red in it; it wins when its R blocks plus k exceed the honest N_h, always at or above 50 percent, below it only for holds under k A / ((1 - 2A) lambda) seconds (6 s at 20%, 56 s at 34%). Withholding longer than the lock latency is useless: the first checkpoint inside the hold locks at most 93 s after it is mined, and a chain missing a certified checkpoint is not a fork-choice candidate (spec 03 F1). Bound: the lock latency, 90 to 120 s. Price of the 90-s race at 51 percent: USD 0.30 at 1 GH/s, USD 304 at 1 TH/s (cost_model.py). What it earns: a deposit credited before its lock, which the four-state rule forbids (ledger P17: a wallet shows included, executed, proven, finalised and credits on the last).

Repeated 60-s withholding at 51 percent turns 26 to 28 percent of honest blocks red; a red block's subsidy goes to its merger (spec 02 2.5), so the majority takes about a quarter of honest income and lifts its weight share to about 56 percent (approximate), reorganising the chain every minute in public. It does not reach two thirds.

The proving pool is the one place 51 percent earns more than it spends today. Consensus checks a proof record's signature and its statement against the node's own execution, and not the proof (spec 07 7.7 item 4, 7.8 item 8; ledger P21). A producer who writes the correct statement, random proof bytes and its own payout address into its own block is paid the shard; at 51 percent of blocks that is at least 51 percent of the 20 percent pool (11,636 IGN an hour at full subsidy) and, since its fake rides its next block while an honest proof takes 9 to 11 s on a 5090, most of the shards outside the 10-s exclusive window. Proof verification in consensus closes it; it is the first item in section 5.

2. What it cannot do, and why

it cannot because measured
Reverse a certified checkpoint a candidate tip must pass through every certified checkpoint (F1); two certificates at one index need two thirds of total weight each, 4/3 in all, so a conflicting pair needs an equivocator holding a third of 30 days of blocks (spec 03 3.11.2) 0 conflicting locks under 1/3 in every partition and eclipse seed; conflicts from 34% (sim/results_v2.md H, I, L3, M5; finality_horizon.py P and E)
Reach two thirds of weight with 51 percent of blocks weight is blue blocks over a flat 30-day window: share = (t/30) A, ceiling A; 51% holds 51% on day 30 and never more while honest miners mine the formula holds to 0.1 day (results_v2.md B, finality_horizon.py R); red-flooding lifts it to about 56% (approximate)
Buy weight faster than mining it a pulsed rental buys 0.26 blocks per hash under the controller (sim/difficulty/attacks scenario 2); 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, finality_horizon.py K: keys worth 51% hold the veto to day 24 and are worth 30% on day 30
Forge state every full node executes natively and ignores a record whose statement differs from its own execution (the veto, spec 07 7.2 item 5); a soundness bug is a light-client problem (P7) the exec-attacks suite, 96 of 97 checks on the shipped node (docs/review/redteam-2026-10-04.md row 28)
Change a rule code activates when 95% of a day's blue blocks signal it, with a floor height as backstop (P2, docs/plans/counter-asic-3-node.md section 6); the signal has 0.07 points of noise over 86,400 blocks, so 94.9% never flips and 95.1% flips on day one signalling.py; the fast-time gate's three cases and its failed case
Grind the hourly program the epoch seed is a 10-minute class-group VDF of a checkpoint block fixed 20 minutes before the epoch; withholding a block to pick a program has expected gain 0 against a 300x evaluator margin (spec 04 4.1, 4.6) proto-vdf Monte Carlo over 2,000,000 epochs
Stretch the clocks a header is at most 10 s ahead of the clock and 10 s behind its parent; a sanitised clock pays a forgery back a 50% forger drifts the rate +0.4 to +1.1% (M23 fixed, sim/difficulty/attacks/README.md)

3. The table: capability against share, time, cost and earnings

capability share time rent at 1 GH/s at 100 GH/s at 1 TH/s subsidy it earns meanwhile (IGN) net
Reorder the last k blocks any 20 s USD 0 2 24 0 a loss
Win the lock-latency race (90 s) 45 to 51% 90 s USD 0.3 30 304 1k nothing credited under a lock
Reach the veto (1/3 of weight): pause finality at will 51% 20 days USD 6k 585k 5.8M 22M the attacker earns 51% of it back
the same 90% 11.1 days USD 28k 2.8M 28M 22M
Lock alone (2/3 of weight): certify any chain forward 67% 30 days USD 17k 1.7M 17M 44M 33% of the period's subsidy at equilibrium
the same 90% 22.2 days USD 56k 5.6M 56M 44M
Hold a pause once the veto is held 1/3 for ever 0 marginal 0 0 keeps earning free
12-h double spend during a pause or the first 30 days 51% 12 h USD 146 15k 146k 559k the deposit
Orphan an hour of honest blocks beyond merge depth (pause only) 51% 1.5 h USD 18 2k 18k 70k the honest hour's subsidy, lost to all
Private DAG heavier over the window (cold-start node, no certificate) 51% 30 days USD 9k 877k 8.8M 34M one fresh node misled
Capture the proving pool with fake records any producer continuous 0 extra 0 0 up to 20% of emission the one positive line
Block a rule change 6% per day until the floor USD 18 1.8k 18k 6% of subsidy about zero
Pause finality by taking the two hands down (tonight's topology) 0 hash a DoS 0 free

At the rental-market equilibrium, where hash joins until rent equals subsidy (39, 156 and 780 GH/s at IGN prices of USD 0.005, 0.02 and 0.10, the three inputs of docs/analysis/security-budget.md, not predictions), the veto nets about 48 percent of 20 days of the chain's subsidy and locking alone about 33 percent of 30 days (cost_model.py section 3).

4. The residual risks, plainly

  1. The pause. Finality pauses whenever less than two thirds of 30-day weight is connected and signing, and the chain runs on proof of work with a 12-hour depth meanwhile (spec 03 3.7 item 2, 3.9). A third of weight holds the pause for nothing once it has it (finality_horizon.py S: 0 locks for the whole silence at 34 to 90 percent, 0 conflicts). During a pause a 51 percent miner is a 51 percent miner on any proof-of-work chain, with the 12-hour depth and USD 146 at 1 GH/s to buy it. What we are building against it is in section 5.

    The departure case (not an attack, the same pause): tonight on the live devnet 20 keys holding 42.7 percent of the frozen weight table stopped mining within three minutes (a rehearsal job), the signing weight fell to 53.1 percent at checkpoint 6843 and finality paused at 18:39:40Z; rule v2 would have locked again after 35 minutes as the departed blocks aged out of the sliding table, and the frozen table (Q5, the live rule) holds the pause for one window, 2 hours there and 30 days on mainnet; locks had formed with the hand nodes down, so it was weight, not topology (docs/analysis/horizon/finality-and-weight.md 3.1 and 4.1; finality_horizon.py C: 51 percent leaving pauses 10.5 days under v2, 30.0 under v3). A sudden exit of a third or more of weight, by a price crash or a hosting failure, does the same. What an attacker can do during it is exactly the pause line: proof of work with a 12-hour depth, nothing against any certificate, no new lock to forge; what it cannot do is end the pause early or lock alone, since the departed weight is still in the denominator. The fix is a signed departure (LEAVE, section 5): a key that announces it is leaving is out of every denominator one hour later, which a partitioned key cannot fake.

  2. The first 20 to 30 days. No lock forms before the window holds 30 days of history (spec 03 3.8, min_daa = window): the first month is proof of work with the 12-hour depth, by design, and the renter's day counts start at genesis. On day 30 an attacker producing share s of blocks from day k holds s (31 - k)/30 of the window: 75 percent from day 2 locks alone on day 30 (ledger F1).

  3. Sybil of keys buys nothing; buying keys buys their blocks. Weight is blue blocks and every draw is by weight (W6; harness s2). A pool's key with its history can be sold or stolen and is worth exactly its 30 days of blocks, decaying linearly as the window slides (K); the sellers' price, not hash, is the limit, and a seller who keeps a copy strips the buyer by equivocating.

  4. Two thirds of total under churn. The denominator is every key's blocks in the window. If half the honest miners leave, a miner at 51 percent of the old hash holds 51/(51 + 24.5) = 67.5 percent of the window after 30 days and locks alone (spec 03 3.7 item 4: "same as Bitcoin, with a month's warning"). A departure that stops mining and signing at once pauses finality 30 (1 - 1/(3A)) days under rule v2 and until the frozen table expires, 30 days after the last lock, under rule v3 (finality_horizon.py C), because a view cannot tell a departure from a partition.

  5. A partition longer than a window forks finality. Each side fills its own table; under v3 neither side under two thirds locks for 30 days after the last common certificate, then both lock alone at once (results_v2.md M3) and an operator's trusted certificate resolves it (3.11.4). Under a third of weight, no equivocator shortens that.

  6. The proving pool. Section 1's fake-record capture, until proofs are verified in consensus.

5. What we are building next

rank defence what it closes cost liveness cost
1 Proof verification in consensus: a record whose aggregated proof does not verify against the pinned key is invalid the pool capture of section 1 8 to 12 hours; per-record verify time to measure none
2 Weight-gated deep fork choice: a tip forked more than D (about 10 min) back is a candidate only if the keys that built it hold a third of the weight table at the fork the pause-time and first-month deep reorg: rented hash has no weight for 10 days, so a 12-h double spend needs the 20-day veto 10 to 16 hours none for certificates; a sub-third partition side cannot reorg the other past D, which is the intended outcome
3 Vote-or-burn: a block whose key has participation under 0.5 in its own past burns 20 percent of its producer share the free pause: silence then costs 7,757 IGN an hour at 34 percent 6 to 8 hours none; partition-safe by construction
4 Peer floor and mesh: every box dials three others beside the hands; the node alarms under three peers or two checkpoints without a vote; no unwrap on any peer-driven sync path (a pruned node was crashed by one request tonight) a star fleet pausing on one host; one request crashing any pruned node 5 to 7 hours + 4 none
5 The vote signs the execution root too snapshot poisoning, and "ordered and executed" in one certificate 8 to 12 hours lock latency plus executor lag
6 Signalling over 7 consecutive daily windows, the floor a week past the publish a one-day renter forcing a flip 3 hours a week's latency on class changes
7 LEAVE, the signed departure (lane 3's rank 1): 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; F5's trusted certificate implemented tonight's 30-day-scale pause after a planned departure; sim: first lock 1 h after a 34 to 50 percent departure, 0 conflicts in every partition row (finality-and-weight.md 6) 6 + 4 hours none
8 A client-shipped certified checkpoint the cold-start private DAG 3 hours none

Not adopted: prover attestations as a finality leg (the provers are the miners, coverage is a few percent, every lock would wait on a proof); vesting weight (a bought key transfers vested weight); any automatic rule that keeps locks going after an abrupt departure (a view cannot tell it from a partition: results_v2.md L4, M3).

The honest sentence for the litepaper: a hash majority on Igneum can reorder about two minutes, can buy a veto over finality in twenty public days and then pause it for free, can double-spend at a 12-hour depth only while finality is paused or in the first month, and today can take the proving pool without proving; it cannot reverse a certificate, cannot reach two thirds of weight while honest miners mine, cannot forge state, and cannot change a rule without 95 percent of a day's blocks.