93 KiB
Finality rule V2: checkpoint-level simulation with latency, partitions and eclipses
Date: 3 October 2026. Simulator: sim/finality_v2.py, seed 7. Seed 11 was run for B and E and reproduces every crossing day in B and the partition pattern in E (its figures are quoted where they differ). Every number below was produced by the simulator. Nothing is from memory.
The rule as simulated
| Element | As simulated |
|---|---|
| Vote weight | a key's blue blocks over the trailing 30 days (720 hourly buckets), no damping |
| Dust | a key under 100 blocks in the window is not a voter and is in no denominator |
| Checkpoint | one per 30 blocks of blue score; every online voter signs every checkpoint it sees |
| Lock | signatures with weight at or above 2/3 of the denominator |
| ACTIVE denominator | sum of weight x participation; participation = min(1, certified votes over the last 240 checkpoint indices / 240); a key whose first block is under 240 checkpoints old counts 1 |
| TOTAL denominator | sum of weight over every key above dust, no factor |
| Equivocation | a key that signed two different checkpoint blocks at one index loses its weight for the rest of the run once the evidence is seen (at the heal) |
Three readings of "participation" differ only when a checkpoint index gets no certificate. All three are run where it matters.
| Reading | An uncertified index ... | Property |
|---|---|---|
| cert (the brief, literal) | credits nobody, so a stall decays everyone's participation | objective (from certificates), self-healing, shrinks the denominator during any stall |
| seen | credits the keys whose votes the node saw | silent keys decay, present keys do not; not objective, each node counts its own view |
| frozen | is skipped, the window is over certified indices only | fails safe, never recovers liveness within the window |
Model assumptions (apply to every table)
| Assumption | Value |
|---|---|
| Time step | one 30-s slot; 2,880 slots a day |
| Blocks | Poisson(30) per slot across the network, split per key by hashrate share (perfect retarget), every block blue |
| Checkpoint cadence in a partition | each side forms a checkpoint per 30 of its own blocks, so a side with share s forms s checkpoints per slot (no retarget during the partition unless marked '+daa', where each side retargets to 1 block/s at once) |
| Honest network | 1,000 keys, Pareto shape 1.0: top key 17.1%, top 10 keys 49.8%, bottom 500 keys 8.5% |
| Regions | 3, holding 45% / 35% / 20% of honest hashrate (model assumption), one-way delay 2 s between regions (0.5 and 5 s swept in A), 0.1 s inside a region, lognormal jitter sigma 0.25 per hop |
| Vote path | checkpoint block at t0; a voter in region r sees it at t0 + d(miner region, r); its vote reaches the aggregator in region a at + d(r, a); one aggregator per region on a side; the certificate forms at the first aggregator to reach quorum |
| Certificate signers | every vote that reached that aggregator by max(quorum time, t0 + 15 s grace) |
| Uptime | two-state chain per honest key: 97% availability for keys under 1% of hashrate, 99.5% for pools at or above 1%, mean outage 10 minutes; attackers and the eclipsed pool are always online |
| Partition | sides have separate views (certificates, participation ring, blue score); at the heal certificates are unioned, two certificates at one index with different blocks are a conflicting lock, rings are OR-merged by index, equivocators are stripped |
| Weights in a partition | global (a side does not see the other side's blocks for at most 150 minutes of a 30-day window; ignored) |
| Warm start | B to G start from a 30-day steady state (Poisson-filled window, participation 1), then run 1 to 3 hours before the event |
Minutes in C to F are wall minutes after the event. "Stalled" counts checkpoint indices that never got a certificate.
A. Steady state
1,000 honest keys from zero history, 60 days, both denominators.
| day | total weight / full window | keys under dust (100 blocks) |
|---|---|---|
| 1 | 3.3% | 905 |
| 2 | 6.7% | 781 |
| 5 | 16.7% | 463 |
| 10 | 33.4% | 15 |
| 20 | 66.7% | 0 |
| 30 | 100.0% | 0 |
| 60 | 100.0% | 0 |
Weight against hashrate at day 60 (identical under both denominators, they mine the same blocks):
| corr(hash, weight) | Gini hash / weight | top-1 hash / weight | top-10 hash / weight | bottom-500 hash / weight | min / max weight:hash | keys under 0.9x | dust keys |
|---|---|---|---|---|---|---|---|
| 1.00000 | 0.761 / 0.761 | 17.1% / 17.0% | 49.8% / 49.8% | 8.5% / 8.5% | 0.818 / 1.124 | 11 | 0 |
Lock latency, seconds from the checkpoint block to quorum, inter-region delay 2 s:
| run | checkpoints | locked in slot 0 | slot 1 | slot 2+ | stalled | median s | p99 s | max s | min signed/denominator |
|---|---|---|---|---|---|---|---|---|---|
| active, days 0 to 1 | 2,887 | 2,870 | 0 | 0 | 17 | 2.5 | 4.6 | 5.7 | 0.733 |
| active, days 1 to 30 | 83,553 | 83,553 | 0 | 0 | 0 | 2.5 | 4.6 | 6.0 | 0.795 |
| active, days 30 to 60 | 86,443 | 86,443 | 0 | 0 | 0 | 2.5 | 4.6 | 6.1 | 0.782 |
| total, days 0 to 1 | 2,887 | 2,870 | 0 | 0 | 17 | 2.5 | 4.6 | 5.7 | 0.733 |
| total, days 1 to 30 | 83,553 | 83,553 | 0 | 0 | 0 | 2.5 | 4.6 | 6.0 | 0.767 |
| total, days 30 to 60 | 86,443 | 86,443 | 0 | 0 | 0 | 2.6 | 4.6 | 6.1 | 0.763 |
Delay sweep, warm-started, active denominator, 3 days each. Last column: weight-averaged participation per region.
| delay | checkpoints | locked in slot 0 | stalled | median s | p99 s | max s | min signed/denominator | participation r0 / r1 / r2 |
|---|---|---|---|---|---|---|---|---|
| 0.5 s | 8,682 | 8,682 | 0 | 0.7 | 1.2 | 1.4 | 0.832 | 0.981 / 0.981 / 0.980 |
| 2.0 s | 8,682 | 8,682 | 0 | 2.5 | 4.6 | 5.7 | 0.832 | 0.981 / 0.981 / 0.980 |
| 5.0 s | 8,682 | 8,682 | 0 | 6.2 | 11.5 | 14.1 | 0.832 | 0.981 / 0.981 / 0.964 |
Interpretation. Without damping, weight is hashrate: correlation 1.00000, Gini and top-k shares equal to three figures, and the weight:hash ratio stays within 0.82 to 1.12 (Poisson noise on the smallest keys, 11 of 1,000 under 0.9x). The network holds full weight on day 30, a week earlier than the damped rule (day 32 to 41 in results.md). Every key is above dust by day 20; the smallest keys (about 11 blocks a day) need 9 to 10 days. The only stalls are 17 checkpoints at genesis (8.5 minutes) before any key has 100 blocks. Lock latency is two message hops: median 2.5 s and p99 4.6 s at a 2-s delay, 14 s worst case at 5 s, always inside the 30-s slot. Zero stalls over 60 days under either denominator. At a 5-s delay the slowest region's votes start to miss the 15-s certificate grace (participation 0.964 against 0.981), a first sign that the grace must scale with real-world delay. The remaining 2% participation shortfall is the uptime model.
B. Rental burst
At day 60 one new key appears with a times the honest hashrate, mines publicly and signs every checkpoint.
Attacker weight share, simulated / formula (t/30) x a/(1+a):
| day after burst | a=1 (50% of hashrate) | a=2 (67%) | a=4 (80%) | a=9 (90%) |
|---|---|---|---|---|
| +1 | 1.7% / 1.7% | 2.2% / 2.2% | 2.7% / 2.7% | 3.0% / 3.0% |
| +5 | 8.3% / 8.3% | 11.1% / 11.1% | 13.4% / 13.3% | 15.0% / 15.0% |
| +10 | 16.7% / 16.7% | 22.2% / 22.2% | 26.7% / 26.7% | 30.0% / 30.0% |
| +15 | 25.0% / 25.0% | 33.3% / 33.3% | 40.0% / 40.0% | 45.0% / 45.0% |
| +20 | 33.4% / 33.3% | 44.4% / 44.4% | 53.4% / 53.3% | 60.0% / 60.0% |
| +25 | 41.7% / 41.7% | 55.5% / 55.6% | 66.7% / 66.7% | 75.5% / 75.0% |
| +30 | 50.0% / 50.0% | 66.7% / 66.7% | 80.8% / 80.0% | 91.3% / 90.0% |
| +35 | 50.0% / 50.0% | 66.7% / 66.7% | 80.8% / 80.0% | 91.3% / 90.0% |
| event | a=1 | a=2 | a=4 | a=9 |
|---|---|---|---|---|
| crosses 1/3 (honest alone can no longer lock), simulated day | 20.0 | 15.0 | 12.5 | 11.1 |
| crosses 1/3, formula 10(1+a)/a | 20.0 | 15.0 | 12.5 | 11.1 |
| crosses 2/3 (locks alone), simulated day | never | 30.0 | 25.0 | 22.2 |
| crosses 2/3, formula 20(1+a)/a | never (ceiling 50%) | 30.0 | 25.0 | 22.2 |
| max abs deviation from the formula, days 1 to 30, points | 0.04 | 0.04 | 0.85 | 1.28 |
| stalled checkpoints after the burst | 0 | 0 | 0 | 0 |
Seed 11 gives the same crossing days to one decimal.
Interpretation. The review's formula holds: share(t) = (t/30) x a/(1+a) to 0.04 points for a = 1 and 2, and the crossing days are exactly 10(1+a)/a and 20(1+a)/a. The headline in CLAUDE.md (10 days to 1/3, 20 days to 2/3 with unbounded hashrate) is the a -> infinity limit; a 9x renter needs 11.1 and 22.2 days. A renter at 2x reaches 2/3 only at day 30, exactly at the ceiling, so in practice an attacker needs more than 2x the honest hashrate for 25 days or more to lock alone. The 0.85 and 1.28 point overshoots at a = 4 and 9 are the dust threshold: when honest keys mine at a fifth or a tenth of their former rate, the smallest ones fall under 100 blocks in the window and leave the denominator, which hands the attacker about one extra point. A liveness note: from the 1/3 crossing the renter can veto every lock, and because it keeps signing here the chain never stalls; a renter that stops signing at that point is scenario C.
C. Silent set
At day 60 (hour 3 of the run) a random set holding x of weight stops signing and keeps mining. Time from the event to the first lock, and checkpoints stalled in the run (6 hours for the first three columns, 72 hours for the rest).
| silent weight | active/cert | active/seen | active/frozen | active/cert, presence 2,880 | active/cert + floor 0.80 | active/cert + floor 0.85 | total |
|---|---|---|---|---|---|---|---|
| 34% | 0 min, 0 stalled | 0 min, 0 stalled | 0 min, 0 stalled | 20 min, 99 stalled | 0 min, 0 stalled | 0 min, 0 stalled | never (3.0 d), 8,666 stalled |
| 40% | 13 min, 26 stalled | 28 min, 57 stalled | never (6 h), 720 stalled | 2.6 h, 325 stalled | 13 min, 47 stalled | 13 min, 138 stalled | never (3.0 d), 8,666 stalled |
| 45% | 20 min, 41 stalled | 46 min, 93 stalled | never (6 h), 720 stalled | 4.4 h, 535 stalled | 20 min, 455 stalled | never (3.0 d), 8,666 stalled | never (3.0 d), 8,666 stalled |
| 50% | 29 min, 59 stalled | 62 min, 125 stalled | never (6 h), 720 stalled | 6.0 h, 736 stalled | never (3.0 d), 8,666 stalled | never (3.0 d), 8,666 stalled | never (3.0 d), 8,666 stalled |
| 55% | 38 min, 79 stalled | 72 min, 144 stalled | never (6 h), 720 stalled | 7.9 h, 955 stalled | never (3.0 d), 8,666 stalled | never (3.0 d), 8,666 stalled | never (3.0 d), 8,666 stalled |
Active/cert detail. Predicted stalls = presence x (p0 - 1.5 x online signing share), p0 = weight-averaged participation at the event.
| silent weight | keys | online signing share at event | p0 | predicted stalls | first lock | stalled | silent participation at end | signed/denominator at end | active/total at end |
|---|---|---|---|---|---|---|---|---|---|
| 34% | 535 | 65.6% | 0.978 | 0 | 0 min | 0 | 0.000 | 1.008 | 0.650 |
| 40% | 505 | 59.5% | 0.978 | 20 | 13 min | 26 | 0.000 | 1.006 | 0.589 |
| 45% | 519 | 54.5% | 0.978 | 38 | 20 min | 41 | 0.000 | 1.003 | 0.542 |
| 50% | 566 | 49.6% | 0.978 | 56 | 29 min | 59 | 0.000 | 1.005 | 0.493 |
| 55% | 646 | 44.6% | 0.978 | 74 | 38 min | 79 | 0.000 | 1.004 | 0.445 |
Interpretation. Under the total denominator a silent set is fatal: the silent keys keep mining, so their weight never ages out, and 34% silent blocks every lock for as long as they stay silent (8,666 stalls in 3 days, and by the arithmetic for the whole 30-day window and beyond). Under the active denominator with the literal (cert) reading the stall is minutes: 0 at 34% (the network's resting participation of 0.978 already leaves 65.6% of online signers above 2/3 of the active weight), 13 min at 40%, 29 min at 50%, 38 min at 55%. The mechanism is the one the formula states: every stalled index lowers everyone's participation by 1/240, so the denominator shrinks until the present signers reach 2/3 of it; after the first lock the silent keys fall to participation 0 within 2 hours and the signers lock with a ratio of 1.0. The same mechanism is what makes the partition in E unsafe. The seen reading takes about twice as long (silent keys decay, signers do not) and the frozen reading never recovers, like total. A 24-hour presence window multiplies the stall by 12 (2.6 h at 40%, 6 h at 50%). The floor hybrid at 0.80 keeps the minute-scale recovery up to 45% silent (455 stalls there: the margin is one pool outage thin) and stalls for the window at 50% and above, which is the price of the partition safety it buys in E. At 0.85 (a lock needs 56.7% of total) liveness ends between 40% silent (13 min, with 138 intermittent stalls in 3 days) and 45% (never).
D. Churn
At day 60 (hour 3) a random set holding x of weight stops mining and signing. Survivors inherit the whole block supply (perfect retarget).
| churn weight | keys | denominator | first lock after the event | stalled checkpoints | stalled after first lock | live share of total weight at end of run |
|---|---|---|---|---|---|---|
| 35% | 395 | active/cert | 2 min | 4 | 0 | 68.5% (3 days) |
| 35% | 395 | active/cert, presence 2,880 | 40 min | 109 | 25 | 68.5% |
| 35% | 395 | active/cert + floor 0.80 | 2 min | 102 | 98 | 68.5% |
| 35% | 395 | active/cert + floor 0.85 | 2 min | 102 | 98 | 68.5% |
| 35% | 395 | total | 41.0 h | 6,446 | 1,518 | 68.5% |
| 50% | 566 | active/cert | 31 min | 106 | 45 | 70.0% (12 days) |
| 50% | 566 | active/cert, presence 2,880 | 6.1 h | 862 | 140 | 70.0% |
| 50% | 566 | active/cert + floor 0.80 | 2.2 d | 7,244 | 1,033 | 70.0% |
| 50% | 566 | active/cert + floor 0.85 | 4.1 d | 12,823 | 1,006 | 70.0% |
| 50% | 566 | total | 10.1 d | 30,307 | 1,180 | 70.0% |
Analytic, perfect retarget: live share of total weight on day t = 1 - x(30 - t)/30, so the total denominator recovers at t = 30(1 - 1/(3x)): never for x at or under 1/3, day 1.4 at 35%, day 10 at 50%.
Interpretation. Under total the stall is days: 41 hours at 35% churn (the analytic 34 hours plus the uptime shortfall, then 1,518 intermittent stalls while the margin stays thin) and 10.1 days at 50%, as the first simulation found. Under active/cert it is minutes: 2 min at 35%, 31 min at 50%, with a tail of intermittent stalls (45 at 50%) while the live keys' participation recovers and the dead keys' decays over the next 2 hours. A 24-hour presence window stretches that to 40 min and 6.1 h. The floor at 0.80 removes most of the gain at 50% churn (2.2 days: a lock needs 53.3% of total, which the survivors only hold once 10 points of dead weight have aged out) and at 35% it stalls 98 times in 3 days because the 17% pool's outages take the live online share under 53.3%. At 0.85 the 50% churn stall is 4.1 days, between the 2.2 days of 0.80 and the 10.1 days of total; 35% churn is unchanged (2 min, 98 intermittent stalls). Not modelled: the real DAA takes of the order of an hour (approximate) to restore 1 block/s after half the hashrate leaves, which slows the checkpoint clock and so the presence decay by the same factor for that hour.
E. Partition
Honest weight split across sides for 30, 90 or 150 minutes, then healed. Attacker = one equivocating key holding the stated share of total weight, mining on the first side and signing every side's checkpoints. A conflicting lock is two certificates at one index with different blocks. Pass criterion: zero conflicting locks at 0% attacker for every duration under the 2-hour presence window.
Conflicting locks, with the minute of the first one:
| honest split | attacker | partition min | active/cert | active/seen | total |
|---|---|---|---|---|---|
| 50/50 | 0% | 30 | 0 | 0 | 0 |
| 50/50 | 0% | 90 | 32 (first at 60) | 0 | 0 |
| 50/50 | 0% | 150 | 90 (first at 60) | 31 (first at 118) | 0 |
| 50/50 | 34% | 30 | 19 (first at 2) | 19 (first at 2) | 2 (first at 24) |
| 50/50 | 34% | 90 | 59 (first at 2) | 59 (first at 2) | 2 (first at 24) |
| 50/50 | 34% | 150 | 98 (first at 2) | 98 (first at 2) | 2 (first at 24) |
| 33/33/34 | 0% | 30 | 0 | 0 | 0 |
| 33/33/34 | 0% | 90 | 0 | 0 | 0 |
| 33/33/34 | 0% | 150 | 0 | 0 | 0 |
| 33/33/34 | 34% | 30 | 0 | 0 | 0 |
| 33/33/34 | 34% | 90 | 2 (first at 88) | 0 | 0 |
| 33/33/34 | 34% | 150 | 53 (first at 88) | 0 | 0 |
Minutes after the split until each side's first lock (side order as in the split), and stalls in the 3 hours after the heal (0 in every run):
| honest split | attacker | active/cert | active/seen | total |
|---|---|---|---|---|
| 50/50 | 0% | 60 / 59 | 118 / 112 | never / never |
| 50/50 | 34% | 1 / 1 | 1 / 1 | 12 / 1 |
| 33/33/34 | 0% | never / never / never | never / never / never | never / never / never |
| 33/33/34 | 34% | 36 / 88 / 85 | 80 / never / never | never / never / never |
Seed 11: 50/50 at 0% attacker locks on both sides at 64 to 68 min under cert and 117 to 122 under seen; 33/33/34 with the attacker at 38 / 94 / 90 min under cert; total gives 0 conflicts at 30 and 90 min and 2 at 150 min for 50/50 with the attacker.
Supplementary, 0% attacker, more splits, 150 and 360 min. Cell = conflicting locks; minutes to each side's first lock. '+daa' = each side retargets to 1 block/s at once, the worst case for the presence clock. '+floor' = the active denominator is never below that fraction of total weight (a lock needs at least 53.3% of total at 0.80, 56.7% at 0.85).
| honest split | min | active/cert | active/seen | total | active/cert+daa | active/seen+daa | cert+floor0.80+daa | cert+floor0.85+daa |
|---|---|---|---|---|---|---|---|---|
| 50/50 | 150 | 90; 60 / 59 | 31; 118 / 112 | 0; never / never | 240; 30 / 30 | 184; 60 / 56 | 0; never / never | 0; never / never |
| 50/50 | 360 | 297; 60 / 59 | 238; 118 / 112 | 0; never / never | 658; 30 / 30 | 602; 60 / 56 | 0; never / never | 0; never / never |
| 60/40 | 150 | 23; 19 / 121 | 0; 44 / never | 0; never / never | 204; 13 / 47 | 141; 32 / 77 | 0; 13 / never | 0; 13 / never |
| 60/40 | 360 | 193; 19 / 121 | 131; 44 / 198 | 0; never / never | 624; 13 / 47 | 561; 32 / 77 | 0; 13 / never | 0; 13 / never |
| 67/33 | 150 | 0; 4 / never | 0; 4 / never | 0; 105 / never | 174; 8 / 61 | 116; 8 / 89 | 0; 8 / never | 0; 8 / never |
| 67/33 | 360 | 122; 4 / 180 | 64; 4 / 262 | 0; 105 / never | 593; 8 / 61 | 535; 8 / 89 | 0; 8 / never | 0; 8 / never |
| 80/20 | 150 | 0; 0 / never | 0; 0 / never | 0; 0 / never | 127; 0 / 82 | 85; 0 / 103 | 0; 0 / never | 0; 0 / never |
| 80/20 | 360 | 0; 0 / never | 0; 0 / never | 0; 0 / never | 542; 0 / 82 | 500; 0 / 103 | 0; 0 / never | 0; 0 / never |
| 33/33/34 | 150 | 0; never x3 | 0; never x3 | 0; never x3 | 354; 59 / 61 / 60 | 198; 87 / 90 / 92 | 0; never x3 | 0; never x3 |
| 33/33/34 | 360 | 226; 192 / 185 / 170 | 106; 279 / 278 / 258 | 0; never x3 | 1,198; 59 / 61 / 60 | 1,042; 87 / 90 / 92 | 0; never x3 | 0; never x3 |
Presence window sweep, active/cert, 0% attacker, no retarget. Prediction for the smallest side with share s: first lock after presence x (1 - 1.5 s) / s slots.
| presence | hours | honest split | partition min | conflicts | first lock per side, min | predicted smallest side, min |
|---|---|---|---|---|---|---|
| 240 | 2 | 50/50 | 360 | 297 | 60 / 59 | 60 |
| 240 | 2 | 60/40 | 360 | 193 | 19 / 121 | 120 |
| 240 | 2 | 33/33/34 | 360 | 226 | 192 / 185 / 170 | 184 |
| 720 | 6 | 50/50 | 720 | 526 | 188 / 179 | 180 |
| 720 | 6 | 60/40 | 720 | 287 | 62 / 367 | 360 |
| 720 | 6 | 33/33/34 | 720 | 201 | 558 / 560 / 516 | 551 |
| 2,880 | 24 | 50/50 | 1,500 | 708 | 734 / 725 | 720 |
| 2,880 | 24 | 60/40 | 1,500 | 28 | 254 / 1,461 | 1,440 |
| 2,880 | 24 | 33/33/34 | 1,500 | 0 | never x3 | 2,204 |
Interpretation. The active denominator as written FAILS the pass criterion. In a 50/50 honest partition with no attacker, both sides lock their own checkpoint block 60 minutes after the split (cert) or 118 minutes (seen), and every index after that is a conflicting lock: 32 of them in a 90-minute partition, 90 in a 150-minute one. The cause is the mechanism that gives C its liveness: a side that cannot see the other side's votes sees stalls, the stalls shrink its denominator, and once the denominator has fallen to 1.5 times the side's own weight the side certifies alone. The time to that point is presence x (1 - 1.5 s) / s slots for a side of share s, confirmed to within 5% across every row of the sweep. With 240 checkpoints that is 60 min for a 50% side, 120 min for a 40% side, 180 min for a 33% side, and even a 20% side locks alone after 84 min once the DAA has restored its block rate ('+daa' columns). The 33/33/34 split passes at 2 hours only because each side's checkpoint clock runs at a third of normal speed with no retarget; with retarget it conflicts at 59 min. The 2-hour window therefore protects against nothing longer than about an hour of partition. Every honest split conflicts under either reading once the partition outlasts the formula. The total denominator produced zero conflicting locks in every honest partition of every duration, as expected: no side holds 2/3 of total.
With a 34% equivocating attacker the 2/3 quorum is already broken by arithmetic: 33% honest plus 34% attacker is 67% on each side of a 50/50 split, so both sides lock within 2 minutes under active (19 to 98 conflicts) and sit on the knife edge under total (2 conflicts in seed 7, 0 to 2 in seed 11, the uptime noise decides). The 33/33/34 split with the attacker holds 22% plus 34% per side, which total never certifies and active certifies after 36 to 88 minutes. Post-heal, with the attacker stripped, no run stalled.
The presence sweep shows the trade: a 24-hour window pushes the 50/50 conflict to 12 hours (6 hours with retarget) and the 60/40 one to 24 hours, at the cost of C and D stalls of hours instead of minutes. The floor hybrid is the other lever: with the active denominator never below 80% of total, a lock needs 53.3% of total weight, no honest side in any split tested could reach it, and every honest partition gave 0 conflicts for 6 hours with full retarget while the majority side (where one exists) kept locking. The 0.85 floor gives the same zeros and the same majority-side lock times (13 min at 60/40, 8 min at 67/33).
F. Eclipse
One pool holding 20% of total weight, always online, in its own region.
F1. Delayed view: the pool receives every block and vote D hours late and votes for the right blocks, late. Participation is as the rest of the network computes it.
| eclipse | pool participation, minimum | first drop below 1 | back to 1 after the end | conflicting locks | stalls during / after |
|---|---|---|---|---|---|
| 1 h | 0.500 | 5 min (first sample) | 120 min | 0 | 0 / 0 |
| 2 h | 0.000 | 5 min | 120 min | 0 | 0 / 0 |
| 4 h | 0.000 | 5 min | 125 min | 0 | 0 / 0 |
Identical under active/cert, active/seen and total.
F2. Poisoned view: an attacker holding 34% of weight feeds the pool a private fork for the eclipse, signs both forks, and is stripped at the heal. The pool votes for the attacker's checkpoints. Honest side = the remaining 46%.
| eclipse | denominator | pool participation, minimum (honest view) | back to 1 after the end | conflicting locks | first conflict, min after start | locks on the eclipsed side | honest-side stalls during / after heal |
|---|---|---|---|---|---|---|---|
| 1 h | active/cert | 0.787 | 115 min | 10 | 49 | 18 | 0 / 0 |
| 1 h | active/seen | 0.787 | 0 min | 0 | never | 0 | 0 / 0 |
| 1 h | total | 0.738 | 125 min | 0 | never | 0 | 0 / 0 |
| 1 h | active/cert + floor 0.80 | 0.787 | 115 min | 10 | 49 | 18 | 0 / 0 |
| 1 h | active/cert + floor 0.85 | 0.738 | 125 min | 0 | never | 0 | 0 / 0 |
| 2 h | active/cert | 0.562 | 85 min | 65 | 49 | 82 | 0 / 0 |
| 2 h | active/seen | 0.562 | 0 min | 12 | 106 | 29 | 0 / 0 |
| 2 h | total | 0.471 | 125 min | 0 | never | 0 | 0 / 0 |
| 2 h | active/cert + floor 0.80 | 0.562 | 85 min | 65 | 49 | 82 | 0 / 0 |
| 2 h | active/cert + floor 0.85 | 0.471 | 125 min | 0 | never | 0 | 0 / 0 |
| 4 h | active/cert | 0.108 | 20 min | 174 | 49 | 209 | 0 / 0 |
| 4 h | active/seen | 0.108 | 0 min | 121 | 106 | 156 | 0 / 0 |
| 4 h | total | 0.000 | 125 min | 0 | never | 0 | 0 / 0 |
| 4 h | active/cert + floor 0.80 | 0.108 | 20 min | 174 | 49 | 209 | 0 / 0 |
| 4 h | active/cert + floor 0.85 | 0.000 | 125 min | 0 | never | 0 | 0 / 0 |
Interpretation. A pure delay is harmless to safety and costly to the pool: its late votes miss every certificate, its participation falls linearly to 0.5 after one hour and to 0 after two, it leaves the active denominator entirely, and it recovers to 1 exactly 2 hours after its view catches up (the missed indices roll out of the 240-checkpoint window). No lock was lost on the honest side, under any denominator, because 80% of weight kept signing. A poisoned eclipse is the dangerous version, and it is the partition hazard of E in miniature: the eclipsed side holds 54% of weight (pool plus attacker), which under total can never certify (0 conflicts at 1, 2 and 4 hours) but under active/cert certifies the attacker's fork 49 minutes in, once the eclipsed view's denominator has decayed below 54%/(2/3) = 81%. Under seen it takes 106 minutes. The attacker here holds 34%, over the 1/3 bound, so this is a "beyond" case by the brief's criterion, but it shows that against the active denominator an attacker does not need 2/3 of weight to finalise a private fork: it needs a third plus one well-connected pool to isolate for an hour. The floor rows settle which floor closes this: 0.80 asks for 53.3% of total, the eclipsed side holds 54%, and the result is identical to no floor (10, 65 and 174 conflicts, first at 49 min); 0.85 asks for 56.7% and gives 0 conflicts at 1, 2 and 4 hours with the pool's participation and recovery exactly as under total. The quick return of the pool's participation to 1 at the heal under seen (0 min) is a merge artefact: the eclipsed side's own records credit the pool and are OR-merged into the honest view.
G. Honest doubling overnight
At day 60 the honest network doubles: 1,000 new keys with a fresh Pareto draw and the same total hashrate as the old 1,000, new keys as participation 1 for their first 240 checkpoints.
| day after doubling | old cohort weight | new cohort weight | formula t/60 | new keys under dust |
|---|---|---|---|---|
| +1 | 98.9% | 1.1% | 1.7% | 935 |
| +5 | 92.9% | 7.1% | 8.3% | 736 |
| +10 | 84.4% | 15.6% | 16.7% | 462 |
| +15 | 75.4% | 24.6% | 25.0% | 186 |
| +20 | 66.7% | 33.3% | 33.3% | 15 |
| +21 | 65.0% | 35.0% | 35.0% | 9 |
| +25 | 58.3% | 41.7% | 41.7% | 0 |
| denominator | last day old cohort holds 2/3 (locks alone) | stalled checkpoints in 25 days | lock latency median / p99, days 1 to 25 |
|---|---|---|---|
| active | 19 | 0 | 2.5 s / 4.6 s |
| total | 19 | 0 | 2.6 s / 4.6 s |
Interpretation. New honest miners are under-weighted for the whole window, as the flat rule requires: the new cohort's share is t/60, its smallest keys sit under dust for up to 25 days (935 of 1,000 on day 1, 462 on day 10), and the old cohort can lock without a single new signature for 19 days (the arithmetic says 20; the 1.1 to 1.7 point lag on days 1 to 10 is the dust threshold holding small new keys out). This is the same 20-day window a rental burst gets, by design: a doubling overnight and a 1x renter are the same event to the rule. Lock latency and stall count do not move (0 stalls, median 2.5 s), because the new keys sign from the moment they clear dust and the old keys hold the quorum throughout. Under active the new keys' participation-1 grace has no visible effect, since they are either under dust or signing.
Recommended parameters
| Parameter | Recommendation | Reason from the runs |
|---|---|---|
| Window | 30 days flat, no damping | Weight is hashrate (A: corr 1.00000), the renter formula holds to 0.04 points (B), and the crossing days 10(1+a)/a and 20(1+a)/a are the whole defence. Nothing here argues for changing it. |
| Dust threshold | 100 blocks, keep | Every honest key clears it by day 20 from zero and by day 25 after a doubling (A, G). It costs an a = 9 renter's victims about one point (B) and holds small new keys out for up to 25 days (G); both are acceptable. Raising it would widen both effects for no measured gain. |
| Quorum | 2/3 of the denominator, keep | The 34% attacker breaks every variant by arithmetic (E: 33% + 34% on each side of a 50/50 split). Lowering the quorum would let a smaller attacker do the same; raising it would stall at smaller silent sets (C: 34% silent already stalls total). |
| Denominator | active, cert reading (objective, from certificates), WITH a floor: the denominator is never below 85% of total weight, so a lock needs 2/3 of active and at least 56.7% of total | Active alone fails E: a 50/50 honest partition finalises two histories after 60 min (30 min with DAA retarget), a 60/40 one after 121 min (47 min), and a 34% attacker with one eclipsed 20% pool finalises a private fork in 49 min (F2). Total alone fails C and D: a 34% silent set that keeps mining stops finality for the whole 30-day window, 50% churn stops it for 10.1 days. The 0.85 floor gave 0 conflicting locks in every honest partition of every split for 6 hours with full retarget (E) and 0 in the poisoned eclipse at 1, 2 and 4 hours (F2), where 0.80 gave the same 10 to 174 conflicts as no floor because the eclipsed side's 54% clears 53.3%. It keeps C's recovery at 0 min for 34% silent and 13 min for 40%, and D's at 2 min for 35% churn. Its costs, all measured: 45% silent or more stalls like total (for as long as they stay silent), 50% churn stalls 4.1 days (total: 10.1, floor 0.80: 2.2), and at 40% silent and 35% churn the margin is one pool outage thin (138 and 98 intermittent stalls in 3 days). The floor turns the liveness bound from 1/3 (total) to about 42% of weight, and the safety bound stays at 1/3 for every event tested. |
| Presence window | 240 checkpoints (2 hours) with the floor; 2,880 (24 hours) if the floor is rejected | With the floor the window only sets how fast silent keys leave the denominator, and 2 hours gives C and D their minute-scale recovery. Without the floor the window is the only partition defence and 2 hours is too short: 24 hours moves the 50/50 conflict to 12 hours (6 with retarget) and the 60/40 one to 24 hours, at the cost of 2.6 to 7.9 hour stalls in C and 6.1 hours at 50% churn in D. |
| Certificate grace | at least 3x the worst one-way delay | At a 5-s delay the slowest region already loses 1.7 points of participation to the 15-s grace (A). Too short a grace turns geography into a participation penalty. |
| Participation reading | cert | seen is not objective (each node counts its own view, so nodes disagree on the denominator) and is slower in C by 2x for no safety gain in E once the floor is in place. frozen is total with extra steps. |
What this model still cannot tell us
The DAG is abstract. Every block is blue, a partition side's checkpoint block is "the block at blue score 30i in that view", and at the heal the two blue-score sequences are simply unioned by index. Real GHOSTDAG will merge the sides' blocks, colour some red under merge depth 3,600 s, and shift which block sits at blue score 30i, so the conflicting-lock counts in E are counts of index collisions, not a reorg depth. The post-heal fork choice ("GHOSTDAG among tips through all certified checkpoints") is not modelled at all, so the model cannot say what a node does when it holds two certificates at one index.
Difficulty retargets perfectly or instantly ('+daa'). The real DAA window delays retarget by of the order of an hour (approximate), which puts the real partition numbers between the two columns in E and slows the first hour of D.
Weights are global in a partition. Over 150 minutes that is 0.35% of the window; over the 6 to 25 hour partitions in the presence sweep it is up to 3.5% and would slightly favour the side that cannot see the other's blocks.
Aggregation is idealised: one aggregator per region, instant aggregation, no VRF sampling of 8 aggregators, no aggregator failure, no gossip of partial aggregates. Lock latency is therefore a lower bound of two message hops.
The uptime model is a guess: 97% for small keys, 99.5% for pools over 1%, 10-minute outages. The resting participation it produces (0.978) is what makes a 34% silent set painless in C and a 50/50 split with a 34% attacker a knife edge under total in E; a different uptime moves both.
The silent set and churn sets are random by key. A silent set made of the top pools, or a regional one, would have the same weight but a different participation trajectory and a different interaction with the floor.
Equivocation evidence is detected only at the heal and the penalty is only forward-looking. The model does not revoke the conflicting certificates or re-evaluate them without the attacker's weight, and it does not cost the attacker anything for the fork it finalised.
The eclipse attacker in F2 is simplified: it mines its fork at its full 34% rate for the pool alone and still signs the honest chain. A real attacker would also have to keep the pool from seeing honest certificates, which this model grants for free.
Keys are free. A 34% attacker is one key here; split across thousands of keys it would behave the same under this rule (no damping), so the model has nothing to add on Sybil behaviour beyond what results.md said.
Additions, 3 October 2026: scenarios H to K for section 3.11 (Guarantees)
Same simulator, same model assumptions as above, the rule exactly as Q3 specifies it (active denominator, cert reading, floor 0.85: a lock needs 2/3 of active and 17/30 of total), five seeds (7, 11, 13, 17, 19) per cell. A cell gives one number when every seed agrees and "a to b" otherwise; "never in k of n" counts the seeds in which the event did not occur. Partition runs use '+daa' (each side retargets at once), which is the median-time clock of Q1. Run time 9.5 min single-process at nice 19 on a loaded machine (K is 9 of them). Every number below was produced by the simulator; the predictions in the H header are the arithmetic of section 3.11.2.
H. Partition of a 50/50 honest network with an equivocating attacker, rule as specified (active/cert + floor 0.85), each side retargets at once (+daa, the median-time clock of Q1), seeds 7,11,13,17,19
Attacker = one key holding the stated share of TOTAL weight, mining on the first side, voting on both. Each side holds (1 - a)/2 + a of total. Prediction (section 3.11 item 2): a side holding s of total locks alone once s >= 17/30 and its view's active weight has decayed to 1.5 s, which under the cert reading is P x (1 - 1.5 s) slots after the split (P = 240, so 2 h x (1 - 1.5 s)); two sides over 17/30 need a >= 4/30 = 13.3%.
| attacker (of total) | each side holds | partition min | predicted first conflict | conflicting locks | first conflict, min | first lock per side, min | every pre-heal lock kept at the heal | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|
| 0% | 50.0% | 150 | no (side holds 50.0% < 56.7%) | 0 | never | never / never | yes | 0 |
| 0% | 50.0% | 360 | no (side holds 50.0% < 56.7%) | 0 | never | never / never | yes | 0 |
| 10% | 55.0% | 150 | no (side holds 55.0% < 56.7%) | 0 | never | never / never | yes | 0 |
| 10% | 55.0% | 360 | no (side holds 55.0% < 56.7%) | 0 | never | never / never | yes | 0 |
| 13% | 56.5% | 150 | no (side holds 56.5% < 56.7%) | 0 | never | never / never | yes | 0 |
| 13% | 56.5% | 360 | no (side holds 56.5% < 56.7%) | 0 | never | never / never | yes | 0 |
| 14% | 57.0% | 150 | 17 min | 0 to 35 | 48 to 74 (never in 3 of 5) | 18 to 74 / 26 to 52 (never in 1 of 5) | yes | 0 |
| 14% | 57.0% | 360 | 17 min | 0 to 54 | 48 to 284 (never in 1 of 5) | 18 to 74 / 26 to 153 | yes | 0 |
| 20% | 60.0% | 150 | 12 min | 256 to 276 | 12 to 16 | 12 to 16 / 12 to 13 | yes | 0 |
| 20% | 60.0% | 360 | 12 min | 658 to 692 | 12 to 16 | 12 to 16 / 12 to 13 | yes | 0 |
| 34% | 67.0% | 150 | 0 min | 278 to 301 | 0 to 1 | 0 to 0 / 0 to 0 | yes | 0 |
| 34% | 67.0% | 360 | 0 min | 697 to 720 | 0 to 1 | 0 to 0 / 0 to 0 | yes | 0 |
I. The 40/40/20 split, rule as specified (active/cert + floor 0.85), +daa, seeds 7,11,13,17,19
| case | partition min | conflicting locks | first conflict, min | locks per side during | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|
| honest 40/40/20, no attacker | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20, no attacker | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 10% equivocator (sides 36+10 / 36+10 / 18+10) | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 10% equivocator (sides 36+10 / 36+10 / 18+10) | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 20% equivocator (sides 32+20 / 32+20 / 16+20) | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 20% equivocator (sides 32+20 / 32+20 / 16+20) | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40 plus a 20% equivocator reaching both (sides 40+20 / 40+20) | 150 | 256 to 276 | 12 to 16 | 259 to 276 / 273 to 277 | yes | 0 to 0 | 0 |
| honest 40/40 plus a 20% equivocator reaching both (sides 40+20 / 40+20) | 360 | 658 to 692 | 12 to 16 | 658 to 700 / 689 to 701 | yes | 0 to 0 | 0 |
J. Signing stops while mining continues for 1, 6 and 24 hours, then resumes; rule as specified, seeds 7,11,13,17,19
A random set holding x of weight stops signing at hour 3 and resumes after the stated time. Its weight never ages out because it keeps mining. 'Longest gap' is the longest interval without a lock while they are silent: the time the node reports finality unavailable.
| silent weight | silent hours | first lock after the stop, min | stalled checkpoints while silent | checkpoints locked while silent | longest gap without a lock, min | first lock after resume, min | stalls in 3 h after resume | conflicting locks |
|---|---|---|---|---|---|---|---|---|
| 34% | 1 | 0 to 2 | 0 to 3 | 98 to 100% | 1 to 2 | 0 | 0 | 0 |
| 34% | 6 | 0 to 2 | 0 to 3 | 100% | 1 to 2 | 0 to 0 | 0 | 0 |
| 34% | 24 | 0 to 2 | 0 to 31 | 99 to 100% | 1 to 15 | 0 | 0 | 0 |
| 40% | 1 | 11 to 13 | 23 to 27 | 78 to 81% | 11 to 13 | 0 | 0 | 0 |
| 40% | 6 | 11 to 13 | 23 to 66 | 91 to 97% | 11 to 13 | 0 to 0 | 0 | 0 |
| 40% | 24 | 11 to 13 | 25 to 123 | 96 to 99% | 12 to 47 | 0 | 0 | 0 |
| 45% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 45% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 45% | 24 | never | 2871 to 2890 | 0% | 1440 | 0 | 0 | 0 |
K. Acquired keys: an attacker buys keys holding 20% or 40% of window weight and mines at 30% of network hashrate from day 0; rule as specified, 30 days, seeds 7,11,13,17,19
The sellers keep their rigs and mine on under fresh keys (so honest hashrate is unchanged and the fresh keys start under dust). Formula (section 3.11 item 5): share(t) = b (1 - t/30) + 0.30 t/30, the bought blocks age out of the window as the attacker's own blocks enter it. 'Fresh hash only' is b = 0: share(t) = 0.30 t/30.
Attacker weight share, simulated over the seeds / formula (attacker signs every checkpoint):
| day after purchase | bought 0% | bought 20% | bought 40% |
|---|---|---|---|
| +1 | 1.0 to 1.0% / 1.0% | 20.3 to 20.4% / 20.3% | 39.8 to 39.8% / 39.7% |
| +5 | 5.0 to 5.0% / 5.0% | 21.7 to 21.7% / 21.7% | 38.5 to 38.7% / 38.3% |
| +10 | 10.0 to 10.0% / 10.0% | 23.3 to 23.4% / 23.3% | 36.7 to 36.9% / 36.7% |
| +15 | 15.0 to 15.0% / 15.0% | 25.0 to 25.0% / 25.0% | 35.0 to 35.1% / 35.0% |
| +20 | 20.0 to 20.0% / 20.0% | 26.6 to 26.7% / 26.7% | 33.2 to 33.4% / 33.3% |
| +25 | 24.9 to 25.0% / 25.0% | 28.0 to 28.2% / 28.3% | 31.1 to 31.4% / 31.7% |
| +30 | 30.0 to 30.0% / 30.0% | 30.0 to 30.0% / 30.0% | 30.0 to 30.0% / 30.0% |
| bought weight | attacker | bought, as picked | peak share | share at day 30 | holds at least 1/3 (can veto) | stalled checkpoints in 30 days | conflicting locks |
|---|---|---|---|---|---|---|---|
| 0% | signs | 0.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 0 | 0 |
| 20% | signs | 20.0 to 20.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 0 | 0 |
| 40% | signs | 40.0 to 40.0% | 39.8 to 39.8% | 30.0 to 30.0% | from day 1 until day 19 to 20 | 0 | 0 |
| 20% | silent | 20.0 to 20.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 0 to 318 | 0 |
| 40% | silent | 40.0 to 40.0% | 39.8 to 39.8% | 30.0 to 30.0% | from day 1 until day 19 to 20 | 305 to 1085 | 0 |
Stalled checkpoints per day while the attacker withholds its votes (2,880 checkpoints a day):
| bought weight | day | stalled that day |
|---|---|---|
| 20% | +1 | 0 to 11 |
| 20% | +5 | 0 to 10 |
| 20% | +10 | 0 |
| 20% | +15 | 0 to 17 |
| 20% | +20 | 0 |
| 20% | +25 | 0 to 11 |
| 20% | +30 | 0 to 7 |
| 40% | +1 | 79 to 186 |
| 40% | +5 | 0 to 56 |
| 40% | +10 | 0 to 69 |
| 40% | +15 | 0 to 33 |
| 40% | +20 | 0 to 130 |
| 40% | +25 | 0 to 53 |
| 40% | +30 | 0 to 50 |
Interpretation. H confirms the bound of section 3.11.2 at every point. Two sides each over the 56.7% floor lock alone once their active weight has decayed to 1.5 times their own share, which under the cert reading is 2 h x (1 - 1.5 s) after the split: a 20% equivocator across a 50/50 honest network gives each side 60% and both sides lock at minute 12 to 16 (predicted 12); a 34% equivocator at minute 0 to 1 (predicted 0). Below the floor nothing locks: 10% (sides 55.0%) and 13% (56.5%) gave 0 conflicts and 0 locks in every seed for six hours. 14% (57.0%) is the knife edge, 0.3 points over the floor against a 2% uptime shortfall, and conflicts in some seeds only, from minute 48. The bound is therefore 4/30 = 13.3% of total weight against a partition that outlasts about 20 minutes, not the one third of 3.3.1, which holds while every honest voter's votes reach every honest node. Every certificate any side held before the heal was in the merged view after it in all 100 partition runs, and no run stalled in the three hours after the heal.
I shows what "40/40/20" means under the floor. As an honest three-way split, no side reaches 56.7%, so no side locks: finality pauses on all three for the whole partition and resumes at minute 0 after the heal, with no conflict. With a 20% equivocator spread over the same three sides (52/52/36 of total) the same holds. The dangerous reading is two honest 40% sides with the 20% reaching both (60/60): 256 to 276 conflicting locks in 150 minutes, the first at minute 12 to 16, exactly H's 20% row.
J measures the pause and the resume. 34% silent costs 0 to 31 stalled checkpoints in 24 hours and a longest gap of 1 to 15 minutes; 40% silent costs 23 to 123 stalls with a longest gap of 11 to 47 minutes (the margin at 40% is the 58.7% online signing share against the 56.7% floor, one pool outage thin, as 3.3.1 says); 45% silent locks nothing for 1, 6 or 24 hours. In every case the first lock comes 0 minutes after the silent set resumes and nothing stalls in the three hours after, and no run produced a conflicting lock: a silent set that keeps mining can pause finality for as long as it stays silent and can do nothing else. These are cert-reading figures; the block reading of Q2 recovers more slowly (O-3.18).
K confirms section 3.11.5: a bought key is worth the blocks it holds and nothing more. The share of an attacker who buys keys worth b and mines at 30% of the network follows b (1 - t/30) + 0.3 t/30 within 0.6 points at every sampled day in every seed (the deviation is the sellers' fresh keys sitting under dust for their first days). Keys worth 20% climb to 30% on day 30 and never reach a third; keys worth 40% hold the veto from the day of purchase until day 19 or 20 (formula: 20) and are worth 30% on day 30, the same as fresh hashrate. Withholding its votes, the 40% buyer stalls 305 to 1,085 of 86,400 checkpoints over the 30 days, most of them on day 1 (79 to 186) while its bought weight is above 40% of the active denominator, and the 20% buyer 0 to 318; neither produced a conflicting lock. Not modelled: a seller who keeps a copy of a sold key and equivocates with it, which strips the buyer (3.11.5).
What these runs still cannot tell us is unchanged from the list above: no DAG, perfect retarget, cert reading, idealised aggregation, uptime a guess, random silent sets, keys free.
Floor 2/3, 4 October 2026: the rule re-run with the floor at two thirds of total weight
Decision of 4 October 2026 (the founder, O-3.15): the floor of Q3 is 2/3 of total weight, not 17/30. Since active weight never exceeds total, the active test is implied and a lock is two thirds of all 30-day weight signing; finality pauses whenever less than two thirds of the weight is connected and signing. In the simulator the rule is floor=1.0 (rule_p, default since this date; --floor 0.85 reproduces the 3 October tables), which is arithmetically the total denominator of A to G: the floor1.00 and total columns agree in every cell below. Two additions to the simulator: --floor, and the +local mode in which a partition side's weight table counts only the blocks it has seen (its own after the split), as a real node's window does; every earlier table kept weights global, which hid the window bound that attack scenario 6A found on the devnet (docs/bench-log.md, "finality v2 attack harness"). Machine load 3 to 13 (other agents' builds and test networks), nice 10: H 25 s, I 13 s, J 16 s, K 400 s, L about 10 min; A to G in --quick mode about 1 min per seed.
A to G at the 2/3 floor, seeds 7, 11, 13, 17, 19, --quick (3-h silent runs, 1-day churn runs)
Every cell of the new active/cert+floor1.00 column equals the total column in every seed. Against the 0.85 floor of 3 October:
| Scenario | Floor 0.85 (3 October, seed 7; the 3 October tables) | Floor 2/3 (five seeds) |
|---|---|---|
| C, 34% silent keeps mining | 0 to 2 min to the first lock, 0 to 3 stalled | never while silent: 356 to 366 stalled in 3 h in every seed |
| C, 40% silent | 11 to 13 min, 23 to 52 stalled | never while silent |
| C, 45%, 50%, 55% silent | 45%: never; 50%, 55%: never | never |
| D, 35% churn | 2 min, 98 intermittent stalls in 3 days | no lock in the 1-day quick run (2,864 to 2,898 stalled); analytic day 1.4, measured in L2 below |
| D, 50% churn | 4.1 days | no lock in 1 day; analytic day 10 (the total column of D measured 10.1 days), L2 below |
| E, 50/50 honest, 150 and 360 min, +daa | 0 conflicts, no side locks | 0 conflicts, no side locks, every seed |
| E, 60/40 | 0 conflicts; the 60 side locks from minute 13 | 0 conflicts; neither side locks (60% is under the floor), every seed |
| E, 67/33 | 0 conflicts; the 67 side locks from minute 0 to 8 | 0 conflicts; the 67 side locks after 40 to 212 min in 3 of 5 seeds and never in 2 (67% sits 0.3 points over the floor against the 2.2% outage shortfall; the 33 side never) |
| E, 80/20 | 0 conflicts; the 80 side at minute 0 | 0 conflicts; the 80 side at minute 0 |
| E, 33/33/34 | 0 conflicts, no side locks | 0 conflicts, no side locks |
| F2, poisoned eclipse (34% attacker, 20% pool, eclipsed side 54%), 1, 2, 4 h | 0 conflicts, 0 locks on the eclipsed side | 0 conflicts, 0 locks on the eclipsed side, every seed; pool participation minimum 0.725 to 0.738 at 1 h, 0 at 4 h, back to 1 within 120 to 125 min of the heal |
| A, B, G | unchanged (weight, dust, the renter formula and the doubling do not involve the floor) | unchanged |
The pattern. Everything a floor under two thirds bought in liveness is gone (a silent third pauses finality; churn waits for the window), and everything it cost in safety is gone with it (no honest split under two thirds locks, however long the presence window has decayed). The remaining sections measure the bounds the new rule states.
H to L at the 2/3 floor, seeds 7, 11, 13, 17, 19, full lengths (the simulator's own output; H, I, J, K as before, L new)
H. Partition of a 50/50 honest network with an equivocating attacker, rule as specified (active/cert + floor 1.00 (a lock needs 66.7% of total)), each side retargets at once (+daa, the median-time clock of Q1), seeds 7,11,13,17,19
Attacker = one key holding the stated share of TOTAL weight, mining on the first side, voting on both. Each side holds (1 - a)/2 + a of total. Prediction (section 3.11 item 2): a side holding s of total locks alone once s >= the floor (66.7%) and its view's active weight has decayed to 1.5 s, which under the cert reading is P x (1 - 1.5 s) slots after the split (P = 240, so 2 h x (1 - 1.5 s), 0 at s >= 2/3); two sides over the floor need a >= 33.3%.
| attacker (of total) | each side holds | partition min | predicted first conflict | conflicting locks | first conflict, min | first lock per side, min | every pre-heal lock kept at the heal | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|
| 0% | 50.0% | 150 | no (side holds 50.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 0% | 50.0% | 360 | no (side holds 50.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 10% | 55.0% | 150 | no (side holds 55.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 10% | 55.0% | 360 | no (side holds 55.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 13% | 56.5% | 150 | no (side holds 56.5% < 66.7%) | 0 | never | never / never | yes | 0 |
| 13% | 56.5% | 360 | no (side holds 56.5% < 66.7%) | 0 | never | never / never | yes | 0 |
| 14% | 57.0% | 150 | no (side holds 57.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 14% | 57.0% | 360 | no (side holds 57.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 20% | 60.0% | 150 | no (side holds 60.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 20% | 60.0% | 360 | no (side holds 60.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 30% | 65.0% | 150 | no (side holds 65.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 30% | 65.0% | 360 | no (side holds 65.0% < 66.7%) | 0 | never | never / never | yes | 0 |
| 33% | 66.5% | 150 | no (side holds 66.5% < 66.7%) | 0 | never | never / never | yes | 0 |
| 33% | 66.5% | 360 | no (side holds 66.5% < 66.7%) | 0 | never | never / never | yes | 0 |
| 34% | 67.0% | 150 | 0 min | 2 to 51 | 2 to 77 | 0 to 60 / 0 to 76 | yes | 0 |
| 34% | 67.0% | 360 | 0 min | 5 to 54 | 2 to 77 | 0 to 60 / 0 to 76 | yes | 0 |
I. The 40/40/20 split, rule as specified (active/cert + floor 1.00 (a lock needs 66.7% of total)), +daa, seeds 7,11,13,17,19
| case | partition min | conflicting locks | first conflict, min | locks per side during | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|
| honest 40/40/20, no attacker | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20, no attacker | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 10% equivocator (sides 36+10 / 36+10 / 18+10) | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 10% equivocator (sides 36+10 / 36+10 / 18+10) | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 20% equivocator (sides 32+20 / 32+20 / 16+20) | 150 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40/20 plus a 20% equivocator (sides 32+20 / 32+20 / 16+20) | 360 | 0 | never | 0 / 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40 plus a 20% equivocator reaching both (sides 40+20 / 40+20) | 150 | 0 | never | 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40 plus a 20% equivocator reaching both (sides 40+20 / 40+20) | 360 | 0 | never | 0 / 0 | yes | 0 to 0 | 0 |
| honest 40/40 plus a 34% equivocator reaching both (sides 33+34 / 33+34) | 150 | 2 to 51 | 2 to 77 | 11 to 61 / 40 to 159 | yes | 0 | 0 |
| honest 40/40 plus a 34% equivocator reaching both (sides 33+34 / 33+34) | 360 | 5 to 54 | 2 to 77 | 22 to 87 / 212 to 354 | yes | 0 to 0 | 0 |
J. Signing stops while mining continues for 1, 6 and 24 hours, then resumes; rule as specified, seeds 7,11,13,17,19
A random set holding x of weight stops signing at hour 3 and resumes after the stated time. Its weight never ages out because it keeps mining. 'Longest gap' is the longest interval without a lock while they are silent: the time the node reports finality unavailable.
| silent weight | silent hours | first lock after the stop, min | stalled checkpoints while silent | checkpoints locked while silent | longest gap without a lock, min | first lock after resume, min | stalls in 3 h after resume | conflicting locks |
|---|---|---|---|---|---|---|---|---|
| 34% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 34% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 34% | 24 | never | 2871 to 2890 | 0% | 1440 | 0 | 0 | 0 |
| 40% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 40% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 40% | 24 | never | 2871 to 2890 | 0% | 1440 | 0 | 0 | 0 |
| 45% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 45% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 45% | 24 | never | 2871 to 2890 | 0% | 1440 | 0 | 0 | 0 |
K. Acquired keys: an attacker buys keys holding 20% or 40% of window weight and mines at 30% of network hashrate from day 0; rule as specified, 30 days, seeds 7,11,13,17,19
The sellers keep their rigs and mine on under fresh keys (so honest hashrate is unchanged and the fresh keys start under dust). Formula (section 3.11 item 5): share(t) = b (1 - t/30) + 0.30 t/30, the bought blocks age out of the window as the attacker's own blocks enter it. 'Fresh hash only' is b = 0: share(t) = 0.30 t/30.
Attacker weight share, simulated over the seeds / formula (attacker signs every checkpoint):
| day after purchase | bought 0% | bought 20% | bought 40% |
|---|---|---|---|
| +1 | 1.0 to 1.0% / 1.0% | 20.3 to 20.4% / 20.3% | 39.8 to 39.8% / 39.7% |
| +5 | 5.0 to 5.0% / 5.0% | 21.7 to 21.7% / 21.7% | 38.5 to 38.7% / 38.3% |
| +10 | 10.0 to 10.0% / 10.0% | 23.3 to 23.4% / 23.3% | 36.7 to 36.9% / 36.7% |
| +15 | 15.0 to 15.0% / 15.0% | 25.0 to 25.0% / 25.0% | 35.0 to 35.1% / 35.0% |
| +20 | 20.0 to 20.0% / 20.0% | 26.6 to 26.7% / 26.7% | 33.2 to 33.4% / 33.3% |
| +25 | 24.9 to 25.0% / 25.0% | 28.0 to 28.2% / 28.3% | 31.1 to 31.4% / 31.7% |
| +30 | 30.0 to 30.0% / 30.0% | 30.0 to 30.0% / 30.0% | 30.0 to 30.0% / 30.0% |
| bought weight | attacker | bought, as picked | peak share | share at day 30 | holds at least 1/3 (can veto) | stalled checkpoints in 30 days | conflicting locks |
|---|---|---|---|---|---|---|---|
| 0% | signs | 0.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 0 | 0 |
| 20% | signs | 20.0 to 20.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 0 | 0 |
| 40% | signs | 40.0 to 40.0% | 39.8 to 39.8% | 30.0 to 30.0% | from day 1 until day 19 to 20 | 0 | 0 |
| 20% | silent | 20.0 to 20.0% | 30.0 to 30.0% | 30.0 to 30.0% | never | 304 to 1045 | 0 |
| 40% | silent | 40.0 to 40.0% | 39.8 to 39.8% | 30.0 to 30.0% | from day 1 until day 19 to 20 | 63307 to 68716 | 0 |
Stalled checkpoints per day while the attacker withholds its votes (2,880 checkpoints a day):
| bought weight | day | stalled that day |
|---|---|---|
| 20% | +1 | 0 to 54 |
| 20% | +5 | 0 to 61 |
| 20% | +10 | 0 to 22 |
| 20% | +15 | 0 to 103 |
| 20% | +20 | 0 to 40 |
| 20% | +25 | 0 to 76 |
| 20% | +30 | 17 to 154 |
| 40% | +1 | 2880 to 2898 |
| 40% | +5 | 2853 to 2895 |
| 40% | +10 | 2870 to 2886 |
| 40% | +15 | 2863 to 2901 |
| 40% | +20 | 2857 to 2885 |
| 40% | +25 | 137 to 675 |
| 40% | +30 | 39 to 188 |
L. The floor at 66.7% of total (floor factor 1.00): silent weight, churn, the eclipse, and long partitions with view-local weight; seeds 7,11,13,17,19 (churn and long partitions: 7,11,13)
L1. Signing stops while mining continues (as J), finer around one third. The floor needs the signing weight at or above 66.7% of total; the model's resting participation is 0.978, so the online signing share is about 0.978 x (1 - silent).
| silent weight | online signing share, about | silent hours | first lock after the stop, min | stalled checkpoints while silent | checkpoints locked while silent | longest gap without a lock, min | first lock after resume, min | stalls in 3 h after resume | conflicting locks |
|---|---|---|---|---|---|---|---|---|---|
| 25% | 73.4% | 1 | 0 | 0 | 100% | 1 | 0 | 0 | 0 |
| 25% | 73.4% | 6 | 0 | 0 | 100% | 1 | 0 to 0 | 0 | 0 |
| 30% | 68.5% | 1 | 0 | 0 | 100% | 1 | 0 | 0 | 0 |
| 30% | 68.5% | 6 | 0 | 0 to 40 | 94 to 100% | 1 to 8 | 0 to 0 | 0 | 0 |
| 32% | 66.5% | 1 | 0 | 0 to 56 | 54 to 100% | 1 to 10 | 0 | 0 | 0 |
| 32% | 66.5% | 6 | 0 | 35 to 221 | 69 to 95% | 8 to 32 | 0 to 0 | 0 | 0 |
| 33% | 65.5% | 1 | 24 to 49 (never in 3 of 5) | 108 to 123 | 0 to 11% | 34 to 60 | 0 | 0 | 0 |
| 33% | 65.5% | 6 | 24 to 266 (never in 1 of 5) | 665 to 727 | 0 to 8% | 90 to 360 | 0 to 0 | 0 | 0 |
| 34% | 64.5% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 34% | 64.5% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 40% | 58.7% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 40% | 58.7% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
| 45% | 53.8% | 1 | never | 118 to 123 | 0% | 60 | 0 | 0 | 0 |
| 45% | 53.8% | 6 | never | 714 to 727 | 0% | 360 | 0 to 0 | 0 | 0 |
L2. Churn (as D): a set stops mining and signing; survivors inherit the block supply (perfect retarget). Analytic first lock at day 30 (1 - (1 - 66.7%) / x) for a set holding x: 35%: day 1.4, 50%: day 10.0.
| churn weight | days run | first lock after the event | stalled checkpoints | stalled after the first lock | live share of total weight at the end | conflicting locks |
|---|---|---|---|---|---|---|
| 35% | 3 | 1.7 to 1.7 days | 6136 to 6446 | 1322 to 1518 | 68.5 to 68.5% | 0 |
| 50% | 12 | 10.1 to 10.3 days | 29735 to 31126 | 693 to 1318 | 70.0 to 70.0% | 0 |
L3. The poisoned eclipse (as F2): a 34% attacker feeds a 20% pool a private fork and signs both; the eclipsed side holds 54% of total.
| eclipse h | conflicting locks | first conflict, min | locks on the eclipsed side | honest-side stalls during | stalls after heal | pool participation, minimum |
|---|---|---|---|---|---|---|
| 1 | 0 | never | 0 | 0 | 0 | 0.725 to 0.738 |
| 2 | 0 | never | 0 | 0 | 0 | 0.442 to 0.471 |
| 4 | 0 | never | 0 | 0 | 0 | 0.000 |
L4. Long honest partitions with view-local weight ('+local'): after the split a side's window holds only the blocks it has seen, so its own share of its own table rises as s + (1 - s) T / 30 on day T (each side retargets, +daa). Prediction: a side with pre-split share s locks alone from day 30 (floor - s) / (1 - s), 0 if s is already at the floor; 12 days, no attacker, seeds 7,11,13. The 3 October tables kept weights global (results_v2.md, 'Weights in a partition'), which hid this bound; attack scenario 6A found it on the devnet.
| honest split | floor factor (lock needs, of the side's own table) | partition days | predicted first lock per side, day | first lock per side, day | conflicting locks | first conflict | every pre-heal lock kept | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|
| 50/50 | 1.00 (66.7%) | 12 | 10.0 / 10.0 | 10.2 to 10.2 / 10.1 to 10.3 | 2890 to 3550 | 10.2 to 10.4 days | yes | 0 |
| 50/50 | 0.85 (56.7%) | 12 | 4.0 / 4.0 | 4.1 to 4.2 / 4.1 to 4.2 | 20644 to 20827 | 4.2 to 4.4 days | yes | 0 |
| 60/40 | 1.00 (66.7%) | 12 | 5.0 / > 12 | 5.1 to 5.3 / never | 0 | never | yes | 0 |
| 60/40 | 0.85 (56.7%) | 12 | 0 / 8.3 | 0.0 to 0.0 / 8.5 to 8.6 | 8465 to 8869 | 8.5 to 8.6 days | yes | 0 |
| 55/45 | 1.00 (66.7%) | 12 | 7.8 / 11.8 | 7.9 to 8.0 / 12.0 to 12.0 (never in 1 of 3) | 0 to 4 | 12.0 days | yes | 0 |
| 55/45 | 0.85 (56.7%) | 12 | 1.1 / 6.4 | 1.2 to 1.4 / 6.5 to 6.5 | 14313 to 14504 | 6.5 to 6.5 days | yes | 0 |
Interpretation of H to L at the 2/3 floor, and the deltas against 0.85
| Measure | Floor 0.85 (3 October) | Floor 2/3 (4 October) |
|---|---|---|
| H, equivocator across a 50/50 honest split, smallest share that conflicts | 14% in some seeds (sides 57.0%), 20% in every seed from minute 12 to 16 | 34% (sides 67.0%): 2 to 54 conflicts in 150 to 360 min, the first at minute 2 to 77; 33% (sides 66.5%) and everything below: 0 conflicts and no lock on either side in every seed. The predicted "0 min" at 34% is the arithmetic without outages; with 2.2% of honest weight in outage a 67.0% side sits at the knife edge and locks intermittently, which is why the conflict counts are tens, not hundreds |
| I, 40/40 plus a 20% equivocator reaching both (sides 60/60) | 256 to 276 conflicts in 150 min, first at minute 12 to 16 | 0 conflicts, no lock on either side |
| I, 40/40 plus a 34% equivocator reaching both (sides 67/67) | not run (34% was known to break 0.85 at once) | 2 to 54 conflicts, as H's 34% row |
| I, honest 40/40/20 with and without a 10% or 20% equivocator | 0 conflicts, no locks | 0 conflicts, no locks |
| J and L1, silent set that keeps mining: pause threshold | between 40% (13-min first lock, 23 to 123 stalls) and 45% (never) | between 32% and 34%: 30% silent locks every checkpoint for 6 h in 4 of 5 seeds (0 to 40 stalls in one), 32% locks 69 to 100%, 33% locks 0 to 11% with a first lock after 24 to 266 min when there is one, 34% and above lock nothing for as long as they stay silent |
| J, first lock after the silent set returns | 0 min | 0 min, every weight, every length; 0 conflicts |
| L2 and D, churn | 35%: 2 min, 98 intermittent stalls in 3 days; 50%: 4.1 days | 35%: 1.7 days (analytic 1.4 plus the outage shortfall), then 1,322 to 1,518 intermittent stalls while the margin is thin; 50%: 10.1 to 10.3 days (analytic 10.0), then 693 to 1,318 intermittent stalls |
| K, acquired keys worth 40% that withhold their votes | 305 to 1,085 stalls in 30 days | 63,307 to 68,716 stalls of 86,400: a silent 40% bought key pauses finality until it has decayed below one third (day 19 to 20), as the arithmetic says; keys worth 20% that withhold: 304 to 1,045 stalls (the knife edge at 30% own hashrate, days 25 to 30); shares unchanged; 0 conflicts in every K row |
| L3 and F2, poisoned eclipse (34% attacker, 20% pool, eclipsed side 54%) | 0 conflicts | 0 conflicts, 0 locks on the eclipsed side, every seed and length |
| L4, long honest partition with view-local weight, 50/50 | both sides lock alone from day 4.1 to 4.2 (predicted 4.0); 20,644 to 20,827 conflicts by day 12 | both sides from day 10.1 to 10.3 (predicted 10.0); 2,890 to 3,550 conflicts by day 12 |
| L4, 60/40 | the 60 side at once, the 40 side from day 8.5 to 8.6 (predicted 8.3); 8,465 to 8,869 conflicts | the 60 side from day 5.1 to 5.3 (predicted 5.0), the 40 side never in 12 days (predicted 13.3); 0 conflicts |
| L4, 55/45 | day 1.2 to 1.4 and 6.5 (predicted 1.1 and 6.4); 14,313 to 14,504 conflicts | day 7.9 to 8.0 and 12.0 (predicted 7.8 and 11.8); 0 to 4 conflicts at the very end of the 12 days |
What the floor at two thirds buys and costs, in one line each. Safety: no equivocator under one third of total weight produces a conflicting lock in any partition or eclipse of any tested length, because two certificates need 4/3 of weight in signatures; the 13.3% bound of 3 October is gone and the one-third headline of spec 3.1 holds in every view. Liveness: finality pauses whenever less than two thirds of the weight is connected and signing, which in the model is reached at 32 to 34% silent (the 2.2% outage shortfall sits inside the margin) and lasts for as long as a mining silent set stays silent, or 30 (1 - 1/(3x)) days for a departed share x. The window bound: a side of an honest partition holds two thirds of its own table from day 30 (2/3 - s) / (1 - s), 10 days at 50/50 against 4 under the old floor; no floor removes it, the exchange guidance of spec 3.9 covers it, and the devnet measured the young-window form of it (docs/bench-log.md, "finality v2 attack harness" S6A and "finality floor 2/3").
What these runs still cannot tell us: as before, plus that the '+local' mode ages the unseen blocks with the global buckets (exact for partitions under 30 days, which is every run here) and that the pre-split half of each table ages at the global rate while the real window is in each side's own DAA time, the same thing under '+daa'.
Rule v3, 4 October 2026 (evening): the frozen weight table, ledger F21
The window bound of 3.7 item 9 (L4, attack scenario 6A) comes from each side of a partition filling its own sliding table with its own blocks. Rule v3 adds a second table to the lock test: the weight table at the view's last certified checkpoint (the highest locked checkpoint on the chain of C_i), frozen until a newer checkpoint certifies, and expiring one window (30 days) after its checkpoint. A certificate locks when its signers hold two thirds of the sliding table at C_i (Q3 as today) AND two thirds of the frozen table at the frozen table's weights. In the simulator: P.frozen (+frozen), rule_v3(), scenario M (--scenarios M --seeds 7,11, 496 s at nice 10 on the loaded Mac). What it changes and what it does not, measured:
- A side of an honest partition holds its pre-split share of the frozen table whatever it mines, so no side under two thirds locks for 30 days after the last certified checkpoint (M2: never in 12 days at 50/50, 60/40 and 55/45, against days 10.2, 5.2 and 7.9 under v2; M3: both sides of a 50/50 and of a 60/40 split lock alone at day 30.00, when the frozen table expires and the sliding table decides). The heal keeps every pre-heal lock and locks 0 minutes after it in every row.
- A side at or above two thirds locks at once under both rules (M1, 70/30: the 70 side from minute 0 to 4, the 30 side never, 0 conflicts). A side at exactly two thirds (67/33, the 4/2 split) is a knife edge under both: with the model's 2.2% outage shortfall it locked in 1 of 2 seeds under v2 after 239 minutes and never under v3 in 360 minutes.
- The cost is liveness after a sudden departure: a set that stops mining and signing at once stalls the survivors until the frozen table expires at day 30, where v2 recovers at 30 (1 - 1/(3x)) days (M4: 35% from 1.7 to 30.0 days, 50% from 10.1 to 30.0 days). A gradual departure costs nothing: every certified checkpoint re-freezes the table, so keys that leave while locks continue age out of it as they age out of the sliding table.
- The equivocator bound is unchanged (M5): an equivocator at a of total gives each side (1 - a)/2 + a of the frozen table, 66.5% at 33% (0 conflicts, no lock on either side) and 67.0% at 34% (21 to 69 conflicts, the first at minute 14 to 78).
M. Rule v3, the frozen weight table (ledger F21): partitions, the heal, churn and the equivocator bound; v2 = the rule as specified today with view-local weights (+local), v3 = v2 plus the frozen table (+frozen); seeds 7,11
Prediction for v3: a side of an honest partition holds its pre-split share s of the frozen table for as long as the table stands, so no side under two thirds locks until the frozen checkpoint is one window old (day 30 after the last lock, whatever s), after which the sliding table applies and the v2 bound (already crossed) locks both sides. A side at or above two thirds (6B) locks at once under both. A departed set stalls the survivors until day 30 under v3 (v2: 30 (1 - 1/(3x)) days). The equivocator bound of 3.11.2 is unchanged: an equivocator at a of total gives each side (1 - a)/2 + a of the frozen table, two thirds at a = 1/3.
M1. Honest partitions at devnet lengths, no attacker, each side retargets and counts only its own blocks:
| honest split | partition min | rule | conflicting locks | locks per side during | first lock per side, min | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|
| 50/50 (6A) | 150 | v2 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 50/50 (6A) | 150 | v3 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 50/50 (6A) | 360 | v2 | 0 | 0 / 0 | never / never | yes | 0 | 0 |
| 50/50 (6A) | 360 | v3 | 0 | 0 / 0 | never / never | yes | 0 | 0 |
| 60/40 | 150 | v2 | 0 | 0 / 0 | never / never | yes | 0 | 0 |
| 60/40 | 150 | v3 | 0 | 0 / 0 | never / never | yes | 0 | 0 |
| 60/40 | 360 | v2 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 60/40 | 360 | v3 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 67/33 (6B, the 4/2 split) | 150 | v2 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 67/33 (6B, the 4/2 split) | 150 | v3 | 0 | 0 / 0 | never / never | yes | 0 to 0 | 0 |
| 67/33 (6B, the 4/2 split) | 360 | v2 | 0 | 0 to 4 / 0 | 239 (never in 1 of 2) / never | yes | 0 | 0 |
| 67/33 (6B, the 4/2 split) | 360 | v3 | 0 | 0 / 0 | never / never | yes | 0 | 0 |
| 70/30 | 150 | v2 | 0 | 291 to 302 / 0 | 0 to 4 / never | yes | 0 to 0 | 0 |
| 70/30 | 150 | v3 | 0 | 291 to 302 / 0 | 0 to 4 / never | yes | 0 to 0 | 0 |
| 70/30 | 360 | v2 | 0 | 698 to 716 / 0 | 0 to 4 / never | yes | 0 | 0 |
| 70/30 | 360 | v3 | 0 | 698 to 716 / 0 | 0 to 4 / never | yes | 0 | 0 |
M2. L4 again (long honest partitions, 12 days): v2 locks alone from day 30 (2/3 - s) / (1 - s), v3 not before day 30:
| honest split | rule | partition days | first lock per side, day | conflicting locks | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|
| 50/50 | v2 | 12 | 10.2 to 10.2 / 10.1 to 10.2 | 2890 to 3415 | yes | 0 | 0 |
| 50/50 | v3 | 12 | never / never | 0 | yes | 0 | 0 |
| 60/40 | v2 | 12 | 5.2 to 5.3 / never | 0 | yes | 0 to 0 | 0 |
| 60/40 | v3 | 12 | never / never | 0 | yes | 0 to 0 | 0 |
| 55/45 | v2 | 12 | 7.9 to 8.0 / 12.0 | 0 to 4 | yes | 0 | 0 |
| 55/45 | v3 | 12 | never / never | 0 | yes | 0 | 0 |
M3. The frozen table expires one window after its checkpoint (31-day partitions, v3): the bound moves to day 30 for every split under two thirds:
| honest split | partition days | first lock per side, day | conflicting locks | first conflict |
|---|---|---|---|---|
| 50/50 | 31 | 30.00 / 30.00 | 2679 to 2701 | 30.03 to 30.06 days |
| 60/40 | 31 | 30.00 / 30.00 | 2822 to 2870 | 30.00 to 30.02 days |
M4. Churn (as L2): a set holding x of weight stops mining and signing at once; v2 recovers at 30 (1 - 1/(3x)) days, v3 when the frozen table expires:
| churn weight | rule | days run | first lock after the event | stalled checkpoints | stalled after the first lock | conflicting locks |
|---|---|---|---|---|---|---|
| 35% | v2 | 31 | 1.67 to 1.71 days | 6889 to 6981 | 1961 to 2179 | 0 |
| 35% | v3 | 31 | 30.00 days | 86386 to 86511 | 0 | 0 |
| 50% | v2 | 31 | 10.09 to 10.10 days | 30107 to 30546 | 1065 to 1419 | 0 |
| 50% | v3 | 31 | 30.00 days | 86404 to 86514 | 4 to 10 | 0 |
M5. The equivocator across a 50/50 split (as H), v3: each side holds (1 - a)/2 + a of the frozen table, so the one-third bound stands:
| attacker (of total) | each side holds | conflicting locks | first conflict, min | first lock per side, min |
|---|---|---|---|---|
| 30% | 65.0% | 0 | never | never / never |
| 33% | 66.5% | 0 | never | never / never |
| 34% | 67.0% | 21 to 69 | 14 to 78 | 12 to 50 / 0 to 77 |
Rule v4, 8 October 2026: the anchored table and the majority-continuity recovery (the finality boundary lane, docs/spec/finality-guarantees.md)
The external review of 8 October 2026 (accepted by the founder at 16:1x UK) found the fault in M3: rule v3's frozen table expires one window after its checkpoint, a timeout, and a timeout alone cannot tell a node whether missing miners are gone or on the other side of a partition. Rule v4 anchors the table (it never expires by time; P.anchored, +anchored) and, with P.recovery (+recovery), adds the one resumption that is not a timeout: once the anchored checkpoint is a full window old with no certificate since, a certificate locks when its signers hold two thirds of the sliding table (Q3) AND strictly more than half of the anchored table at its weights, on the chain through the anchored checkpoint; the lock re-anchors the table and is counted as a recovery lock. rule_v4() and rule_v4r(); scenario N (--scenarios N --seeds 7,11, the six parts in parallel on 8 cores of igneum-build-3 under the lease tool at nice 19, 16:02 to 16:09 UK; the known-failed v3 rows reproduce M3 to the checkpoint). What it changes, measured:
- N1: v3 locks both sides of every 31-day split under two thirds alone at day 30.00 (2,679 to 2,870 conflicting locks, the fault). v4 pause: no side locks, 0 conflicts, the heal locks 0 minutes after. v4 recovery: the side holding more than half of the anchored table (the 60 of 60/40, the 55 of 55/45) recovery-locks once at day 30.00 and then locks normally on its re-anchored table; the other side never; the 50/50 split stays paused on both sides; 0 conflicts; every pre-heal lock kept.
- N2: the honest 40/40/20 split pauses on all three sides for 150 minutes and for 31 days under both v4 rules, 0 conflicts, the heal locks at once. With a 20 percent equivocator reaching both 40 sides (60/60 of the anchored table) only the side the equivocator MINES on recovers at day 30: after a window the equivocator is under dust on the other side's sliding table and so not in that side's canonical voter list (C3) at all, whatever its anchored weight. N1b and N2b (below, the equivocator mining on both sides,
P.att_both) carry the bound: 100 blocks a month on each side keeps it in both lists. - N3: a silent set that keeps mining pauses finality for as long as it is silent under v4 as under v2 and v3 (24 h at 34, 40, 45 percent: every checkpoint stalled, first lock 0 minutes after the resume, 0 conflicts). At 31 days of silence neither v4 rule locks: the silent third keeps its share of the SLIDING table, so the signers hold 66 percent and fail Q3's own floor, and the recovery relaxes only the anchored test. The single recovery lock in the v4 recovery row is the first lock after the resume, which passed by the majority test because the anchored checkpoint was then a window old (the signers held every key of both tables); a node reports
recoveredfor one window after such a resume. - N4: keys worth 40 percent withhold while their holder mines at 30 percent: v2 resumes at day 20.9 (the bought weight below a third of the sliding table), v3 at day 30.00 (the frozen table's expiry), v4 pause NEVER (one purchase is a permanent veto: the cost of the pause-only variant), v4 recovery at day 30.00. Compromised keys whose owners strip them by self-equivocation on day 1: the first lock the same day under v4 pause (571 to 736 stalled checkpoints, the first day).
- N6: 35 percent departing at once: v3 day 30.00, v4 pause never in 31 days, v4 recovery day 30.00. 50 percent departing: v4 recovery is a knife edge at exactly half (the simulated set is 49.x to 50.x percent of weight): one seed recovered at day 30.23, the other never; v4 pause never.
N1. The 31-day partition at the frozen table's expiry (the documented case, results M3), under v3 (known-failed), v4 pause (the anchored table) and v4 recovery (the majority-continuity test); seeds 7,11, 31-day partitions, each side retargets and counts only its own blocks
Prediction. v3: both sides of every split under two thirds lock alone at day 30.00 when the frozen table expires, and the heal leaves conflicting locks (the fault). v4 pause: no side locks, ever; 0 conflicts; the heal locks within minutes. v4 recovery: at day 30 a side holding MORE THAN HALF of the anchored table locks alone (the 60 of 60/40, the 55 of 55/45), the other never; the 50/50 split stays paused; 0 conflicts with no equivocator; every pre-heal lock kept.
| split | equivocator (of total) | partition days | rule | first lock per side, day | recovery locks per side | conflicting locks | first conflict | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|---|---|
| 50/50 | none | 31.0 | v3 | 30.00 / 30.00 | 0 / 0 | 2679 to 2701 | 30.03 to 30.06 d | yes | 0 | 0 |
| 50/50 | none | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 50/50 | none | 31.0 | v4 recovery | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 60/40 | none | 31.0 | v3 | 30.00 / 30.00 | 0 / 0 | 2822 to 2870 | 30.00 to 30.02 d | yes | 0 | 0 |
| 60/40 | none | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 60/40 | none | 31.0 | v4 recovery | 30.00 / never | 1 / 0 | 0 | never | yes | 0 | 0 |
| 55/45 | none | 31.0 | v3 | 30.00 / 30.00 | 0 / 0 | 2795 to 2820 | 30.02 to 30.03 d | yes | 0 to 0 | 0 |
| 55/45 | none | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 55/45 | none | 31.0 | v4 recovery | 30.00 / never | 1 / 0 | 0 | never | yes | 0 to 0 | 0 |
N2. The 40/40/20 split under the active-set rules (Q1 to Q3 with the floor at two thirds, Q5 anchored), 150 minutes and 31 days; seeds 7,11
Prediction. Honest three-way: no side holds two thirds of anything, so finality pauses on all three; at day 30 no side holds more than half of the anchored table (40/40/20), so v4 recovery stays paused too; the heal locks at once with 0 conflicts. Two honest 40 sides with a 20% equivocator reaching both (60/60 of the anchored table): no lock for 30 days under either v4; at day 30 both sides pass the recovery majority, the known bound of N1b.
| split | equivocator (of total) | partition days | rule | first lock per side, day | recovery locks per side | conflicting locks | first conflict | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|---|---|
| 40/40/20 honest | none | 150 min | v4 recovery | never / never / never | 0 / 0 / 0 | 0 | never | yes | 0 | 0 |
| 40/40/20 honest | none | 31.0 | v4 pause | never / never / never | 0 / 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 40/40/20 honest | none | 31.0 | v4 recovery | never / never / never | 0 / 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 40/40 + 20% equivocator on both (60/60) | 20% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 40/40 + 20% equivocator on both (60/60) | 20% | 31.0 | v4 recovery | 30.00 / never | 1 / 0 | 0 | never | yes | 0 to 0 | 0 |
N3. Signing stops while mining continues (as J), under rule v4; seeds 7,11
Prediction. A silent set that keeps mining stays in the sliding table and in the anchored table, so finality pauses for as long as it is silent and the first lock comes 0 minutes after it resumes, 0 conflicts (as J). At 31 days of silence the anchored table is a window old: v4 pause stays paused (the silent 34% holds a third of it); v4 recovery locks at day 30 on the signing 66%, more than half of the anchored table.
| silent weight | rule | silent hours | first lock after the stop, min | stalled checkpoints while silent | longest gap without a lock, min | first lock after resume, min | stalls after resume | recovery locks | conflicting locks |
|---|---|---|---|---|---|---|---|---|---|
| 34% | v4 recovery | 24 | never | 2880 to 2889 | 1440 | 0 | 0 | 0 | 0 |
| 40% | v4 recovery | 24 | never | 2880 to 2889 | 1440 | 0 | 0 | 0 | 0 |
| 45% | v4 recovery | 24 | never | 2880 to 2889 | 1440 | 0 | 0 | 0 | 0 |
| 34% | v4 pause | 744 | never | 89286 to 89318 | 44640 | 0 | 0 | 0 | 0 |
| 34% | v4 recovery | 744 | never | 89286 to 89318 | 44640 | 0 | 0 | 1 | 0 |
N4. Old voting keys against fresh hashrate (as K): keys worth 40% of the window withhold their votes while the holder mines at 30% of the network, 31 days; seeds 7,11
Prediction. v2: the pause ends when the bought weight has decayed below a third of the sliding table (day 19 to 20). v3: the frozen table holds the keys at 40% until it expires, day 30. v4 pause: the anchored table holds them for ever: a permanent veto for one purchase (the cost stated in the specification). v4 recovery: day 30, the honest 60% being more than half of the anchored table and the honest 70% of hashrate two thirds of the sliding one. Self-strip: a COMPROMISED key's owner equivocates with it on day 1; the evidence strips it from every table and finality resumes that day.
| rule | the keys | first lock after the purchase, day | stalled checkpoints | recovery locks | holder's share at the end | conflicting locks |
|---|---|---|---|---|---|---|
| v2 | withhold | 20.92 to 20.97 | 64957 to 66272 | 0 | 0.300 to 0.300 | 0 |
| v3 | withhold | 30.00 to 30.00 | 86416 to 86572 | 0 | 0.300 to 0.300 | 0 |
| v4 pause | withhold | never | 89262 to 89373 | 0 | 0.300 to 0.300 | 0 |
| v4 recovery | withhold | 30.00 to 30.00 | 86416 to 86572 | 1 | 0.300 to 0.300 | 0 |
| v4 pause | withhold, owners self-strip on day 1 | 0.00 | 571 to 736 | 0 | 0.300 to 0.300 | 0 |
N6. A set holding x of weight stops mining and signing at once (as M4), 31 days; seeds 7,11
Prediction. v3: the survivors lock when the frozen table expires, day 30. v4 pause: never (the departed weight is anchored; only succession, a strip or the heal moves it). v4 recovery: day 30 for 35% departed (the survivors hold 65% of the anchored table, more than half) and NEVER for 50% departed (exactly half is not more than half): the recovery rule's own limit.
| departed weight | rule | days run | first lock after the event, day | stalled checkpoints | recovery locks | conflicting locks |
|---|---|---|---|---|---|---|
| 35% | v3 | 31 | 30.00 | 86386 to 86511 | 0 | 0 |
| 35% | v4 pause | 31 | never in 31 days | 89272 to 89401 | 0 | 0 |
| 35% | v4 recovery | 31 | 30.00 | 86386 to 86511 | 1 | 0 |
| 50% | v3 | 31 | 30.00 | 86404 to 86514 | 0 | 0 |
| 50% | v4 pause | 31 | never in 31 days | 89287 to 89389 | 0 | 0 |
| 50% | v4 recovery | 31 | 30.23 | 87056 to 89389 | 0 to 1 | 0 |
N1b and N2b, run on igneum-build-4 (16:06 to 16:16 UK, after the box-3 drain stopped the first run): the recovery bound
The equivocator must be in BOTH sides' voter lists at the recovery index to produce two recovery locks: mining on one side only, it is under dust on the other side's sliding table after a window and that side cannot recover (0 conflicts in every such row); mining on both sides (100 blocks a month on each is enough), both sides recover at day 30.00 under v4 recovery and the heal shows 2,800 to 2,889 conflicting locks (50/50 at 10 and 20 percent; 40/40 plus 20 percent the same), 0 under v4 pause; at 34 percent the one-third bound stands under every rule (conflicts from minute 0). Every pre-heal lock kept, first lock after the heal 0 minutes, 0 post-heal stalls in every row.
N1b. The 31-day 50/50 split with an equivocator holding a of total, v4 pause against v4 recovery; seeds 7,11
Prediction. Each side holds (1 - a)/2 + a of the anchored table. Under two thirds (a < 1/3) neither side locks for 30 days under either v4. At day 30 the recovery test (more than half) passes on BOTH sides for any a > 0, so v4 recovery conflicts where v4 pause does not: this is the recovery rule's bound, stated in the specification (the equivocator must outweigh the split's imbalance, 0 at 50/50). At a = 34% both sides hold 67% and lock from minute 0 under every rule (the one-third bound, unchanged).
Two rows per share: the equivocator mines on the first side only (as H, I and M5; after a window it is under dust on the other side's sliding table and so not in that side's voter list, C3's canonical list at C_i), and the equivocator mines on BOTH sides (att_both: 100 blocks a month on each keeps it in both lists). The bound is the second row.
| split | equivocator (of total) | partition days | rule | first lock per side, day | recovery locks per side | conflicting locks | first conflict | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|---|---|
| 50/50, equivocator on side 1 only | 10% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 50/50, equivocator on side 1 only | 10% | 31.0 | v4 recovery | 30.00 / never | 1 / 0 | 0 | never | yes | 0 | 0 |
| 50/50, equivocator mining on both sides | 10% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 50/50, equivocator mining on both sides | 10% | 31.0 | v4 recovery | 30.00 to 30.00 / 30.00 | 1 / 1 | 2800 to 2889 | 30.00 to 30.02 d | yes | 0 to 0 | 0 |
| 50/50, equivocator on side 1 only | 20% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 50/50, equivocator on side 1 only | 20% | 31.0 | v4 recovery | 30.00 / never | 1 / 0 | 0 | never | yes | 0 to 0 | 0 |
| 50/50, equivocator mining on both sides | 20% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 50/50, equivocator mining on both sides | 20% | 31.0 | v4 recovery | 30.00 to 30.00 / 30.00 | 1 / 1 | 2835 to 2860 | 30.01 to 30.02 d | yes | 0 | 0 |
| 50/50, equivocator on side 1 only | 34% | 31.0 | v4 pause | 0.01 to 0.03 / 0.00 to 0.05 | 0 / 0 | 87676 to 87827 | 0.01 to 0.05 d | yes | 0 | 0 |
| 50/50, equivocator on side 1 only | 34% | 31.0 | v4 recovery | 0.01 to 0.03 / 0.00 to 0.05 | 0 / 0 | 87676 to 87827 | 0.01 to 0.05 d | yes | 0 | 0 |
| 50/50, equivocator mining on both sides | 34% | 31.0 | v4 pause | 0.00 to 0.01 / 0.00 to 0.00 | 0 / 0 | 87428 to 87915 | 0.00 to 0.01 d | yes | 0 to 0 | 0 |
| 50/50, equivocator mining on both sides | 34% | 31.0 | v4 recovery | 0.00 to 0.01 / 0.00 to 0.00 | 0 / 0 | 87428 to 87915 | 0.00 to 0.01 d | yes | 0 to 0 | 0 |
N2b. The 40/40 split with a 20% equivocator reaching and MINING on both sides (60/60 of the anchored table), 31 days; seeds 7,11
| split | equivocator (of total) | partition days | rule | first lock per side, day | recovery locks per side | conflicting locks | first conflict | every pre-heal lock kept | first lock after heal, min | stalls in 3 h after heal |
|---|---|---|---|---|---|---|---|---|---|---|
| 40/40 + 20% equivocator on both sides | 20% | 31.0 | v4 pause | never / never | 0 / 0 | 0 | never | yes | 0 | 0 |
| 40/40 + 20% equivocator on both sides | 20% | 31.0 | v4 recovery | 30.00 to 30.00 / 30.00 | 1 / 1 | 2835 to 2860 | 30.01 to 30.02 d | yes | 0 | 0 |
The harness line on real nodes (igneum-build-4, 17:06 to 17:20 UK): rule v3, the 3/3 split past the frozen table's expiry, the known-failed line for rule v4
tools/finality-attacks/v3.mjs split50 on the 0.3.25 node pair (/srv/artefacts/0325-ba294c98/node-lane, three igneumd on the 60x file, six voters, one-way delay 300 ms per proxied link, 1 block/s, WARM 230 s, SPLIT 420 s, HEAL 200 s, finality_v3_activation_daa 0): the frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side), both sides locked alone 192 to 198 s after the cut (7 new locks each), the heal left 4/8/8 conflicting certificates logged and 8 locked indices disagreeing across the three nodes, and locking resumed on both forks: the M3 fault on real fork choice. FAIL by the scenario's own v3 criterion, as required of the known-failed line; the v4 line (no lock on either side past the expiry, 0 conflicts, one chain after the heal) runs on the same command once the node carries finality_v4_activation_daa.
split50-v3: warm 230 s, split 420 s, heal window 200 s, 1 blocks/s in all, delay 300 ms, rule v3; old bound W / (3 R) = 80 s, frozen table expires 240 s after the last lock (W = 120 DAA, R = 0.5 blocks/s per side of a 3/3 split)
| measure | n0 (side A) | n1 (side B) | n2 (side B) |
|---|---|---|---|
| max locked index at the cut | 7 | 7 | 7 |
| new locks during the split (index above 7) | 7 | 7 | 7 |
| first new lock, s after the cut | 198 | 192 | 192 |
| max locked index at the end of the heal window | 18 | 18 | 18 |
| locking resumed after the heal | true | true | true |
| conflicting certificates logged | 4 | 8 | 8 |
| checkpoints held back by the frozen table (debug lines) | 0 | 0 | 0 |
n0 reconnected 102 s after the gate reopened; locked indices disagreeing across the three nodes at the end: 8; weights at the cut: window daa 209, voters 6
The harness line on real nodes under rule v4 (igneum-build-2, 18:0x to 18:55 UK, the node lane): the four runs on the release-2.0.0-node pair bee41b5e (the rule v4 node change, finality_v4_activation_daa 0 through IGNEUM_FIN_OVERRIDE_JSON), the pass line per variant as 6.7 states it
tools/finality-attacks/v3.mjs split50 and split70 on /srv/artefacts/200-bee41b5e/node-lane, the same shape as the v3 line above (three igneumd on the 60x file, six voters, one-way delay 300 ms per proxied link, 1 block/s, WARM 230 s, SPLIT 420 s, HEAL 200 s), under the lease pool. The criterion named per row is the one of 6.7: pause-only and v3, no lock on either side during the split, locking resumed after the heal, 0 conflicting certificates, 0 disagreeing locks; recovery, at most one side locks during the split (the side above half of the anchored table by weight), 0 conflicting certificates, 0 disagreeing locks, every node on the recovering side's chain after the heal. The harness's own [PASS]/[FAIL] print on these runs was the pause line for every split50 run (v3.mjs keyed the line on the recovery switch only after them; the commit that carries these rows).
| run | rule (override) | new locks during the split, n0 (side A) / n1 / n2 (side B) | first new lock, s after the cut | max locked index at the cut / at the end | locking resumed after the heal | conflicting certificates logged | disagreeing locked indices at the end | n0 reconnected, s after the gate reopened | verdict by the 6.7 criterion |
|---|---|---|---|---|---|---|---|---|---|
| v3-known-failed | v3 (finality_v3_activation_daa 0) |
6 / 7 / 7 | 258 / 237 / 237 | 7 / 17 | true on every node | 0 / 0 / 0 (build-4's 0.3.25 run logged 4 / 8 / 8: the line depends on which certificate reaches a node first after the reconnect) | 7 | 72 | FAIL (the known-failed line: both sides locked alone past the frozen table's expiry at 240 s, the disagreeing locks are the fault either way) |
| v4-pause | v4, recovery off (finality_v4_activation_daa 0, finality_v4_recovery false) |
0 / 0 / 0 | none | 7 / 7 | false on every node | 0 / 0 / 0 | 0 | 102 | the split half PASSES (no side locks, "paused: held by weight table 7 past its window, a recovery lock needs more than half" and "the pause stands until two thirds sign" on every node); the heal half read a NODE FAULT: 98 s after the reconnect the anchored table read 84.03 to 100.00 percent signing on every node and finality stayed paused, because the node's anchored test refused everything past one window of the lock when the recovery was off (a pause longer than a window could never end, against 6.5). Fixed on release-2.0.0-node (two thirds of the anchored table locks at any time; the majority test only past the window with the recovery on); the rerun with HEAL 400 s below |
| v4-recovery | v4, recovery on (finality_v4_recovery true) |
7 / 0 / 0 | 195 / none / none | 7 / 18 | true on every node | 0 / 0 / 0 | 0 | 102 | PASS by the recovery criterion: side A held 51.26 percent of the anchored table frozen at lock 7 (block-count jitter in a 3/3 split: a0 a1 a2 against b0 b1 b2 at equal shares), took the "RECOVERY lock at index 11 (rule v4): 51.26% of the anchored table ... signed, more than half; the table re-anchors here" one full window after lock 7 and locked 12 to 18 on its chain; side B read 48.73 percent and stayed paused ("a recovery lock needs more than half"); on the reconnect n1 and n2 took A's index 11 certificate as the re-anchor and its locks 12 to 18; 0 conflicts, 0 disagreement, all three nodes at 18 |
| v4-recovery-split70 | v4, recovery on, split70 (p0..p3 at 0.175 on n1 and n2, q0 q1 at 0.15 on n0) |
0 / 9 / 9 | none / 39 / 39 | 6 / 21 | true on every node | 0 / 0 / 0 | 0 | 72 | PASS (the harness's own line too): the 70 percent side locks from 39 s after the cut (above two thirds of the sliding table it fills and above half of the anchored one, no window needed), the 30 percent side never, the heal takes the 30 side onto the 70 side's chain with 0 conflicts |
Two facts for the reader. The v3 known-failed line's conflicting-certificate count is not the fault's measure (0 here, 4/8/8 on build-4); the disagreeing locked indices after the heal are (7 here, 8 there). The pause line's "resumed" needs a heal window of at least one weight window after the reconnect: with HEAL 200 s and the reconnect at 102 s only 98 s remain, under W = 120 DAA s at 1 block/s, so the fair read is HEAL 400 s (the rerun rows below).
The reruns on the fixed node (igneum-build-2, 19:32 to 20:25 UK): the three rule v4 runs on the 2.0.1 successor pair 12424341 (the pause fix 6872db13: two thirds of the anchored table locks at any time, the majority test only past the window with the recovery on), HEAL 400 s
The same harness and shape, the heal window at 400 s so "resumed" has a full weight window after the reconnect (W = 120 DAA s at 1 block/s; the reconnect came 72 to 102 s after the gate reopened). The result files are in sim/finality-attacks-results/ (the seven runs of tonight, the harness's own markdown). The harness's [PASS]/[FAIL] print on the two split50 runs is the copy on build-2 from before the criterion change; the line in tools/finality-attacks/v3.mjs is keyed on the recovery switch from this commit.
| run | rule (override) | new locks during the split, n0 (side A) / n1 / n2 (side B) | first new lock, s after the cut | max locked index at the cut / at the end | locking resumed after the heal | conflicting certificates | disagreeing locked indices at the end | n0 reconnected, s after the gate reopened | verdict by the 6.7 criterion |
|---|---|---|---|---|---|---|---|---|---|
| v4-pause-fix | v4, recovery off | 0 / 0 / 0 | none | 6 / 25 | true on every node (lock 7 at the reconnect, 22 to 25 by the window's end at 68 to 72 percent of active) | 0 / 0 / 0 | 0 | 102 | PASS: the node fault of the first run is closed; no side locks during the split and the pause ends when two thirds of the anchored table sign again (6.5's "lasts until it signs again") |
| v4-recovery-fix | v4, recovery on | 0 / 8 / 8 | none / 249 / 249 | 5 / 24 | true on every node | 0 / 0 / 0 | 0 | 72 | PASS by the recovery criterion: this time side B held 54.62 percent of the table frozen at lock 5 and took the "RECOVERY lock at index 10" 249 s after the cut, side A paused at 31 to 45 percent ("a recovery lock needs more than half"); n0 took B's chain and certificate at the heal, all three at 24. With the first run (side A at 51.26 percent) the jitter decided the side both ways: the fact for section 7 item 4 |
| v4-recovery-split70-fix | v4, recovery on, split70 | 0 / 9 / 9 | none / 24 / 24 | 7 / 28 | true on every node | 0 / 0 / 0 | 0 | 102 | PASS (the harness's own line too): the 70 percent side locks from 24 s after the cut, the 30 side never, the heal takes it onto the 70 side's chain |
Summary of the seven runs: rule v3 fails its known-failed line as before (both sides lock alone, disagreeing locks after the heal); rule v4 without the recovery never locks during a split and resumes after the heal once the fixed node is used; rule v4 with the recovery lets exactly the side above half of the anchored table lock after a full window and brings the other side onto that chain at the heal; no run under rule v4 logged a conflicting certificate or ended with a disagreeing lock.