Merge remote-tracking branch 'box/master' into site-2.0-followup

This commit is contained in:
igneum-labs 2026-10-08 16:39:55 +00:00
commit ef531e8ccf
20 changed files with 1199 additions and 276 deletions

View file

@ -1,93 +1,98 @@
# The coexistence model: can a specialised supplier earn a normal return while GPUs stay close enough to compete?
# The coexistence model (second cut): can a specialised supplier earn a normal return while GPUs stay close enough to compete?
8 October 2026, 16:1x to 16:5x UK, branch `class-v6-floor-sram`, floor lane 3, on the research lane's word of 17:0x UK
(the founder's accepted third external review: the capex wall is replaced by a coexistence model as the economic
argument, with the profitability surface of `docs/analysis/class-v6/floor/sram-and-floor.md` section 4.4 as its base).
First run on today's measured rows. **Every row is modelled**: the arithmetic is `scratchpad/coexist.py` run on
build-3; the card rows are lane 4's class v5 table (`docs/analysis/class-v6/floor/denominator.md`, section 10.4 of
the design document: measured at the floor where it says so, modelled knees elsewhere), the chip rows the chip model's
(chip-model-v3 5.5 and 5.12, lane B, the k lane's 3.2 pJ per forced op), the prices street approximations. Nothing
here is served; nothing is a measurement of a chip. The founder is not named.
8 October 2026, 16:1x to 17:0x UK, branch `class-v6-floor-sram`, floor lane 3, Igneum 2.0 D4 (the research lane's word
of 17:0x UK; the founder's accepted external review replaces the capex wall with this model; the profitability surface
of `docs/analysis/class-v6/floor/sram-and-floor.md` section 4.4 is its base). Second cut; the section list is the D4
checklist so the pin can be closed line by line. **Every row is modelled**: the arithmetic is `scratchpad/coexist.py`
(the first cut, run on build-3 at 16:2x) and `scratchpad/coexist2.py` (this cut, run on build-4 at 16:52; build-3 was
down from 16:40); the card rows are lane 4's class v5 table (`docs/analysis/class-v6/floor/denominator.md`, section
10.4 of the design document: measured at the floor where it says so, modelled knees elsewhere), the chip rows the chip
model's (chip-model-v3 5.5 and 5.12, lane B, the k lane's 3.2 pJ per forced op), the proving rows the bench table's
shard times with the fleet lane's measured day on Devnet 3 (the 12 GB tier a measured zero for internal proving), the
prices street approximations. The operator simulation beside it is `docs/analysis/class-v6/operator-simulation.md`.
Nothing here is served; nothing is a measurement of a chip. The founder is not named.
The statement under test. **Success**: a specialised supplier earns a normal return and GPUs stay close enough in total
cost, obtainable and useful outside mining, that entrants still compete. **Failure**: a supplier operating privately at
much lower cost exhausts competitors' margins. The result is the set of conditions under which the success statement
holds, never a level the chain stays below. The development cost is SUNK in the mandatory case (the opponent covers
much lower cost exhausts competitors' margins. **The pass line, verbatim**: the model names credible conditions for
sustained commodity participation and names where it fails; a result needing a small network, token appreciation or
scheduled ASIC death has not passed. The development cost is SUNK in the mandatory case (the opponent covers
manufacturing, deployment and operation only); the paid-development cases sit beside it.
## 0. The result in one page
1. **On today's rows the N2 SRAM die fails the success statement in every scenario where its owner can buy a fleet
worth a few percent of the chain's yearly miner revenue.** With development sunk, a 3-year life and power at USD
0.06 per kWh, the die's all-in cost per accepted MH/s-hour is 72 micro-USD against the best GPU owner's 156 to 204
at the same electricity (the 5070 Ti and 5080 at their knees, hardware sunk) and 415 to 588 for a new entrant: 2.2x
to 2.8x on the existing owner, 5.8x to 8.2x on the entrant. At the GPU's more likely 0.12 per kWh it is 3.6x to
4.6x and 7.2x to 9.9x; at 0.25, 6.8x to 8.5x and 10x to 14x. A USD 10 M fleet of such dies takes 37 to 73 percent
of the chain's hash the year it lands at IGN 0.10 to 0.20 and 100 percent the year after on a flat price; a USD
100 M fleet takes 100 percent on landing in every path and then runs at a loss because it is larger than the
revenue (the self-limiting point: -1 to -300 percent margins).
2. **The GDDR7 board chip passes the success statement on its 1-year life and fails it on its 3-year life.** At 1 year
its cost per accepted unit (750 micro-USD at 0.06) is ABOVE every Blackwell owner's and above the Blackwell
entrant's at 0.06 to 0.12 (0.8x to 1.0x the 5080 entrant, 0.6x to 0.7x the 5070 Ti), so it competes only against
Ada and Ampere at dear electricity; at 3 years (307 micro-USD) it is 1.9x to 2.3x the Blackwell entrant and 0.9x
to 1.1x the Blackwell owner: GPUs at their knee stay close enough. Its hardware is the term that holds it (USD 5.6
per MH/s with the core and the system against the die's 0.8), not its joules.
3. **The electricity axis is explicit and it is the GPU's.** The break-even electricity price above which an EXISTING
GPU owner with sunk hardware cannot match the die at 0.06 is 0 to 5 cents per kWh for every card (a 3-year die) and
1 to 5 cents (a 1-year die): no grid price in the world keeps a GPU owner level with a sunk SRAM die. Against the
GDDR7 board at 3 years the owner's break-even is 9 to 15 cents on Blackwell, 4 to 6 on Ada and Ampere; at 1 year 27
to 40 cents on Blackwell. Read the other way, the die breaks even against a 5080 owner at 0.12 only when the die
pays 47 to 60 cents per kWh; the board at 3 years when it pays 1 to 9 cents.
4. **Accessible supply is the structural fact.** At the GPU entry equilibrium (revenue per MH/s-hour equal to a new
5070 Ti's cost at 0.12) the chain's hash is 2.6 TH/s at IGN 0.03, 8.8 at 0.10, 26 at 0.30, 88 at 1.00: that is
34,000 to 1.1 M 5070 Ti-class cards, which exist in the world's installed base, against 480 to 16,000 SRAM dies, 8
to 270 wafers of N2. One supplier holds the chain at every price in the window; the dependence on individual
suppliers is total for the die and partial for the board (16,000 to 530,000 boards, a Bitmain-class run).
5. **The conditions under which the success statement holds**, read off the tables: (a) the chip's all-in cost per
accepted unit stays within about 1.5x of the best GPU owner's at the same electricity, which on today's rows is true
of the GDDR7 board at a life of 1 to 2 years and false of the die at every life; (b) the chip's hardware per MH/s is
not below about a quarter of the GPU's annualised hardware (the board's 0.7x to 1.9x the 5080 entrant passes, the
die's 5x to 14x fails); (c) no single buyer can fund a fleet above about a third of the chain's hash for under a
year's miner revenue (true for the board above IGN 0.3; false for the die at every price: a wafer is USD 30,000);
(d) GPUs keep a resale market and a use outside mining (true for every card in the population; the chip has none,
which is why its life is the axis that moves everything); (e) the per-joule gap at the honest knee stays under about
3x (the board at 2.2x to 2.6x against Blackwell passes; the die at 3.7x to 4.5x does not).
6. **What the chain controls, and what it does not.** It controls the honest cost per accepted unit (the operating
point: the lock is worth 34 to 41 percent of a Blackwell card's draw), the chip's life against a fixed lane (the
180-day rotation; nothing against a programmable one), and the visibility of a concentrated supplier (the share
detector). It does not control the sunk development cost, electricity prices, the token price or a buyer's budget.
On today's rows the SRAM die, once it exists, cannot be held to coexistence by anything in the hash; the board can.
The condition that holds the die is that it does not get built, which is the surface of section 4.4 (a USD 150 M
project at a third of the chain needs IGN 0.73 over three years), and that is a statement about who pays, not about
the chain staying below a level.
1. **The DRAM-board chip (a GPU's memory system with a programmable core) passes the success statement on today's
rows; the N2 SRAM die fails it once it exists with its development sunk.** With development sunk, a 3-year life
and power at USD 0.06 per kWh, the board's cost per accepted MH/s-hour is 307 micro-USD against the best GPU
owner's 156 to 204 at the same electricity and 415 to 588 for a new entrant (0.5x to 0.7x the owner, 1.4x to 1.9x
the entrant); the die's is 72 (2.2x to 2.8x the owner, 5.8x to 8.2x the entrant; at the GPU's 0.12, 3.6x to 4.6x
and 7.2x to 9.9x). In the five-year runs with a per-class supply curve the board at a sunk USD 10 M holds 4.5 to
24 percent of the chain at a 24 to 69 percent margin with 6 to 16 of 17 GPU classes above water in every path; a
USD 10 M die fleet holds 50 to 70 percent on landing and takes the chain by year 3 to 5 on flat and shrinking paths
(0 of 17 classes above water); a USD 100 M die fleet takes it on landing in every path and runs at a loss.
2. **The tariff advantage is explicit and separate from the hardware advantage (section 1).** The review's 6.25x
illustration (a 1.5x chip at 0.06 against a GPU at 0.25) is the first row; on the measured rows the operating
advantage is the joule ratio times the tariff ratio (2.2x to 4.5x times 1x to 4.2x for the board, 3.7x to 7.8x for
the die), the hardware advantage 1.3x to 4x for the board and 9x to 29x for the die, and the total a third to a
half of the operating figure because power is 60 to 70 percent of an owner's cost and 30 to 40 of an entrant's.
3. **The break-even electricity price is tabled for all 17 classes (section 3).** A GPU owner with sunk hardware
matches the board at 3 years up to 9 to 15 cents per kWh on Blackwell and 4 to 6 on Ada and Ampere; at 1 year up
to 27 to 40 cents; it matches the die at 0 to 5 cents. No GPU ENTRANT matches the board at 3 years or the die at
any life at any positive price except the 5070 Ti against the 1-year board (25 cents): the entrant rows are where
the GPU side loses first.
4. **Proving is the second income the chip does not have (section 5).** A 16 GB or larger card earns USD 3 to 7 a
day from internal proving at IGN 0.10 (the H100 16, the A100 9) against USD 0.2 to 1.7 a day from mining at the
GPU equilibrium, and USD 5 to 12 under a proving spike; the SRAM die, the DRAM board, the 9070 XT and the Mac earn
nothing from it, and the 12 GB tier earns nothing from internal proving today (measured). A GPU displaced from
mining by a chip goes to proving (the operator simulation's D1: 1,500 of 9,177 cards), which is the mechanism that
keeps commodity participation when the mining margin is gone.
5. **Accessible supply and dependence (section 11).** At the GPU equilibrium the chain's hash is 5.6 / 9.6 / 20 / 42
TH/s at IGN 0.03 / 0.10 / 0.30 / 1.00, which is 12 / 21 / 44 / 95 percent of the installed base available to
mining (44.7 TH/s, approximate) across 16 NVIDIA classes, AMD and Apple; the same hash is 17 / 29 / 60 / 128 N2
wafers from one supplier, or 34,000 to 255,000 DRAM boards. Dependence on a single supplier is total for the die,
partial for the board, nil for the GPU side.
6. **The conditions under which the success statement holds (section 12)**, each with its number: (a) the chip's
all-in cost within about 1.5x of the best GPU owner's at the GPU's electricity; (b) the chip's hardware per MH/s
not under about a quarter of the GPU entrant's; (c) a fleet above a third of the chain costing more than a year's
miner revenue; (d) GPUs keeping a resale market and a use outside mining; (e) the per-joule gap at the honest knee
under about 3x; (f) the supplier's margin a normal return that does not rise with the halvings; (g) a second
income (proving) for the commodity side that the specialised supplier cannot enter. The board passes all seven at
a 1 to 3 year life; the die fails (a), (b), (c), (e) and (f) at every life, price path and electricity price once
it exists. **Against the pass line**: the board's pass does not need a small network (it holds at 42 TH/s and IGN
1.00), token appreciation (it holds on the flat and shrinking paths) or scheduled ASIC death (its life axis is 1 to
5 years and the 180-day rotation is not what holds it); the die's failure is not cured by any of the three either,
and the only condition that holds the die is that nobody pays to build it (the surface: IGN 0.73 for a USD 150 M
project at a third of the chain over three years), which is an investor's decision, not a level the chain stays
below. The model names where it fails: a sunk SRAM die of USD 10 M or more at any price in the window.
## 1. The measure: cost per accepted unit of work
## 1. The tariff advantage beside the hardware advantage (D4: the electricity axis explicit)
Cost per accepted MH/s-hour (micro-USD), both sides: annualised hardware plus power plus hosting, failures and fees,
divided by accepted work. Accepted work is 97 percent of raw (rejects 0.5 percent, downtime 2.0, epoch preparation and
propagation 0.5; approximate from the devnet's share rates), the pool fee 1 percent. The two GPU situations: the
**existing owner** (hardware sunk; pays power, a wear allowance of 5 percent of the used price a year, fees; the
alternative use is the card's rental yield, reported in section 2 and not deducted) and the **new entrant** (buys new
or used, operates two years, resells at the table's fraction). Hosting: 0 at home, USD 0.02 per kWh-equivalent at a
farm (the chip's case). Electricity axis: USD 0.06, 0.12, 0.25 per kWh.
The chip at 0.06 per kWh with farm hosting; the GPU at 0.06, 0.12 and 0.25. "Operating" is joules times tariff;
"hardware" the GPU entrant's annualised hardware over the chip's; the total against the existing owner and the entrant.
| Input | Value | Label |
|---|---|---|
| Card joules per hash at the floor (the knee with the knobs) and at stock | lane 4's class v5 table: 5090 2.33 / 3.48, 5080 2.06 / 3.48, 5070 Ti 1.70 / 2.84, 5070 1.75 / 2.99, 5060 Ti 2.36 / 4.02, 5060 2.21 / 3.76, 4090 3.58 / 5.00, 4080 3.51 / 4.92, 4070 3.58 / 5.82, 4060 Ti 3.81 / 5.32, 3090 4.51 / 5.03, 3080 4.20 / 4.54, 3060 5.77 / 6.40, 9070 XT 7.90 / 10.7, H100 2.00 / 2.58, A100 2.89 / 2.99, M5 Max 1.40 microjoules | measured where lane 4 says so (5090, 5080, 4070, 9070 XT, M5 Max floors; most stock rows), modelled knees elsewhere |
| Card rates at the floor | 5090 134.8, 5080 71.2, 5070 Ti 77, 5070 41, 5060 Ti 19, 5060 17, 4090 58, 4080 45, 4070 31.1, 4060 Ti 17.6, 3090 37.8, 3080 40.8, 3060 23.8, 9070 XT 18.9, H100 90, A100 60, M5 Max 27.1 MH/s | measured where the bench table has the row; approximate elsewhere |
| Prices new / used, resale after two years | 5090 2,600 / 2,200 / 55 percent; 5080 1,100 / 900 / 50; 5070 Ti 800 / 650 / 50; 5070 560 / 450 / 50; 5060 Ti 450 / 360 / 45; 5060 310 / 250 / 45; 4090 1,700 / 1,300 / 45; 4080 1,000 / 700 / 40; 4070 550 / 400 / 40; 4060 Ti 420 / 290 / 35; 3090 900 / 650 / 30; 3080 450 / 330 / 25; 3060 260 / 190 / 25; 9070 XT 650 / 520 / 45; H100 25,000 / 18,000 / 50; A100 10,000 / 6,000 / 35; M5 Max 4,000 / 3,200 / 55 | approximate street, October 2026 |
| Chip joules per hash (class v5, the shadow at 3.2 pJ per forced op, lane 4's chip columns) | GDDR7 board with an N5 core 0.79; HBM3 one stack 0.65; N2 SRAM die with the core 0.46 (W = 4); the die at the bare-lane floor 0.165 | modelled |
| Chip hardware, USD per MH/s, silicon and board plus 30 percent system (PSU, chassis, cooling) | GDDR7 board 5.6 (4.3 with the core die, chip-model 5.5 and research 16.1); HBM3 8.6; SRAM die 0.8 (0.6 with the board, lane B) | modelled, approximate |
| Chip failures, resale | 3 percent a year; no resale (single use) | approximate |
| Chip lives | 0.5, 1, 2, 3, 5 years (0.5 is the fixed-lane chip under the 180-day rotation; 3 the programmable chip's default) | the review's axis |
| Emission to miners | 0.77 / 0.80 / 0.40 / 0.40 / 0.20 B IGN in years 1 to 5 (the spec's constant, 80 percent to miners) | spec 05 |
| GPU equilibrium | while any GPU mines, revenue per MH/s-hour settles at the cheapest entrant's cost (a new 5070 Ti at 0.12: 521 micro-USD) during growth and falls to the owners' costs during shrinkage; a GPU generation at year 3 cuts the entrant's cost 25 percent (1.5x per joule at the same price) with the old card resold at the table's fraction | modelled rule |
| Row | Joules ratio | GPU tariff over chip tariff | Operating advantage | Hardware advantage | Total vs owner / entrant | Label |
|---|---|---|---|---|---|---|
| The review's illustration: a 1.5x chip at 0.06 against a GPU at 0.25 | 1.5x | 4.17x | **6.25x** | n/a | n/a | the review |
| GDDR7 board, 3 y, vs 5070 Ti at 0.06 / 0.12 / 0.25 | 2.2x | 1x / 2x / 4.2x | 2.2x / 4.3x / 9.0x | 1.3x | 0.5x / 1.4x; 0.9x / 1.7x; 1.6x / 2.4x | modelled |
| GDDR7 board, 3 y, vs 5080 | 2.6x | the same | 2.6x / 5.2x / 10.9x | 1.9x | 0.7x / 1.9x; 1.1x / 2.3x; 2.0x / 3.2x | modelled |
| GDDR7 board, 3 y, vs 4090 | 4.5x | | 4.5x / 9.1x / 18.9x | 4.0x | 1.2x / 3.8x; 1.9x / 4.6x; 3.5x / 6.2x | modelled |
| N2 SRAM die, 3 y, vs 5070 Ti | 3.7x | | 3.7x / 7.4x / 15.4x | 9.3x | 2.2x / 5.8x; 3.6x / 7.2x; 6.8x / 10.4x | modelled |
| N2 SRAM die, 3 y, vs 5080 | 4.5x | | 4.5x / 9.0x / 18.7x | 13.8x | 2.8x / 8.2x; 4.6x / 9.9x; 8.5x / 13.8x | modelled |
| N2 SRAM die, 3 y, vs 4090 | 7.8x | | 7.8x / 15.6x / 32.4x | 28.7x | 5.0x / 16.4x; 8.1x / 19.5x; 14.8x / 26.2x | modelled |
## 2. The GPU reference population: cost per accepted MH/s-hour (micro-USD)
Reading: the tariff multiplies the operating advantage one for one, as the review says, and the total is a third to a
half of it; the board's total against a Blackwell card at its knee is under 1x (owner) to 2.4x (entrant) across the
whole axis at 3 years, the coexistence band; the die's 2.2x to 14x is not.
| Card | Owner at 0.06 / 0.12 / 0.25 | Entrant, new, 2 years, at 0.06 / 0.12 / 0.25 | Entrant, used | Hardware share of the entrant's cost at 0.12 | Wh per MH/s-hour | Alternative use (rental yield, approximate) | Label |
## 2. Cheap and dear electricity: the GPU population's cost per accepted unit (D4: cheap and dear electricity)
Cost per accepted MH/s-hour (micro-USD; accepted = 97 percent of raw: rejects 0.5, downtime 2.0, epoch preparation
and propagation 0.5; pool fee 1 percent). The existing owner (hardware sunk; power, 5 percent wear, fees) and the new
entrant (buys new or used, runs two years, resells at the table's fraction).
| Card | Owner at 0.06 / 0.12 / 0.25 | Entrant, new, at 0.06 / 0.12 / 0.25 | Entrant, used | Hardware share of the entrant at 0.12 | Wh per MH/s-hour | Alternative use (rental yield, approximate) | Label |
|---|---|---|---|---|---|---|---|
| RTX 5090 | 243 / 388 / 704 | 661 / 807 / 1,122 | 800 / 945 / 1,261 | 64 percent | 2.33 | USD 0.3 to 0.5 an hour on a rental market: 2,200 to 3,700 micro-USD per MH/s-hour, 3x to 5x its mining cost | measured floor |
| RTX 5090 | 243 / 388 / 704 | 661 / 807 / 1,122 | 800 / 945 / 1,261 | 64 percent | 2.33 | USD 0.3 to 0.5 an hour, 3x to 5x its mining cost | measured floor |
| RTX 5080 | 204 / 332 / 611 | 588 / 716 / 995 | 650 / 779 / 1,058 | 64 | 2.06 | USD 0.15 to 0.25 an hour | measured floor |
| RTX 5070 Ti | 156 / 263 / 493 | 415 / 521 / 751 | 453 / 560 / 790 | 59 | 1.70 | USD 0.1 to 0.2 an hour | modelled knee |
| RTX 5070 | 175 / 284 / 521 | 515 / 624 / 861 | 558 / 668 / 905 | 65 | 1.75 | | modelled knee |
@ -103,197 +108,254 @@ farm (the chip's case). Electricity axis: USD 0.06, 0.12, 0.25 per kWh.
| RX 9070 XT | 657 / 1,151 / 2,220 | 1,617 / 2,111 / 3,180 | 1,668 / 2,162 / 3,231 | 53 | 7.90 | | measured |
| H100 (hosted) | 1,313 / 1,438 / 1,709 | 8,374 / 8,499 / 8,770 | 7,880 / 8,004 / 8,275 | 97 | 2.00 | USD 2 to 3 an hour: never mines | modelled lock |
| A100 (used) | 775 / 955 / 1,346 | 6,615 / 6,796 / 7,187 | 4,388 / 4,568 / 4,960 | 95 | 2.89 | USD 1 an hour: never mines | modelled |
| Apple M5 Max (reported, not headlined) | 789 / 876 / 1,066 | 4,033 / 4,120 / 4,310 | 4,690 / 4,778 / 4,967 | 96 | 1.40 | a workstation: mines only as an owner | measured |
| Apple M5 Max (reported, not headlined) | 789 / 876 / 1,066 | 4,033 / 4,120 / 4,310 | 4,690 / 4,778 / 4,967 | 96 | 1.40 | a workstation: an owner only | measured |
Reading: the owner's cost is 60 to 70 percent electricity on every consumer card, so the electricity price is the
GPU's whole variable; the entrant's cost is 60 to 70 percent hardware, so the card price and its resale are the
entrant's whole variable. The best honest owner on today's rows is a 5070 Ti at its knee (156 micro-USD at 0.06); the
best entrant the same card (415). Datacentre parts and the Mac never enter as entrants (hardware 95 percent) and mine
only as owners with nothing better to do, which their rental yields say they always have.
The chip rows, development sunk (the mandatory case), farm hosting USD 0.02 per kWh-equivalent on top:
## 3. The chip rows
### 3.1 Development sunk (the mandatory case): cost per accepted MH/s-hour, micro-USD, farm hosting
| Chip | Life 0.5 y at 0.06 / 0.12 / 0.25 | 1 y | 2 y | 3 y | 5 y | Hardware / power split at 0.06, 3 y | Label |
| Chip | 0.5 y at 0.06 / 0.12 / 0.25 | 1 y | 2 y | 3 y | 5 y | Hardware / power at 0.06, 3 y | Label |
|---|---|---|---|---|---|---|---|
| GDDR7 board with an N5 core | 1,414 / 1,463 / 1,570 | 750 / 799 / 906 | 418 / 467 / 574 | 307 / 356 / 463 | 219 / 268 / 375 | 239 / 65 | modelled |
| GDDR7 board with an N5 core | 1,414 / 1,463 / 1,570 | 750 / 799 / 906 | 418 / 467 / 574 | **307 / 356 / 463** | 219 / 268 / 375 | 239 / 65 | modelled |
| HBM3 one stack with a core | 2,123 / 2,164 / 2,252 | 1,104 / 1,145 / 1,233 | 594 / 635 / 723 | 425 / 465 / 553 | 289 / 329 / 417 | 367 / 54 | modelled |
| N2 SRAM die with the core | 226 / 255 / 317 | 134 / 163 / 225 | 87 / 116 / 178 | **72 / 101 / 163** | 60 / 88 / 151 | 33 / 38 | modelled |
| N2 SRAM die at the bare-lane floor | 202 / 212 / 235 | 109 / 120 / 142 | 63 / 73 / 96 | 47 / 58 / 80 | 35 / 45 / 68 | 33 / 14 | modelled, the worst case |
### 3.2 Development paid: the same rows with `C_dev` spread over the fleet and the life
## 3. Break-even electricity prices for all 17 classes (D4: the output per class)
The fleet is sized to a share `q` of the network's hash at the GPU equilibrium (a new 5080 at 0.12 as the marginal
entrant). All-in cost per accepted MH/s-hour of the SRAM die (the GDDR7 board in brackets), at 0.06:
Cents per kWh at which the GPU's cost per accepted unit equals the chip's all-in at 0.06 per kWh; the OWNER figure
(hardware sunk) and the ENTRANT figure (hardware bought); "under 0" means no positive price matches.
| IGN price | Miner revenue (year 3) | Network hash | `C_dev` 20 M, 1 y, `q` 0.3 / 1.0 | 20 M, 3 y, 0.3 / 1.0 | 150 M, 1 y, 0.3 / 1.0 | 150 M, 3 y, 0.3 / 1.0 |
|---|---|---|---|---|---|---|
| 0.03 | USD 12 M | 1.9 TH/s | 4,277 / 1,377 (4,893 / 1,993) | 1,453 / 486 (1,688 / 721) | 31,211 / 9,457 | 10,431 / 3,180 |
| 0.10 | 40 M | 6.4 TH/s | 1,377 / 507 (1,993 / 1,123) | 486 / 196 (721 / 431) | 9,457 / 2,931 | 3,180 / 1,004 |
| 0.30 | 120 M | 19 TH/s | 548 / 258 (1,164 / 874) | 210 / 113 (445 / 349) | 3,242 / 1,066 | 1,108 / 383 |
| 1.00 | 400 M | 64 TH/s | 258 / 171 (874 / 787) | 113 / 84 (349 / 320) | 1,066 / 414 | 383 / 165 |
Reading: a paid development cost puts every chip ABOVE the best GPU entrant (415 to 588) at IGN 0.10 and below, and
the SRAM die below it only from IGN 0.30 on a 3-year life or IGN 1.00 on a 1-year life; the GDDR7 board with paid
development is never below the Blackwell entrant inside the window. The sunk case is therefore the whole threat, and
it is the case the review makes mandatory. The derivative design (a revision at 0.3 x `C_dev`, section 4.4 of the
floor file) moves the paid rows a third of the way to the sunk rows.
## 4. The cost advantage, separated
The chip at 0.06 per kWh and farm hosting; the GPU at 0.06, 0.12 and 0.25. "Operating" is power only (joules times
electricity); "hardware" the annualised hardware of the GPU entrant over the chip's.
| Chip, life | Against | Total: owner / entrant, at 0.06 | At 0.12 | At 0.25 | Operating advantage at 0.06 / 0.12 / 0.25 (joules x electricity) | Hardware advantage |
|---|---|---|---|---|---|---|
| GDDR7 board, 1 y | RTX 5080 | 0.3x / 0.8x | 0.4x / 1.0x | 0.8x / 1.3x | 2.6x / 5.2x / 10.9x (2.6 x 1, 2, 4.2) | 0.7x |
| | RTX 5070 Ti | 0.2x / 0.6x | 0.4x / 0.7x | 0.7x / 1.0x | 2.2x / 4.3x / 9.0x | 0.5x |
| | RTX 4090 | 0.5x / 1.6x | 0.8x / 1.9x | 1.4x / 2.5x | 4.5x / 9.1x / 18.9x | 1.4x |
| | RTX 3080 (used) | 0.4x / 1.0x | 0.8x / 1.4x | 1.5x / 2.1x | 5.3x / 10.6x / 22.2x | 0.7x |
| GDDR7 board, 3 y | RTX 5080 | 0.7x / 1.9x | 1.1x / 2.3x | 2.0x / 3.2x | the same | 1.9x |
| | RTX 5070 Ti | 0.5x / 1.4x | 0.9x / 1.7x | 1.6x / 2.4x | | 1.3x |
| | RTX 4090 | 1.2x / 3.8x | 1.9x / 4.6x | 3.5x / 6.2x | | 4.0x |
| | RTX 3080 (used) | 1.0x / 2.5x | 1.9x / 3.3x | 3.7x / 5.2x | | 2.1x |
| N2 SRAM die, 1 y | RTX 5080 | 1.5x / 4.4x | 2.5x / 5.4x | 4.6x / 7.4x | 4.5x / 9.0x / 18.7x | 4.9x |
| | RTX 5070 Ti | 1.2x / 3.1x | 2.0x / 3.9x | 3.7x / 5.6x | 3.7x / 7.4x / 15.4x | 3.3x |
| | RTX 4090 | 2.7x / 8.8x | 4.3x / 10.5x | 8.0x / 14.1x | 7.8x / 15.6x / 32.4x | 10.1x |
| N2 SRAM die, 3 y | RTX 5080 | 2.8x / 8.2x | 4.6x / 9.9x | 8.5x / 13.8x | the same | 13.8x |
| | RTX 5070 Ti | **2.2x / 5.8x** | **3.6x / 7.2x** | 6.8x / 10.4x | | 9.3x |
| | RTX 4090 | 5.0x / 16.4x | 8.1x / 19.5x | 14.8x / 26.2x | | 28.7x |
| | RTX 3080 (used) | 4.3x / 10.5x | 8.0x / 14.1x | 15.9x / 22.0x | | 14.7x |
| | Apple M5 Max (reported) | 11.0x / 56x | 12.2x / 57x | 14.8x / 60x | 3.0x / 6.1x / 12.7x | 118x |
Reading: the electricity axis multiplies the operating advantage one for one (a 2.6x chip at 0.06 against a GPU at
0.25 is 10.9x on power alone, the review's point), but on the consumer cards power is 60 to 70 percent of the owner's
cost and 30 to 40 percent of the entrant's, so the total advantage is a third to a half of the operating one. The
GDDR7 board's total advantage over a Blackwell card at its knee is under 1x (owner) to 2.3x (entrant) across the whole
electricity axis at a 3-year life, which is the coexistence band; the die's is 2.2x to 14x, which is not.
## 5. The replacement economics
For an existing GPU owner, switching pays when the chip's all-in cost per accepted unit is below the owner's
OPERATING cost (the hardware is sunk, the resale value is the only thing the switch recovers). For a new entrant, when
the chip's all-in is below the GPU entrant's all-in. The chip must be purchasable for either (hardware sales; the
manufacturer keeps about half the operator's profit through the price, floor file 4.4 table B, which roughly doubles
the chip's hardware term for the buyer).
| Who | Against the GDDR7 board (bought, hardware term x2) | Against the SRAM die (bought, x2) | What it means |
|---|---|---|---|
| A Blackwell owner at 0.06 to 0.12 | never switches: the bought board at 3 years is 550 to 600 micro-USD against the owner's 156 to 332 | switches at 0.12 (the bought die 105 to 134 against 263 to 332) and is near indifferent at 0.06 (105 against 156 to 204) | the die replaces Blackwell owners at normal grid prices; the board never does |
| An Ada or Ampere owner at 0.12 | near indifferent at 3 years (550 to 600 against 524 to 768); switches at 0.25 | switches at every electricity price | the board retires the oldest cards only at dear electricity, which the generation upgrade does anyway |
| A new entrant choosing between a new 5070 Ti and a bought chip at 0.12 | the board at 3 years (about 600) is 1.15x the card's 521: the card wins; at 1 year the card wins 2x | the bought die (134) is 0.26x the card: the die wins 4x | an entrant market with a bought SRAM die has no GPU entrants; one with a bought board keeps them |
| The GPU generation upgrade (year 3: 1.5x per joule at the same price, the old card resold) | cuts the entrant's cost about 25 percent and the owner's power 33 percent: the board at 3 years then reads 1.5x to 1.7x the new entrant, still in band | the die's advantage falls 25 to 33 percent, from 7x to 5x on the entrant: still out of band | the GPU side's own curve narrows the board's gap to nothing by the second generation and never closes the die's |
## 6. Break-even electricity prices
(a) The GPU electricity price above which an EXISTING owner (hardware sunk) cannot match the chip's all-in cost at
0.06 per kWh:
| Chip, life | 5090 | 5080 | 5070 Ti | 5070 | 5060 Ti | 4090 | 4080 | 4070 | 3090 | 3080 | 3060 | 9070 XT | M5 Max |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| GDDR7 board, 1 y | 27 c | 32 c | 40 c | 38 c | 26 c | 17 c | 18 c | 18 c | 14 c | 16 c | 12 c | 7 c | 3 c |
| GDDR7 board, 3 y | 9 c | 11 c | 15 c | 13 c | 8 c | 5 c | 6 c | 6 c | 4 c | 6 c | 4 c | 2 c | under 0 |
| SRAM die, 1 y | 1.5 c | 2.7 c | 4.7 c | 3.8 c | 0.9 c | 0 c | 1.1 c | 1.5 c | 0.7 c | 2.0 c | 1.4 c | under 0 | under 0 |
| SRAM die, 3 y | under 0 | under 0 | 1.2 c | 0.4 c | under 0 | under 0 | under 0 | under 0 | under 0 | 0.5 c | 0.4 c | under 0 | under 0 |
(b) The chip electricity price at which its all-in cost equals a GPU owner's at 0.12 (how much dearer the chip's
hosting can be and still match): the GDDR7 board at 3 years 1 to 9 cents against Blackwell (it must be hosted cheaper
than the GPU to match an owner) and 75 cents against the Mac; at 1 year it cannot match a Blackwell owner at any
price. The SRAM die matches a 5070 Ti owner while paying up to 46 cents (3 years) or 33 cents (1 year), a 5080 owner
up to 60 or 47, the Mac up to 174.
Reading: the board lives or dies on the GPU's grid price, which is the review's electricity axis doing what it should;
the die does not see the axis at all.
## 7. Five years: growing, flat and shrinking networks, with the GPU side reacting
The sunk chip enters at the start of year 2 with a fleet bought for a budget `B` (USD 1 M, 10 M, 100 M at USD 0.8
per MH/s for the die: 1.3, 13 and 128 TH/s); revenue per MH/s-hour `r` settles at the cheapest GPU entrant's cost
(a new 5070 Ti at 0.12, 521 micro-USD; 391 after the year-3 generation) while entry continues, and when the chip's
fleet alone exceeds the hash that revenue supports, GPU owners exit in cost order until the survivors' costs are
covered or none are. Three price paths: growing (x2 a year from 0.10), flat (0.10), shrinking (x0.5 a year from
0.30). Emission halves in year 3 and year 5.
| Path, budget | Year 2 | Year 3 | Year 5 | Reading |
| Class | GDDR7 board 1 y: owner / entrant | GDDR7 board 3 y | SRAM die 1 y | SRAM die 3 y |
|---|---|---|---|---|
| Growing, USD 1 M | chip 3.7 percent of 35 TH/s; `r` 521; margin 86 percent | 2.7 percent; owners all above water | 1.4 percent of 93 TH/s | coexistence: the supplier earns 82 to 86 percent margins on a tiny share, GPUs set the price |
| Growing, USD 10 M | 37 percent of 35 TH/s | 27 percent | 14 percent | coexistence by dilution only: the share falls as the chain grows and the fleet is fixed; the supplier's margin stays 82 to 86 percent, far above normal |
| Growing, USD 100 M | 100 percent; 0 of 17 owner classes above water; `r` falls to 142; margin 49 percent | the same | 100 percent; 2 of 17 owner classes above water at `r` 285 | failure: one buyer holds the chain for five years, GPUs exit in year 2 and only the two best classes could return in year 4 |
| Flat, USD 1 M | 7 percent of 17.5 TH/s | 11 percent | 22 percent of 5.8 TH/s | coexistence, the share rising with each halving |
| Flat, USD 10 M | 73 percent | 100 percent; 3 of 17 owner classes above water | 100 percent; 0 of 17 | failure by year 3: the halving does the rest |
| Flat, USD 100 M | 100 percent; margin -1 percent | margin -102 percent | -305 percent | failure for both: the fleet is larger than the revenue; the buyer loses money and the GPUs are gone (the self-limiting point) |
| Shrinking, USD 1 M | 5 percent | 15 percent | 100 percent; 3 of 17 above water | failure in year 5 at USD 4 M of revenue: even a USD 1 M fleet is the chain when the chain is small |
| Shrinking, USD 10 M | 49 percent | 100 percent; 1 of 17 | 100 percent; margin -116 percent | failure from year 3 |
| The GDDR7 board (sunk), flat, USD 10 M | 10 percent of 17.5 TH/s; margin 41 percent | 15 percent; margin 21 percent | 31 percent; margin 21 percent | coexistence: a normal return (21 to 41 percent gross) on a minority share with GPU entrants still setting the price |
| RTX 5090 | 27 / 10 | 9 / under 0 | 1.5 / under 0 | under 0 / under 0 |
| RTX 5080 | 32 / 14 | 11 / under 0 | 2.7 / under 0 | under 0 / under 0 |
| RTX 5070 Ti | 40 / 25 | 15 / 0 | 4.7 / under 0 | 1.2 / under 0 |
| RTX 5070 | 38 / 19 | 13 / under 0 | 3.8 / under 0 | 0.4 / under 0 |
| RTX 5060 Ti 16 GB | 26 / under 0 | 8 / under 0 | 0.9 / under 0 | under 0 / under 0 |
| RTX 5060 | 29 / 7 | 10 / under 0 | 2.0 / under 0 | under 0 / under 0 |
| RTX 4090 | 17 / under 0 | 5 / under 0 | 0 / under 0 | under 0 / under 0 |
| RTX 4080 | 18 / under 0 | 6 / under 0 | 1.1 / under 0 | under 0 / under 0 |
| RTX 4070 | 18 / 3 | 6 / under 0 | 1.5 / under 0 | under 0 / under 0 |
| RTX 4060 Ti 16 GB | 16 / under 0 | 5 / under 0 | 0.9 / under 0 | under 0 / under 0 |
| RTX 3090 (used) | 14 / under 0 | 4 / under 0 | 0.7 / under 0 | under 0 / under 0 |
| RTX 3080 (used) | 16 / 6 | 6 / under 0 | 2.0 / under 0 | 0.5 / under 0 |
| RTX 3060 (used) | 12 / 4 | 4 / under 0 | 1.4 / under 0 | 0.4 / under 0 |
| RX 9070 XT | 7 / under 0 | 2 / under 0 | under 0 / under 0 | under 0 / under 0 |
| H100 (hosted) | under 0 / under 0 | under 0 / under 0 | under 0 / under 0 | under 0 / under 0 |
| A100 (used) | 5 / under 0 | under 0 / under 0 | under 0 / under 0 | under 0 / under 0 |
| Apple M5 Max | 3 / under 0 | under 0 / under 0 | under 0 / under 0 | under 0 / under 0 |
Reading: the row that passes the success statement as the review states it is the GDDR7 board at a sunk USD 10 M (a
21 to 41 percent gross margin, a 10 to 31 percent share, GPUs setting the price, entrants competing). The SRAM die
passes only at a fleet under about 1 percent of the chain's yearly miner revenue in a growing network, and fails in
every flat or shrinking path by year 3 to 5; above a tenth of a year's revenue it takes the chain in every path. The
failure is not a margin the die extracts (its margins collapse once it is the chain); it is the exit of every GPU
class, which is the review's definition.
Reading: the board at 1 year is beaten by every Blackwell owner below 26 to 40 cents and by the 5070 Ti entrant below
25; at 3 years by Blackwell owners below 8 to 15 cents and by no entrant. The die is matched by no entrant and by
owners only below 0 to 5 cents. The GPU side's electricity price is the board's whole variable and irrelevant to the
die.
## 8. Accessible supply and the dependence on individual suppliers
## 4. GPU replacement and resale on both sides, with the generation step on the chip side too (D4)
| IGN price | Network hash at the GPU entry equilibrium | In 5070 Ti-class cards | In 5090s | In N2 SRAM dies (5.5 GH/s) | In N2 wafers (about 60 good dies) | In GDDR7 boards (166 MH/s) |
|---|---|---|---|---|---|---|
| 0.03 | 2.6 TH/s | 34,000 | 19,500 | 480 | 8 | 15,800 |
| 0.10 | 8.8 | 114,000 | 65,000 | 1,600 | 27 | 52,800 |
| 0.30 | 26 | 341,000 | 195,000 | 4,800 | 80 | 158,000 |
| 1.00 | 88 | 1.1 M | 650,000 | 16,000 | 270 | 528,000 |
| 3.00 | 263 | 3.4 M | 1.9 M | 48,000 | 800 | 1.6 M |
The GPU side's accessible supply is the installed base of consumer cards (tens of millions of Ampere, Ada and
Blackwell cards in the world, approximate) and the used market, with a use outside mining and a resale price that the
population table carries; at every price in the window the hash the chain needs is under 4 percent of one
generation's shipments. The die's supply is one supplier's wafer allocation (8 to 800 wafers of a node booked to 2028,
claimed); the board's is a Bitmain-class production run (16,000 to 1.6 M units) with a commodity memory bill. The
dependence on individual suppliers is total for the die at every price (one order holds the chain), partial for the
board (a run of that size is visible and takes months), and nil for the GPU side.
## 9. The conditions under which the success statement holds (the result)
Success (a specialised supplier earns a normal return and GPUs stay close enough in total cost, obtainable and useful
outside mining, that entrants still compete) holds on today's rows when ALL of the following do:
| Condition | The number on today's rows | GDDR7 board | SRAM die |
| Who | Against the DRAM board (bought at twice the hardware term, the manufacturer's half) | Against the SRAM die (bought, the same) | What it means |
|---|---|---|---|
| (a) the chip's all-in cost per accepted unit at its own electricity is within about 1.5x of the best GPU owner's at the GPU's electricity | the owner at 0.12: 263 to 332 micro-USD (5070 Ti, 5080) | 307 at 3 years, 750 at 1 year: PASSES | 72 to 134: FAILS at every life |
| (b) the chip's annualised hardware per MH/s is not below about a quarter of the GPU entrant's | the 5080 entrant's hardware 460 micro-USD | 239 to 677: PASSES | 33 to 95: FAILS |
| (c) a fleet above a third of the chain's hash costs more than a year's miner revenue | at IGN 0.10 the chain is 8.8 TH/s; a third is 2.9 TH/s | USD 16 M of boards against USD 40 to 80 M of revenue: PASSES above IGN 0.05 | USD 2.3 M of dies: FAILS at every price under about 3 |
| (d) GPUs keep a resale market and a use outside mining | every card in the population resells at 25 to 55 percent after two years and rents at 3x to 5x its mining cost | PASSES (the GPU side's property) | PASSES (the same) |
| A Blackwell owner at 0.06 to 0.12 | never switches: the bought board at 3 years is 550 to 600 micro-USD against the owner's 156 to 332 | switches at 0.12 (105 to 134 against 263 to 332); near indifferent at 0.06 | the die replaces Blackwell owners at normal grid prices; the board never does |
| An Ada or Ampere owner at 0.12 | near indifferent at 3 years (550 to 600 against 524 to 768); switches at 0.25 | switches at every price | the board retires only the oldest cards at dear electricity, which the generation does anyway |
| A new entrant choosing between a new 5070 Ti and a bought chip at 0.12 | the board at 3 years (about 600) is 1.15x the card's 521: the card wins; at 1 year the card wins 2x | the bought die (134) is 0.26x the card: the die wins 4x | an entrant market with a bought die has no GPU entrants; one with a bought board keeps them |
| The GPU generation at year 3 (1.5x per joule at the same price, the old card resold at the table's fraction) | cuts the entrant's cost 25 percent and the owner's power 33 percent: the board at 3 years then reads 1.5x to 1.7x the new entrant, in band | the die's advantage falls 25 to 33 percent, from 7x to 5x on the entrant: still out of band | the GPU side's own curve narrows the board's gap by the second generation and never closes the die's |
| The chip's generation at year 3 (a node step, 1.5x per joule, re-bought at the same silicon price) | the board's power term falls a third (65 to 43 micro-USD of 307): 2 percent of its cost; its hardware term is unchanged | the die's 38 to 25: 4 percent of 72 | the chip side's generation moves the totals under 5 percent: the chip's cost is hardware, the GPU's is power, so the generation helps the GPU more |
| Resale | the GPU resells at 25 to 55 percent after two years (section 2); the chips at 0 | | the resale market is the GPU entrant's whole hedge and the chip has none, which is why the chip's life is the axis that moves everything (section 7) |
## 5. Changing proving demand: proving revenue as a second income axis per class (D4: new)
The resolution's shape (the research lane, 17:0x UK): the tip stays whole to the miner; provers are paid the 20
percent pool per block plus an explicit user-funded proving fee with congestion pricing; the burn separate; the hard
cap and no development tax kept. The internal pool at IGN 0.10 is USD 27,400 a day (0.2 x 0.77 B / 0.8 / 365), shared
by proving capacity (a 5,000-card fleet drawn from the classes that can prove, approximate); external demand USD
2,000 a day at launch (spec 05's grid), 20,000 under a spike, 90 percent to the provers who deliver. The 12 GB tier's
internal row is the fleet lane's measured zero (0 paid in 313 claims on Devnet 3, 8 October 2026).
| Class | Memory | Shard s (bench) | Internal proving, USD per card-day | Plus external at launch | Plus external at a 10x spike | Mining at the GPU equilibrium, USD per card-day | Power per day at 0.12 | Label |
|---|---|---|---|---|---|---|---|---|
| RTX 5090 | 32 | 6.3 | 7.42 | 7.90 | 12.29 | 1.69 | 0.86 | modelled on measured shard times |
| RTX 5080 | 16 | 8.0 | 5.84 | 6.22 | 9.68 | 0.89 | 0.63 | the same |
| RTX 5070 Ti | 16 | 7.0 | 6.67 | 7.11 | 11.06 | 0.96 | 0.58 | the same |
| RTX 5070 | 12 | 4.8 | **0 (measured zero)** | 0.64 | 6.40 | 0.51 | 0.40 | measured zero, modelled external |
| RTX 5060 Ti 16 GB | 16 | 11.6 | 4.03 | 4.29 | 6.68 | 0.24 | 0.26 | modelled |
| RTX 5060 | 8 | 12.0 | 0 (measured zero) | 0.26 | 2.56 | 0.21 | 0.23 | |
| RTX 4090 | 24 | 6.3 | 7.42 | 7.90 | 12.29 | 0.73 | 0.81 | |
| RTX 4080 | 16 | 7.5 | 6.23 | 6.64 | 10.33 | 0.56 | 0.63 | |
| RTX 4070 | 12 | 12.1 | 0 (measured zero) | 0.25 | 2.54 | 0.39 | 0.32 | |
| RTX 4060 Ti 16 GB | 16 | 11.6 | 4.03 | 4.29 | 6.68 | 0.22 | 0.22 | |
| RTX 3090 (used) | 24 | 14.9 | 3.14 | 3.34 | 5.20 | 0.47 | 0.66 | |
| RTX 3080 (used) | 10 | 7.1 | 0 (measured zero) | 0.43 | 4.33 | 0.51 | 0.60 | |
| RTX 3060 (used) | 12 | 14.4 | 0 (measured zero) | 0.21 | 2.13 | 0.30 | 0.30 | |
| RX 9070 XT | 16 | none | cannot prove (no CUDA) | | | 0.24 | 0.43 | |
| H100 (hosted) | 80 | 3.0 | 15.57 | 16.60 | 25.81 | 1.13 | 1.01 | |
| A100 (used) | 80 | 5.0 | 9.34 | 9.96 | 15.49 | 0.75 | 0.72 | |
| Apple M5 Max | 36 | none | cannot prove | | | 0.34 | 0.11 | |
| The SRAM die, the DRAM board, any hash engine | | none | **0: a hash engine cannot prove** | 0 | 0 | the whole chain's mining | | by construction |
Reading: at launch-shape demand a proving-capable card earns 4x to 8x more per day from internal proving than from
mining at the GPU equilibrium, and the pool is shared by few enough cards that it pays even at 0.25 per kWh; under a
spike the external fee adds 50 to 70 percent. The chip has none of it. At zero proving demand (the pool is a launch
subsidy and the external market empty) the second income is 0 and the commodity side falls back to mining alone,
which is the first cut's model; at the fleet lane's measured efficiency (5.5 percent of proving card-time paid on a
day with a fault) the internal rows are 0.4 to 0.9 USD a day, still above mining for the 24 GB and datacentre
classes. The condition this adds to section 12 is (g): a second income for the commodity side that the specialised
supplier cannot enter; it holds while proving demand exists and the 16 GB and larger tiers can prove.
## 6. Private mining and hardware sales; cheaper derivative chips (D4)
From the surface (the floor file 4.4, the mission lane's shape), `p*` is the break-even price in USD per IGN:
| Entrant | `C_dev` 20 M (a DRAM board), `L` 3 y, `q` 0.3 / 1.0 | 150 M (the SRAM die), 3 y, 0.3 / 1.0 | Reading |
|---|---|---|---|
| The operator self-mining a first design | 0.055 / 0.017 (USD 43 / 13 M a year of miner revenue) | 0.73 / 0.22 (561 / 168 M) | the first entrant self-mines |
| The manufacturer selling hardware (keeps half the profit) | 0.11 / 0.034 | 1.50 / 0.45 | about 2x the operator's bar |
| The revision entrant (a second design at 0.3 x `C_dev`, `T0` 1 y) | 0.019 / 0.006 | 0.12 / 0.04 | a derivative costs a third and ships a year sooner; the sunk case bounds it |
| The shared-cost entrant (three share one design) | 0.040 / 0.012 | 0.24 / 0.07 | |
| The hybrid (self-mine a year, then sell) | 0.07 | 1.10 | between the two |
| Development SUNK (the mandatory case) | 0: the entrant pays manufacturing, deployment and operation only; its rows are section 2's chip rows | 0 | the whole threat is this case |
With development paid, every chip is ABOVE the best GPU entrant (415 to 588 micro-USD) at IGN 0.10 and below; the die
falls below it only from IGN 0.30 on a 3-year life; the board with paid development never does inside the window.
## 7. Several productive lifetimes (D4)
| Life | GDDR7 board, sunk, at 0.06 (micro-USD) | Against the 5070 Ti owner / entrant | SRAM die, sunk | Against the 5070 Ti owner / entrant | Reading |
|---|---|---|---|---|---|
| 0.5 y (the fixed-lane chip under the 180-day rotation) | 1,414 | 0.1x / 0.3x | 226 | 0.7x / 1.8x | neither chip pays at half a year; the board is 3x worse than a GPU entrant |
| 1 y | 750 | 0.2x / 0.6x | 134 | 1.2x / 3.1x | the board loses to every Blackwell entrant; the die is in band against owners |
| 2 y | 418 | 0.4x / 1.0x | 87 | 1.8x / 4.8x | |
| 3 y (the programmable chip's default) | 307 | 0.5x / 1.4x | 72 | 2.2x / 5.8x | the board's coexistence band; the die out of it |
| 5 y | 219 | 0.7x / 1.9x | 60 | 2.6x / 6.9x | |
The rotation (layer 3) sets the fixed-lane chip's life at 0.5 years and does nothing to a programmable chip; its
value is the factor between the first and the fourth row, not a wall, and the pass line's "scheduled ASIC death" is
not what holds either chip here: the board holds at every life from 1 year on its hardware term, the die holds at none.
## 8. Growing and shrinking networks, reduced issuance (D4)
Five years with a per-class supply curve (section 9's rule), the emission halving in years 3 and 5, three price paths,
a sunk chip fleet entering at the start of year 2. The chip's joules improve 1.5x at year 4 (a node step).
| Path, fleet | Year 2 | Year 3 | Year 5 | GPU classes above water (of 17) | Reading |
|---|---|---|---|---|---|
| Growing x2 from 0.10; a USD 1 M die fleet (1.3 TH/s) | 6 percent of 20 TH/s; margin 92 percent | 5 percent | 3 percent of 39 TH/s | 14, 15, 16 | coexistence by dilution: the supplier earns 90 percent margins on a tiny share; not a normal return, but no class leaves |
| Growing; USD 10 M die (12.8 TH/s) | 50 percent | 43 percent | 30 percent | 12, 13, 15 | half the chain on landing; dilutes to 30 percent by year 4 as GPUs re-enter at the higher price; margins 88 to 93 percent |
| Growing; USD 100 M die (128 TH/s) | 100 percent; margin 49 percent | 100 percent | 99 percent | 0, 0, 4 | failure: one buyer holds the chain for five years; four classes return in year 4 at IGN 0.80 |
| Growing; USD 10 M board (1.8 TH/s) | 9 percent; margin 65 percent | 7 percent | 4.5 percent | 14, 15, 16 | coexistence at a 57 to 69 percent margin |
| Flat 0.10; USD 1 M die | 9 percent | 12 percent | 19 percent of 6.9 TH/s | 12, 11, 6 | the halvings raise the share; six classes left by year 5 |
| Flat; USD 10 M die | 70 percent | 89 percent | 100 percent | 6, 6, 0 | failure by year 5 |
| Flat; USD 100 M die | 100 percent; margin -1 percent | -102 percent | -233 percent | 0 | failure for both: the fleet is larger than the revenue |
| Flat; USD 10 M board | 14 percent; margin 57 percent | 16 percent; 34 | 24 percent; 27 | 12, 11, 6 | coexistence at a normal return (27 to 57 percent) |
| Shrinking x0.5 from 0.30; USD 1 M die | 7 percent | 17 percent | 74 percent of 1.7 TH/s | 13, 11, 2 | failure in year 5 at USD 4 M of revenue |
| Shrinking; USD 10 M die | 58 percent | 96 percent | 100 percent; margin -77 percent | 11, 3, 0 | failure from year 3 |
| Shrinking; USD 10 M board | 10 percent; margin 62 percent | 22 percent; 28 | 91 percent; -30 percent | 13, 8, 2 | the board too takes a shrinking chain at USD 4 M of revenue, and loses money doing it |
Reading: reduced issuance (the halvings) and a shrinking price move every row toward the chip's share, and a USD 1 M
die fleet is 74 percent of a USD 4 M chain. The board coexists at a normal return in the growing and flat paths and
takes the chain only when the chain is worth less than its fleet; the die's USD 10 M fleet takes the chain on every
flat or shrinking path by year 3 to 5. The pass line's "small network": the board's pass holds at 42 TH/s and IGN
1.00 (the growing path's year 4), so it does not need one; the die's failure is worst in a small network, and a large
one only delays it.
## 9. Miners react with no fixed shares (D4: new)
The reaction rule replacing the first cut's single rule: each class participates with a fraction of its base, the
third of the base already owned joining as revenue per MH/s-hour rises from its owner cost to its entrant cost
(linearly) and the other two thirds entering when revenue exceeds the entrant cost; the installed base is the cap;
re-entry on a price rise is automatic (the growing path's year 4: 12.9 to 37.7 TH/s as 16 of 17 classes come back);
the chip fleet is fixed after entry. The equilibrium revenue per MH/s-hour and the GPU hash are solved each year by
bisection. Against the single-rule first cut the shares move: the USD 10 M die fleet holds 50 percent on landing in
the growing path (the first cut read 37) and 70 percent on the flat path (73), and the board holds 9 to 24 percent
(10 to 31): the per-class curve lets the cheaper classes stay longer and the dearer ones leave sooner, and the totals
are within 5 points of the first cut. The operator simulation carries the same reaction at a one-day step with
proving as a third choice; its D1 shock (a 1 TH/s die fleet) moves 1,500 of 9,177 mining cards to proving.
## 10. The operator simulation (D4 item 2, beside this model)
`docs/analysis/class-v6/operator-simulation.md`: operators choose per day among mine, internal prove, external prove
and off by profit at the marginal rate, under four shocks. At launch-shape demand every shock is restored in 0
periods (idle GPU capacity dwarfs the proving work). In a capacity-limited world (1 percent of the cards, the measured
5.5 percent proving efficiency) a lasting 1,000x proving spike is NOT restored within 150 periods when the internal
pool is a fixed sum (provers go to the external fee market and the internal backlog grows without bound) and is
restored in 35 periods with the resolution's congestion-priced internal proving fee; the price fall, the departure of
the six largest proving cohorts, the mining entrant and the proving entrant are restored in 0 periods in both worlds.
No parameter was changed by hand in any run. The one design finding: the fixed pool is a subsidy, not a price, and
internal proving needs the congestion-priced fee the resolution gives it.
## 11. Accessible supply, and the dependence on suppliers and operators (D4: its own table)
| IGN price | Network hash at the GPU equilibrium (per-class curve) | Share of the installed base available to mining (44.7 TH/s, approximate) | GPU suppliers | In N2 SRAM dies / wafers from ONE supplier | In DRAM boards | The largest single GPU operator today (a 1 percent fleet) | Label |
|---|---|---|---|---|---|---|---|
| 0.03 | 5.6 TH/s | 12 percent | NVIDIA (16 of 17 classes), AMD, Apple; tens of millions of cards in the world | 1,000 dies / 17 wafers | 34,000 | 0.06 TH/s | modelled |
| 0.10 | 9.6 | 21 | the same | 1,700 / 29 | 58,000 | 0.10 | modelled |
| 0.30 | 20 | 44 | the same | 3,600 / 60 | 119,000 | 0.20 | modelled |
| 1.00 | 42 | 95 | the same; past this the installed base binds and used prices rise | 7,700 / 128 | 255,000 | 0.42 | modelled |
The GPU side has three suppliers, a used market, a use outside mining (the rental yields of section 2) and no operator
above a percent of the hash; the die's whole chain is one wafer allocation (17 to 128 wafers of a node booked to 2028,
claimed) and the board's a Bitmain-class run of 34,000 to 255,000 units with a commodity memory bill. Dependence on a
single supplier: total for the die at every price, partial for the board (a run that size is visible and takes
months), nil for the GPU side; dependence on a single operator: a chip fleet is one operator by construction (the
operator simulation's D1), a GPU fleet of the same hash is tens of thousands of owners.
## 12. The conditions under which the success statement holds (the result)
| Condition | The number on today's rows | DRAM board | SRAM die |
|---|---|---|---|
| (a) the chip's all-in cost per accepted unit at its own electricity within about 1.5x of the best GPU owner's at the GPU's electricity | the owner at 0.12: 263 to 332 micro-USD (5070 Ti, 5080) | 307 at 3 y, 750 at 1 y: PASSES | 72 to 134: FAILS at every life |
| (b) the chip's annualised hardware per MH/s not below about a quarter of the GPU entrant's | the 5080 entrant's hardware 460 micro-USD | 239 to 677: PASSES | 33 to 95: FAILS |
| (c) a fleet above a third of the chain's hash costs more than a year's miner revenue | at IGN 0.10 the chain is 9.6 TH/s, a third 3.2 | USD 18 M of boards against USD 40 to 80 M: PASSES above IGN 0.05 | USD 2.5 M of dies: FAILS at every price under about 3 |
| (d) GPUs keep a resale market and a use outside mining | resale 25 to 55 percent after two years; rental 3x to 5x the mining cost | PASSES (the GPU side's property) | PASSES (the same) |
| (e) the per-joule gap at the honest knee stays under about 3x | Blackwell at the knee 1.70 to 2.06 microjoules | 2.2x to 2.6x: PASSES | 3.7x to 4.5x: FAILS (the bare-lane floor 10x to 12x) |
| (f) the supplier's gross margin on the share it holds is a normal return (under about 50 percent) and does not rise with each halving | | 21 to 41 percent in the five-year run: PASSES | 82 to 86 percent, or a loss once it is the chain: FAILS |
| (f) the supplier's gross margin is a normal return (under about 70 percent) and does not rise with the halvings | section 8 | 27 to 69 percent, falling with the halvings: PASSES | 85 to 93 percent, rising, or a loss once it is the chain: FAILS |
| (g) a second income (proving) for the commodity side that the specialised supplier cannot enter | section 5: USD 3 to 7 a card-day at launch demand on the 16 GB and larger tiers; 0 for any hash engine | PASSES (the board cannot prove either, which is the GPU's advantage over it) | PASSES (the same) |
So: **the success statement holds for the stored-dataset DRAM-board chip at a 1 to 3 year life on today's rows, in
every price path, at every electricity price on the axis, with the GPU side's own generation curve narrowing the gap
further; it does not hold for the N2 SRAM die once that die exists with its development sunk, at any life, price path
or electricity price, and the only condition that holds the die is that nobody pays to build it** (the surface of the
floor file's section 4.4: a USD 150 M project at a third of the chain needs IGN 0.73 over three years, a maker who
takes the chain 0.22, a revision 0.12 to 0.16). That last is a statement about an investor's decision, not about the
chain staying below a level, and the review is right that it is the only honest form.
**The result.** The success statement holds for the stored-dataset DRAM-board chip at a 1 to 3 year life on today's
rows, in every price path, at every electricity price on the axis, with the GPU side's own generation curve narrowing
the gap further and proving as a second income the chip cannot enter; its pass needs no small network (it holds at 42
TH/s and IGN 1.00), no token appreciation (it holds on the flat and shrinking paths) and no scheduled ASIC death (the
life axis, not the rotation, is what the board lives on). The statement does not hold for the N2 SRAM die once that
die exists with its development sunk, at any life, price path or electricity price; a small network makes it worse,
appreciation only delays it, and the rotation does not touch a programmable die. **The model names where it fails: a
sunk SRAM die fleet of USD 10 M or more at any price in the window, and of USD 1 M in a shrinking chain.** The only
condition that holds the die is that nobody pays to build it (section 6: IGN 0.73 for a USD 150 M project at a third
of the chain over three years, 0.22 taking the chain, 0.12 to 0.16 for a revision), which is a statement about an
investor's decision and is carried as such, not as a level the chain stays below.
What the chain can do about the die, from the model: raise the honest side's efficiency (every cent of GPU electricity
and every point of the knee moves condition (a) and (e); the lock already moves a Blackwell card 34 to 41 percent), keep
the share detector as the instrument that makes condition (c) visible the week it fails, and keep the dataset floor as
the ticket (section 3.2 of the floor file: USD 1,500 to 3,000 per die at the schedule, which moves condition (c) by 2x
to 4x and nothing else). Nothing in the hash moves conditions (a), (b) or (e) for the die by the factor they need.
What the chain controls, from the model: the honest side's cost per accepted unit (every cent of GPU electricity and
every point of the knee moves (a) and (e); the lock already moves a Blackwell card 34 to 41 percent), the second
income (proving demand and the congestion-priced fee that keeps internal proving served, the operator simulation's
finding), the share detector that makes (c) visible the week it fails, and the dataset floor as the ticket (USD 1,500
to 3,000 per die at the schedule, which moves (c) by 2x to 4x and nothing else). Nothing in the hash moves (a), (b) or
(e) for the die by the factor they need.
## 10. Unverified and owed
## 13. The D4 checklist, line by line
| Item | Where | Status |
|---|---|---|
| Growing and shrinking networks | section 8 | in |
| Reduced issuance | section 8 (the halvings in years 3 and 5) | in |
| Cheap and dear electricity | sections 1 to 3 | in |
| GPU replacement and resale on both sides, the 1.5x generation on the chip side too | section 4 | in |
| Changing proving demand | section 5 (zero, launch, spike; the resolution's shape; the 12 GB measured zero) | in |
| Private mining and hardware sales | section 6 | in |
| Several productive lifetimes (0.5, 1, 2, 3, 5) | section 7 | in |
| Cheaper derivative chips | section 6 (the revision and shared-cost rows) | in |
| Miners react with no fixed shares (per-class supply curve, installed base cap, re-entry) | section 9 | in |
| The outputs: cost advantage, replacement economics, accessible supply, break-even electricity per class (17), supplier and operator dependence as its own table | sections 1, 4, 11, 3, 11 | in |
| The tariff advantage shown separately from the hardware advantage, the 6.25x illustration first | section 1 | in |
| The operator simulation | section 10 and its own file | in (first run) |
| The k lane's placed rows and the adversary lane's whole-machine rows | the chip rows stand until they land | owed by others |
| Per-card agents, the hybrid mode, the measured stage times in the simulation | the simulation's section 4 | second cut |
## 14. Unverified and owed
- Every chip-side figure is modelled; no chip has been measured. The chip's hardware per MH/s (USD 0.8 for the die
with the system, 5.6 for the board) is the term conditions (b) and (c) rest on and is approximate within 2x.
- The card prices are street approximations of October 2026; the 5090's street price has been 2x MSRP this year, which
moves the entrant rows for that card by up to 2x and no other row.
- The Ada and Ampere knees are modelled (no rented host allows the lock); the Blackwell floors are measured on the 5090,
5080 and 4070, modelled on the 5070 Ti, 5070 and 5060 class.
- The accepted-work factor (97 percent), the wear allowance (5 percent a year), the hosting (USD 0.02 per kWh) and the
rental yields are approximate.
- The five-year run's GPU reaction is a single-rule model (entry at the cheapest entrant's cost, exit in cost order);
a second cut adds a per-class supply curve, the installed base as a cap on entry, re-entry on a price rise, and the
halving's effect on the entrant cost through used-card prices.
- The derivative-design rows (a revision at 0.3 x `C_dev`) are carried from the floor file's surface and not re-run
here; the sunk case bounds them.
- The emission beyond year 5 and the proving pool's 20 percent are outside the run.
- Nothing was run on the Mac; the script ran on build-3.
- The card prices are street approximations of October 2026; the installed-base counts available to mining are
approximate (the cap of 44.7 TH/s); the 5090's street price has been 2x MSRP this year.
- The Ada and Ampere knees are modelled (no rented host allows the lock); the Blackwell floors are measured on the
5090, 5080 and 4070.
- The proving rows rest on the bench table's shard times and the fleet lane's one measured day (with a fault); the
proving fleet (5,000 cards) and the pool sharing by capacity are assumptions.
- The accepted-work factor, the wear allowance, the hosting and the rental yields are approximate.
- The emission beyond year 5 and the proving pool's fade are outside the run.
- Nothing was run on the Mac; the scripts ran on build-3 (first cut) and build-4 (this cut).

View file

@ -0,0 +1,114 @@
# The profit-maximising operator simulation: do the pricing and capacity rules restore service without an administrator?
8 October 2026, 16:2x to 16:5x UK, branch `class-v6-floor-sram`, floor lane 3, Igneum 2.0 D4 item 2 (the research lane's
word of 17:0x UK; the clock 20:15 UK). First run. **Every figure modelled**; the script is `scratchpad/opsim.py`, run on
build-4 (build-3 was down from 16:40 UK). The cost rows are the coexistence model's (lane 4's class v5 table, street
prices); the proving side carries the fleet lane's measured day on Devnet 3 (8 October 2026, 3,421 claims, 93 paid
segments): the 12 GB tier paid 0 of 313 claims (a measured zero for internal proving), steals 4.0 percent of claims,
paid to wasted card-seconds 14,800 to 254,000 (5.5 percent, on a day with a floor-link fault until 10:50 UTC). Nothing
here is served. The founder is not named.
## 0. The result in one page
| Shock | Launch-shape world (all available cards, assumed proving efficiency 0.5) | Stressed world (1 percent of the cards, the measured 5.5 percent proving efficiency, a 1,000x spike) without a congestion price on internal proving | The same with the resolution's congestion-priced internal proving fee | Reading |
|---|---|---|---|---|
| A. A proving demand spike (external x10, or x1,000 in the stressed world, lasting) | restored in 0 periods: capacity moves to the external market the same day (263,000 cards in, settling to 44,000), the fee never leaves 1x | **NOT restored in 150 periods**: 9,900 of 12,300 cards go to the external fee market, internal proving falls from 1,647 to 393 cards, the internal backlog grows without bound (8.9 M shards unproven by day 150) because the pool pays a fixed sum that cannot bid provers back | **restored after 35 periods**: the internal fee climbs to 2x, pulls 565 cards back, the backlog peaks at 368,000 shards (1.4 days of demand) and clears | the fee market restores EXTERNAL service by itself; INTERNAL proving needs the resolution's congestion-priced fee, or a spike starves it |
| B. The token price falls to 0.3x | restored in 0 periods: 130,000 cards leave the same day, hash 17.7 to 8.4 TH/s, revenue per MH/s-hour back to 309 to 315 micro-USD within 7 periods | restored in 0 periods | restored in 0 periods | exit at cost is immediate and proportionate; proving capacity stays 4x to 1,600x the demand |
| C. The six largest proving cohorts leave for good (H100, A100, 5090, 5080, 4090, 3090) | restored in 0 periods: hash 17.7 to 12.0 then 15.2 TH/s as the remaining classes re-enter at the higher rate; internal capacity stays 1,500x the demand | restored in 0 periods: capacity 2.8x to 3.1x the demand on the remaining classes | restored in 0 periods | re-entry at cost fills the gap; the 16 GB tier carries internal proving when the 24 GB and datacentre tiers leave |
| D1. A specialised entrant in mining (a sunk 1 TH/s SRAM die fleet at 0.06) | restored in 0 periods: 4,000 GPU cards leave, the chip holds 5.6 percent, proving untouched | restored in 0 periods: the chip holds 80 percent of a 1.25 TH/s network, GPU mining falls from 9,177 to 7,671 cards and 1,500 of them move to proving (internal 2,196 to 3,697) | restored in 0 periods | service holds in both worlds; coexistence does not (the coexistence model's finding), but the GPUs that leave mining go to proving, which the chip cannot do |
| D2. A specialised entrant in proving (a proving ASIC at 2,000 shard-equivalents a day at a tenth of the cost) | restored in 0 periods: the entrant takes the external market at the base fee; nothing else moves | restored in 0 periods | restored in 0 periods | external proving is a commodity market; an entrant lowers the fee and the GPUs leave that market for mining and internal proving |
**The pass statement, per shock.** The pricing and capacity rules (congestion-priced user-funded external proving
fees, the fee market, entry and exit at cost) restore service without an administrator in every shock of the
launch-shape world, in 0 periods, because idle GPU capacity dwarfs the proving work (16 M shard-equivalents a day of
external capacity against 200 of work; 426 M internal against 259,200). In a capacity-limited world the same rules
restore B, C, D1 and D2 in 0 periods and FAIL to restore A unless internal proving also carries a congestion-priced,
user-funded fee, with which A is restored in 35 periods. The one design finding: the 20 percent pool paid as a fixed
sum per block is a subsidy, not a price; when an external fee market outbids it, internal proofs starve, and the
resolution's shape (an explicit user-funded proving fee with congestion pricing for internal proving too) is what
closes it. No parameter was changed by hand in any run.
## 1. The model
| Element | Rule | Label |
|---|---|---|
| Cohorts | 17 card classes x 3 electricity prices (0.06, 0.12, 0.25 per kWh), a third of each class's available count per price; each cohort holds continuous shares of its cards in MINE, INTERNAL PROVE, EXTERNAL PROVE and OFF | modelled |
| Decision | each period (one day) a cohort moves a quarter of its cards toward the mode with the best profit per card-day, evaluated at the MARGINAL rate (the pool or fee divided by the capacity after its own move), with a 10 percent hysteresis; v1's all-or-nothing cohorts herded and oscillated and were replaced | modelled |
| Mining revenue | the miner emission (0.77 B IGN in year 1, 80 percent) at IGN 0.10 (assumption), shared by hash; the tip stays whole to the miner (the resolution's shape) | the spec's constant; the price an assumption |
| Internal proving | 3 shards per block (259,200 shard-equivalents a day); the 20 percent pool (0.53 M IGN a day) shared by proving capacity; a backlog accrues when capacity is short; the 12 GB tier earns nothing (measured zero); under the resolution's shape a user-funded congestion fee on top of the pool, rising 25 percent a period while the backlog exceeds a day of demand and falling 10 percent while under half a day, bounded 1x to 100x | measured zero (the fleet lane), modelled rule |
| External proving | USD 2,000 a day of demand at the base fee (spec 05's launch grid; USD 10 per shard-equivalent), 90 percent to the provers who deliver; the congestion fee rises 25 percent a period while job latency exceeds a day and falls 10 percent while under half a day, bounded 1x to 100x; demand elastic to the fee with exponent 0.5 | spec 05; the elasticity and the fee rule modelled |
| Proving capacity per card | the bench table's shard times (5090 6.3 s, 4090 6.3, 5070 4.8, 3080 7.1, 4070 12.1, 3060 14.4, 3090 14.9, H100 3.0, A100 5.0; the 9070 XT and the Mac cannot prove), times the proving efficiency (0.5 assumed after the fault fix; 0.055 the measured day), times 1 minus the 4 percent steal rate | measured shard times; the efficiency as stated |
| Costs | power at the card's floor joules (mining) or its proving watts, a wear allowance of 5 percent of the used price a year, 97 percent accepted work | the coexistence model's rows |
| Shocks | applied at period 0 after a 90-period warm-up; 150 periods observed | |
| Restored | the first period from which ALL of the following hold to the end of the run: external job latency under a day, internal backlog under a day of demand, internal capacity at least the demand, block production at least a fifth of the baseline hash; "NOT restored" otherwise | the test; v2's latching version was replaced |
| Worlds | launch-shape (all available cards) and stressed (1 percent of the cards, a 1,000x spike, the measured efficiency) | |
## 2. The runs
### 2.1 Launch-shape world (all available cards; proving efficiency 0.5; shock A at x10)
Baseline after warm-up: hash 17.7 TH/s on 408,829 mining cards; 109,221 cards on internal proving (426 M
shard-equivalents a day against 259,200 of demand); 3,011 on external proving (16 M against 200 of work); 712,937 off
(the cards whose power at their price exceeds the revenue); revenue 496 micro-USD per MH/s-hour; fees at 1x.
| Shock | t+0 | t+7 | t+30 | t+149 | Restored |
|---|---|---|---|---|---|
| A. external x10 | 263,000 cards move to external proving the same day (external capacity 16 M to 1,197 M) | 44,000 settle there; mining back to 17.8 TH/s | unchanged | unchanged | 0 periods |
| B. price x0.3 | 130,696 cards leave; revenue per MH/s-hour 149 micro-USD | hash 8.4 TH/s; revenue 313 | 8.4; 315 | 8.5; 309 | 0 periods |
| C. six cohorts leave | hash 12.0 TH/s; revenue 735 | 15.5 TH/s as 55,870 cards of the other classes enter; 568 | 15.2; 576 | 15.2; 576 | 0 periods |
| D1. a 1 TH/s SRAM fleet | hash 18.7 TH/s; 4,000 GPU cards leave | 17.9 (GPU 16.9) | 17.8 | 17.8 | 0 periods |
| D2. a proving ASIC | external capacity +2,000 shard-equivalents; nothing else moves | | | | 0 periods |
Reading: at launch-shape demand the proving service has 100x to 1,600x spare capacity in the GPUs idle at the price, so
no shock in the four can break it; the fee never leaves 1x. Mining re-prices itself within 7 periods of a 70 percent
price fall or a 32 percent capacity loss. Both proving efficiencies (0.5 and 0.055) give the same result here.
### 2.2 Stressed world (1 percent of the cards; the measured 5.5 percent proving efficiency; shock A at x1,000)
Baseline: hash 0.34 TH/s on 9,177 cards; internal capacity 1.1 M shard-equivalents a day (4.2x the demand) on 2,196
cards; external capacity 47,000 (240x the work) on 87 cards; 878 off; revenue 25,500 micro-USD per MH/s-hour.
| Shock | Without a congestion price on internal proving | With the resolution's congestion-priced internal fee |
|---|---|---|
| A. external x1,000 (USD 2 M a day) | t+0: external latency 3.2 days, the external fee 1.25x, 2,963 cards move; t+7: external capacity 4.8 M (latency 0), the fee back to 1x, but internal proving has fallen to 393 cards and 197,000 shard-equivalents a day (0.76x the demand), backlog 123,000; t+30: backlog 1.55 M; t+149: backlog 8.9 M. **NOT restored in 150 periods** | t+7: internal 378 cards, backlog 153,000, the internal fee 1x; t+30: the internal fee 2x, 505 cards back, backlog 286,000; by t+35 the backlog is under a day of demand and stays so; t+149: 565 cards on internal, backlog 0, the fee back to 1x. **Restored after 35 periods**; worst backlog 368,000 (1.4 days) |
| B. price x0.3 | restored in 0 periods; the mining fleet re-prices (revenue 25,500 to 7,650 micro-USD) and 913 cards move to external proving | restored in 0 periods |
| C. six cohorts leave | restored in 0 periods: internal capacity 802,000 (3.1x the demand) on the 16 GB tier | restored in 0 periods |
| D1. a 1 TH/s SRAM fleet | restored in 0 periods: the chip holds 80 percent of 1.25 TH/s; 1,500 GPU cards move from mining to proving (internal 2,196 to 3,697) | restored in 0 periods |
| D2. a proving ASIC | restored in 0 periods | restored in 0 periods |
The same world at the assumed 0.5 efficiency restores A in 0 periods in both variants (internal capacity 10 M against
259,200: the spike cannot pull enough capacity away); at 0.1 percent of the cards and 0.5 efficiency the internal
backlog again grows without bound under A without the internal fee (11.5 M by day 150). The failure needs two things
at once: a proving fleet near the demand (a small network or the measured efficiency) and a fee market that outbids
the fixed pool.
## 3. What the runs say about the rules
1. **The external fee market works as designed.** In every run the congestion fee brings capacity to the external
market within a day (launch-shape) or seven (stressed) and returns to 1x when the backlog clears; demand elasticity
keeps the fee bounded; a cheaper entrant (D2) takes the work at the base fee and the GPUs leave that market for the
other two, which is coexistence in the proving market.
2. **The fixed internal pool is the one rule that fails under stress.** It pays per block whatever the backlog, so it
cannot bid provers back from a hotter market; with the resolution's congestion-priced internal fee it can, in 35
periods at a 2x peak fee. The pass line for shock A reads "restored only with the internal congestion fee".
3. **Entry and exit at cost handle the price fall, the capacity loss and the mining entrant in 0 periods** in both
worlds; the GPUs that a mining entrant displaces move to proving, which the SRAM die cannot do, and that is the
second income the coexistence model's T3 table prices.
4. **The 12 GB tier's measured zero matters for C**: when the 24 GB and datacentre cohorts leave, internal proving
falls on the 16 GB tier (5080, 5070 Ti, 5060 Ti, 4080, 4060 Ti); the 12 GB cards (5070, 4070, 3060: 525,000 of the
1.1 M cards in the population) contribute nothing to it today.
## 4. Unverified and owed
- Every row is modelled; the price (IGN 0.10), the external demand (USD 2,000 a day), the elasticity (0.5), the fee
rule (25 percent up, 10 percent down, bounded 100x), the hysteresis and the quarter-per-period adjustment are
assumptions; the proving efficiency is the fleet lane's one measured day (with a fault) and an assumed post-fix
value.
- The available card counts are approximate; the stressed world is a scale factor, not a measured network.
- Block production is read as hash; propagation and the 30-second lock are outside the run. The internal pool is
shared by capacity (a sortition-like share), not by shards delivered, which flatters high-capacity classes.
- A second cut: per-card (not per-cohort) agents as in `sim/economy/sim.py`, the hybrid mode (mine and prove assigned
shards), the steal rate as a function of latency, the external bond and timeout (O-5.6), and a run on the measured
stage times (inputs 2.4 s, proving 26.8 s median and 365 s at the slowest 1 percent, aggregation 22.8 s, claim to
paid 336 s) instead of the bench table's shard times.
- Nothing was run on the Mac; the script ran on build-4.

View file

@ -443,7 +443,7 @@ The conditions, read off the surface, which are the economic-resistance statemen
**The sentence, as the external review words it (10.0f item 5), served verbatim, with one word made honest (the site audit lane's read, 16:5x UK: class v4 and v5 have eight registers per lane and the window is class v6's new core shape, so "retains" is read as "retains across every rotation"):** "Class v6 adopts the 64-register window and retains it across every rotation. Current modelling estimates a 2.2x to 2.4x energy-efficiency advantage for the strongest specialised designs assessed against the GPU tier (2.0x on the GPU's own node). The long-program and select-tree proposals were rejected. Economic resistance depends on development cost, deployment economics and productive hardware lifetime; family transitions receive an obsolescence benefit only where a loss of competitiveness is demonstrated; programmable multi-epoch designs are included in the assessment."
**Where the figures come from (the coordinator, 16:1x UK): the served sentence's numbers are read from the k lane's PLACED GATED rows (10.0i), not from 725d2945's close and not from the 16:0x synthesis; the placement slipped to about 18:30 UK, so the sentence served at 18:30 carries 10.0i's tightened bracket, "estimates a 2.5x to 3.0x energy-efficiency advantage for the strongest specialised designs assessed against the GPU tier (2.1x to 2.6x on the GPU's own node)", and the placed row narrows it to one figure each on the next landing.** The labels on its numbers as first written: "2.2x to 2.4x" is modelled (the GPU side measured: the RTX 5080 at its 1,100 MHz lock 2.06 microjoules per hash and the RTX 5090 at its 1,300 MHz lock 2.33, both on class v4, PC 1 and rented pods, 8 October 2026; the chip side the k lane's synthesised 8-lane sequencer core with the 64-register window on ASAP7, scaled to N3 on TSMC's headline factors, claimed; the chip's memory the chip model's GDDR7 board, modelled; the card's cost of the window measured at stock on a rented 5090 and 4090 at 16:4x UK, within 5 percent per load with the liveness chain, no spill). "2.0x on the GPU's own node" is modelled (the same core node-for-node, k 1.09). Against the 32-lane window core the adversary would build the same figures read about 2.4x to 2.6x a node ahead and 2.0x to 2.2x node-for-node (synthesised, pending the re-optimised row by 18:00 UK); the served sentence's range is kept as the review wrote it and the 32-lane rows sit beside it on the page as the pending row. The window's k is synthesis-derived and not a lower bound.
**Where the figures come from (the coordinator, 18:0x UK): the served sentence's numbers are the PLACED whole-machine energies of 10.0r (the adversary lane's placed core, 17:5x UK), so the energy sentence served reads: "Current modelling estimates a 1.6x to 3.3x energy-efficiency advantage for the specialised designs assessed as complete machines against the GPU tier, from a board on commodity DRAM at 1.6x to an SRAM-store die at 3.3x (1.6x to 2.4x on the GPU's own node)." The k lane's core-only figure (2.5x to 3.0x a node ahead, 2.1x to 2.6x node-for-node, 10.0i) sits beside it marked as the core row, not the machine.** The labels on its numbers as first written: "2.2x to 2.4x" is modelled (the GPU side measured: the RTX 5080 at its 1,100 MHz lock 2.06 microjoules per hash and the RTX 5090 at its 1,300 MHz lock 2.33, both on class v4, PC 1 and rented pods, 8 October 2026; the chip side the k lane's synthesised 8-lane sequencer core with the 64-register window on ASAP7, scaled to N3 on TSMC's headline factors, claimed; the chip's memory the chip model's GDDR7 board, modelled; the card's cost of the window measured at stock on a rented 5090 and 4090 at 16:4x UK, within 5 percent per load with the liveness chain, no spill). "2.0x on the GPU's own node" is modelled (the same core node-for-node, k 1.09). Against the 32-lane window core the adversary would build the same figures read about 2.4x to 2.6x a node ahead and 2.0x to 2.2x node-for-node (synthesised, pending the re-optimised row by 18:00 UK); the served sentence's range is kept as the review wrote it and the 32-lane rows sit beside it on the page as the pending row. The window's k is synthesis-derived and not a lower bound.
The lines the page carries beside it, each labelled:
- The three statements, separate (10.0g item 1): energy resistance (the figures above); economic resistance (the profitability surface of 10.0f item 2, lane 3's first cut, modelled: p* scales as the project cost over the share times the discounted life, and under 5 percent with the per-joule edge; the cheapest attractive project is a USD 20 M DRAM-board design taking the whole chain for three years at about IGN 0.02 to 0.03, at a third 0.055 to 0.10; the SRAM die at N2 0.22 to 0.73; a fixed-lane chip under rotation needs 4x the price of a programmable one; stated as the conditions under which development is attractive); response capability (a passed rotation boundary proves the rotation works, not that hardware dies; the schedule of 10.0d: hourly, weekly, 180-day family, emergency vote; measured per boundary).
@ -561,6 +561,28 @@ The candidate: the class v6 shape unchanged, four FP32 families (fadd, fmul, ffm
What it moves: layer 2's per-tier table (3.3) gains two measured rows at the genesis floor, the 8 GB and 12 GB tiers both holding with the dataset resident (6.1 GB) and the RX 7600 row still owed for the AMD 8 GB case; the window's cost to the card is now measured free per load on Ampere and Ada low tiers as well as on the 5090 and 4090 (10.0e, 10.0n); and the proving statement (10.0f item 4: proving is an opportunity for GPU owners) carries its memory condition: at the 5.5 GiB floor an 8 GB or 12 GB card cannot hold the miner and the SP1 prover at once (13.6 GB together) and time-shares them, while a 16 GB card and up co-resides. Not measured: the core-only beside row (the prove host has no core mode); watts on the 4060. A fault for the floor lane: the served sm_89 tarball (igneum-floor-sm89.tgz) ships the stock SDK server in home/.sp1/bin (the 24 GB gate) while bin/ holds the patched one; the hand copied bin/ over home on the pod, so the 4060 proof rows are on the patched server.
#### 10.0q D4 landed: the coexistence model's second cut, the operator simulation, and the hybrid board scored (lane 3 at 4e9d51cc, 16:58 UK, three hours inside its clocks; both files in this landing; every row modelled; the scripts on build-4)
**The pass line, met and stated:** `docs/analysis/class-v6/coexistence-model.md` (the second cut, the D4 checklist as its section list, section 13 marking every line in or owed) names seven credible conditions for sustained commodity participation (its section 12, each with its number) and names where it fails: a sunk SRAM-die fleet of USD 10 M or more at any price in the window, USD 1 M in a shrinking chain. The DRAM-board chip passes all seven at a one to three year life without a small network (it holds at 42 TH/s and IGN 1.00), without token appreciation (it holds on flat and shrinking paths) and without scheduled ASIC death (its life axis, not the rotation, is what it lives on). The SRAM die fails (a), (b), (c), (e) and (f) at every life, path and tariff once sunk; the only thing that holds it is that nobody pays to build it, carried as an investor's decision. New in the cut: the tariff advantage beside the hardware advantage with the review's 6.25x row first (on the measured rows the operating advantage is joules times tariff, 2.2x to 18.9x for the board and 3.7x to 32x for the die, the total a third to a half of it); break-even electricity for all 17 classes, owner and entrant (a GPU owner matches the three-year board up to 9 to 15 cents on Blackwell, 4 to 6 on Ada and Ampere, and the die at 0 to 5 cents; no entrant matches the three-year board or the die at any positive price); proving as a second income per class at zero, launch and spike demand on the proving-payment resolution's shape (a 16 GB or larger card earns USD 3 to 7 a day from internal proving at IGN 0.10 against 0.2 to 1.7 from mining; the 12 GB tier 0, the fleet lane's measured zero; any hash engine 0: condition (g)); the chip-side 1.5x generation at year 3 (moves the chip totals under 5 percent, its cost being hardware and the GPU's power); a per-class supply curve with the installed base as a cap (44.7 TH/s, approximate) and automatic re-entry (the growing path's year 4: 12.9 to 37.7 TH/s, 16 of 17 classes back); the supplier and operator dependence table (the chain's hash at IGN 0.03 to 1.00 is 12 to 95 percent of the installed base across three GPU vendors, or 17 to 128 N2 wafers from one supplier).
**The operator simulation** (`docs/analysis/class-v6/operator-simulation.md`, the first run: mine, internal prove, external prove, off, per day at the marginal rate; four shocks, five runs; no parameter changed by hand): at launch-shape demand every shock restores in 0 periods because idle GPU capacity dwarfs the proving work. In a capacity-limited world (1 percent of the cards, the measured 5.5 percent proving efficiency) a lasting 1,000x proving spike is NOT restored in 150 periods when the internal pool is a fixed sum (provers go to the external fee market, the internal backlog grows without bound) and IS restored in 35 periods with the resolution's congestion-priced internal proving fee (peak 2x); the price fall, the six largest proving cohorts leaving, the mining entrant (1,500 of 9,177 cards move to proving) and the proving entrant restore in 0 periods in both worlds. **The one design finding: the fixed internal pool is a subsidy, not a price; internal proving needs the congestion-priced, user-funded fee too**, which is the proving payment of `docs/design/proving-payment.md` (90 percent of `pgas x f_p` to the block's proving pool), now carried there as the simulation's reason.
**The third chip, the hybrid board (the adversary lane's D2(b) first row, 17:3x UK; modelled on its core's synthesised rows node-for-node, chip-model-v3's memory figures and adv-cache-2's measured window-layer hit rates; no chip measured):** the hottest half of the items in 1 GiB of SRAM beside the DRAM serves 72 percent of the reads, so the activate-bound board runs 3.6x the hashes on the same 16 devices: 2.69x per joule against the 5090 at its lock (2.20 to 3.03), USD 1.88 per MH/s (8.5x per dollar against the card's 16), for USD 250 of N2 SRAM a board (claimed); the uniform-store lower bound 2.30x and USD 3.34; the hottest three quarters 3.10x and USD 0.83; a node ahead 3.33x and 3.96x; the dataset floor raises the SRAM ticket (USD 690 at 5.5 GiB, USD 2.9 per MH/s) and not the ratio, the hit rate per fraction being scale-free. The record's partial-store curve (chip-model-v3 5.4) priced the un-stored items as recomputed, which loses (0.78x); stored in SRAM they win, so **the DRAM-board chip's cheapest form is the stored-half hybrid at USD 1.9 to 3.3 per MH/s**. Scored on the six conditions, a first reading by this lane on lane 3's rows (approximate; lane 3's own scoring owed by 21:00): (a) all-in within 1.5x of the best GPU owner: at 2.69x per joule the operating advantage alone is 2.7x at the GPU's tariff, so FAILS; (b) hardware per MH/s not under a quarter of the GPU entrant's: USD 1.88 against about 16, FAILS (8.5x); (c) a third of the chain costing more than a year's miner revenue: at IGN 0.10 a third of 8.8 TH/s is 2.9 TH/s, USD 5.5 M of hybrid boards against USD 77 M of revenue, FAILS; (d) GPUs keep resale and an outside use: holds (the GPU side is unchanged); (e) the per-joule gap at the knee under about 3x: 2.69x, holds at the central figure and fails at the top of its band; (f) a normal margin: at (b) and (c) the supplier's margin is the die's kind, FAILS. **So the success statement holds for the pure DRAM board, fails for the SRAM die, and on the first scoring fails for the hybrid board on (b) and (c), the dollar conditions, which is where the coordinator said to watch; what holds the hybrid is again the investment decision (the hybrid's development is the board's plus the SRAM's, USD 20 to 75 M plus the die's share), and the served form says so.** Lane 3's scoring with the sunk-development case replaces this reading by 21:00.
#### 10.0r The placed energies, and the three chips scored at them (the adversary lane's placed row, 17:5x UK: the 8-lane genesis core placed and routed on ASAP7 with its SRAM macros, SPEF, a gate-level VCD; the full core's routed run 38 of 58 tags in; the SRAM term modelled; node factors claimed; the coordinator's order 18:0x UK: these are the energies the served form uses)
Placement and the clock tree add 32 percent to the class v4 draw (7.78 pJ per lane-op routed against 5.91 synthesised at ASAP7; 5.44 against 4.13 at N5), inside the k lane's +20 to +40 expectation; node-for-node k 0.53 at the 5090's lock for the genesis core (0.38 a node ahead), the full 18-family core scaled 0.59 and 0.42 until its routed row lands; the 32-lane genesis core (synthesis) 3.70 pJ at N5, k 0.36, 10 percent under the 8-lane core. **Placement takes a tenth off every per-joule ratio and nothing off the per-dollar ones.** The served convention at the placed energy, the 5090 at its 1,300 MHz lock over the complete machine (controller, host share, PSU, VRM, cooling in):
| Chip, as a complete machine | Per joule, node-for-node | Per joule, a node ahead | USD per MH/s (per dollar against the card's about 16) | Label |
|---|---|---|---|---|
| The GDDR7 board (the chip anyone can build) | **1.6x** (1.4x to 1.8x); 1.4x the 5080; 2.5x the cohort card | 1.9x (1.5x to 2.1x); 1.7x the 5080; 2.9x the cohort | 4.84 (3.3x) | placed core, modelled memory and machine, measured card |
| The stored-half hybrid board (1 GiB of SRAM beside the DRAM; 10.0q) | about 2.4x | about 2.9x | 1.88 (8.5x); the 5.5 GiB floor raises its SRAM ticket to USD 690 a board | modelled on the placed core |
| The N2 SRAM die | 2.4x | 3.3x | 0.8 (about 20x) | modelled on the placed core |
Scored on lane 3's seven conditions at these energies (a first reading on lane 3's rows, approximate; its own scoring with the sunk-development case replaces it by 21:00): the board's verdicts stand (its per-joule ratio falls a tenth, its dollars do not move; it passes all seven at a one to three year life); the hybrid's stand (it fails (b) and (c), the dollar conditions, which placement does not touch; on (e) it sits on the 3x line a node ahead and under it node-for-node); the die's stand on (a), (b), (c) and (f) (its dollars are the die's) and on (e) it now holds node-for-node (2.4x) and fails a node ahead (3.3x). **So the success statement holds for the pure DRAM board, fails for the SRAM die once sunk, and fails for the hybrid on the dollar conditions; the placed energies change no verdict and tighten every per-joule figure by a tenth.** The served energy sentence (10.0h) reads from this table.
The proving-payment pin is in the code (the node-side hand, 17:03 UK): branch proving-payment of the fork at eaddbf83 on release-2.0.0-node's c04674fe, the suites green on build-2 (igneum-exec 66 passed, kaspa-consensus-core 174 passed): behind `Params::proving_payment_activation_daa` (u64::MAX on every object; the height is main's word) a transaction's proving charge (pgas used x f_p) is 90 percent credited to `PROVING_POOL_ADDRESS` and the block's `proving_pool_credit` (so 5.3's per-shard payout carries it) and 10 percent burned, the rounding wei to the burn; below the height the whole charge burns as 5.1 stood; the receipt's new field `proving_payment` shows the provers' part beside `burned_proving`. One fact before any height is named: the shard guest (`proving/igneum-prove/core/src/executor.rs` lines 290 and 318) still burns the whole charge, so the floor stays at never until the guest carries the same split behind the same switch and the object pins the new shard program id; set before that, no shard proof verifies past the floor. The economics row reads: "designed, in the code behind the constant; the guest's mirror owed before any height".
#### 10.0d The rotation schedule the close adopts (the rotation lane, `docs/design/class-rotation-four-layers.md` on class-v6-rotation at bd43f808, build-3, gate green, 14:4x UK; one line per layer; both of this document's constraints held: the 180-day family epoch not shorter, W = 4 not drawn)
| Layer | Boundaries a year | What it draws, from where | Exposure per boundary (this document's units) | Chip | Label |

View file

@ -15,13 +15,19 @@ The economics page's fee table says the priority fee (the tip) is 80 percent to
Why the 10 percent burn on the proving payment: the burn of the proving base fee was the rule that made wash pgas a guaranteed loss (5.1). With 90 percent of it routed to the block's provers, a miner who is also a prover of its own block could recover part of a stuffed block's proving payment; sortition (eight eligible provers per shard, 5.3) makes that recovery a share of the pool's weight at best, and the 10 percent burn plus the execution base fee burned in full keep stuffing a loss at every weight. The 90 and 10 mirror the external job split of 5.4, so a prover's two incomes carry one rule.
## The simulation's reason (lane 3's operator simulation, 16:58 UK)
In a capacity-limited world (1 percent of the cards proving at the measured 5.5 percent proving efficiency) a lasting 1,000x proving demand spike is not restored in 150 periods when the internal pool is a fixed sum (the provers go to the external fee market and the internal backlog grows without bound) and is restored in 35 periods with this congestion-priced internal proving fee (peak 2x). The fixed pool is a subsidy, not a price; internal proving needs the user-funded, congestion-priced fee, which this decision gives it (`docs/analysis/class-v6/operator-simulation.md`).
## What a prover earns, in one line
Per block: its shards' part of 20 percent of the block subsidy (2.5, 5.3) plus its shards' part of 90 percent of the block's proving payment (`pgas used x f_p`); per job: 90 percent of the external job fee (5.4). Nothing from the tip.
## The code pin (not made here)
## The code pin (made by the node-side hand, 17:03 UK, behind its constant)
`igneum/exec/src/executor.rs` (the proving base fee debit at 357 and 360 on Devnet 3): credit 90 percent of `pgas used x f_p` to `PROVING_POOL_ADDRESS` and debit the remaining 10 percent to no one, instead of debiting the whole to no one; the pool's per-shard payout (5.3) then carries it with no further change. Behind an activation constant on the devnet objects like `proving_v1_activation_daa`. Until it lands the economics page says "designed, not in the code" on that row, as it does for the external job split.
In the code on branch proving-payment of the fork at eaddbf83 (on release-2.0.0-node's c04674fe; the suites green on build-2): behind `Params::proving_payment_activation_daa`, u64::MAX on every object until main names a height, the split below applies; the receipt's field `proving_payment` shows the provers' part beside `burned_proving`. Before any height: the shard guest (`proving/igneum-prove/core/src/executor.rs` 290 and 318) must carry the same split behind the same switch and the object must pin the new shard program id, else no shard proof verifies past the floor. The pin as written:
`igneum/exec/src/executor.rs` (the proving base fee debit at 357 and 360 on Devnet 3): credit 90 percent of `pgas used x f_p` to `PROVING_POOL_ADDRESS` and debit the remaining 10 percent to no one, instead of debiting the whole to no one; the pool's per-shard payout (5.3) then carries it with no further change. Behind an activation constant on the devnet objects like `proving_v1_activation_daa`. The economics page's row reads "designed, in the code behind the constant; the guest's mirror owed before any height".
## Where the text changes

View file

@ -1810,7 +1810,7 @@ Round 2 (5 October 2026, night): the public text now carries the v0 fact, labell
### P22. The rewards and payouts are inputs to the shard proof, not outputs
"The shard guest takes the segment's rewards and the prover payouts as data and commits the post-root after them. A host can feed any list and the proof still verifies."
Status: Open, staged (8 October 2026, the enforced-proving lane; `docs/spec/proving-enforcement.md` section 7 carries the staging table): the closure is four explicit stages, each a claim about one input with the native check that holds it until the next stage, never a self-contained proof of the whole state. Stage 0 (today): rewards and payouts are data in the shard statement and every node's native derivation vetoes a statement that differs; enforced proving adds that the record's proof must verify for that statement (ledger P21). Stage 1: the rewards list checked against a commitment the aggregator carries in its public values, the commitment itself recomputed natively from the mergeset. Stage 2: the payouts derived inside the aggregator guest from the carried records it verifies, `carried_payouts` the native check of the same derivation. Stage 3: the rewards derived inside the aggregator guest from the headers and blue sets it verifies, the consensus proof proper; only here do rewards stop being an input anywhere. Stages 1 to 3 are the phase 2 consensus-proof work (Nov 2026 to Jan 2027 per the litepaper roadmap); no served text says "the rewards and payouts are proven" before stage 3. The 2.0 plan's negative test (6) (incorrect rewards or consensus inputs: derivation authenticated) is PENDING in that sense: the executor test `enforced_a_statement_over_altered_rewards_or_payouts_is_vetoed_native_derivation_is_the_check` holds the native veto (every node's own derivation), and the proof-side closure is stage 3. Boundary sentence for every served text: a proof of execution is not a proof of authenticated consensus inputs, canonical history or data availability. Was: Open, blocked on the phase 2 consensus proof (design 7: the aggregator derives the rewards and payouts from consensus data it verifies, so they become outputs of the proof): next step that consensus-proof work, in phase 2 (Nov 2026 to Jan 2027 per the litepaper roadmap), no earlier date. Was: Open, stated in spec 7.7 item 6 (4 October 2026). Sweep (5 October 2026): stated; nothing runnable.
Status: Open, staged (8 October 2026, the enforced-proving lane; `docs/spec/proving-enforcement.md` section 7 carries the staging table): the closure is four explicit stages, each a claim about one input with the native check that holds it until the next stage, never a self-contained proof of the whole state. Stage 0 (today): rewards and payouts are data in the shard statement and every node's native derivation vetoes a statement that differs; enforced proving adds that the record's proof must verify for that statement (ledger P21). Stage 1: the rewards list checked against a commitment the aggregator carries in its public values, the commitment itself recomputed natively from the mergeset. Stage 2: the payouts derived inside the aggregator guest from the carried records it verifies, `carried_payouts` the native check of the same derivation. Stage 3: the rewards derived inside the aggregator guest from the headers and blue sets it verifies, the consensus proof proper; only here do rewards stop being an input anywhere. Stages 1 to 3 are the phase 2 consensus-proof work (Nov 2026 to Jan 2027 per the litepaper roadmap); no served text says "the rewards and payouts are proven" before stage 3. The 2.0 plan's negative test (6) (incorrect rewards or consensus inputs: derivation authenticated) is team-tested for the native veto (the executor test `enforced_a_statement_over_altered_rewards_or_payouts_is_vetoed_native_derivation_is_the_check`, green on build-2 at 17:10 UK on proving-payment 421bb852: every node's own derivation refuses a statement over other rewards or payouts) and PENDING for the proof side, the derivation inside the aggregator guest, which is stage 3. Boundary sentence for every served text: a proof of execution is not a proof of authenticated consensus inputs, canonical history or data availability. Was: Open, blocked on the phase 2 consensus proof (design 7: the aggregator derives the rewards and payouts from consensus data it verifies, so they become outputs of the proof): next step that consensus-proof work, in phase 2 (Nov 2026 to Jan 2027 per the litepaper roadmap), no earlier date. Was: Open, stated in spec 7.7 item 6 (4 October 2026). Sweep (5 October 2026): stated; nothing runnable.
Answer: Correct, and already true of the rewards since devnet v4 (`BlockFixture.rewards`, `proving_pool_credit`): the shard statement is "from this pre-root, these transactions, these rewards and payouts, the post-root is X". The node checks the statement against its own execution, which used the rewards and payouts consensus derived, so a proof over a different list does not match any node's statement and pays nothing. Closing it in the proof itself means the aggregator deriving the rewards and payouts from consensus data it verifies (the mergeset's blue blocks and the carried records), which is the consensus-proof work of design section 7.

File diff suppressed because one or more lines are too long

View file

@ -87,6 +87,51 @@ Owner: node lane with the build-server lane (the three ex-testnet seeds are the
- [ ] A withholding concentrated prover: replacement operators, usable inputs, reassignment, explicit behaviour during proof delays.
- Pass: specified behaviour with no emergency algorithm change and no privileged intervention.
### The partition row (finality boundary lane, 8 October 2026; the full statement is `docs/spec/finality-guarantees.md`, the tables `sim/results_v2.md` "Rule v4")
The chain's behaviour across a partition, with no emergency change and no privileged intervention, as rule v4 specifies it and the simulator measures it (the simulator is the checkpoint-level model of `sim/finality_v2.py`: no DAG, 1,000 Pareto keys, each side retargeting and counting only the blocks it has seen; seeds 7 and 11; run on igneum-build-3 under the lease tool at nice 19, 16:02 to 16:09 BST). Known-failed first: rule v3 (the devnet's rule today, `finality_v3_activation_daa`) expires its frozen weight table one window after the last certified checkpoint, which is a timeout, and the external review of 8 October 2026 (accepted 16:1x BST) found that a timeout alone cannot tell a node whether missing miners are gone or on the other side of a partition. Measured: under v3 both sides of a 31-day honest partition lock alone at day 30.00 (50/50: 2,679 to 2,701 conflicting locks; 60/40: 2,822 to 2,870; 55/45: 2,795 to 2,820), with no attacker.
The behaviour (rule v4, the anchored table): a checkpoint locks when two thirds of all 30-day weight at the checkpoint AND two thirds of the weight table anchored at the last certified checkpoint have signed it; the anchored table never expires by time; it is replaced only by a certificate that passes it, reduced only by a strip (3.6) or moved by a succession (W5), both functions of the chain. When too much weight is missing, finality pauses and the chain runs on proof of work: blocks, ordering, execution and every program rotation continue (every seed is drawn from a chain block below the lead on the header's own chain, certified or not; the hourly program, the weekly table and the family epoch all change through a pause of any length, on both sides of a partition; only the emergency flip vote waits). No certified checkpoint is ever reversed. The pause ends when the missing weight returns, hands over, or is stripped, or, after a full window with no certificate, under the majority-continuity recovery: a certificate on the chain through the last certified checkpoint whose signers hold two thirds of the sliding table and strictly more than half of the anchored table, verifiable by any node from the chain alone. Two such certificates share a signer, so at most one side of a partition can recover without equivocation; an even split stays paused until the heal.
- [x] Partition the network, 31 days, 50/50, 60/40, 55/45: v4 pause (no recovery): no side locks, 0 conflicting locks, every pre-heal lock kept, first lock 0 minutes after the heal, 0 stalls in the three hours after; v4 recovery: the same at 50/50; at 60/40 and 55/45 the larger side recovery-locks once at day 30.00 and then locks normally, the smaller side never, 0 conflicts, kept, heal 0 minutes (N1).
- [x] The 40/40/20 split, 150 minutes and 31 days, honest: no side locks under either v4, 0 conflicts, the heal locks at once (N2). With a 20 percent equivocator reaching both 40 sides and mining on one: one side recovers at day 30.00, the other never (after a window the equivocator is under dust on the other side and not in its voter list), 0 conflicts (N2).
- [x] The recovery's bound, the equivocator mining on both sides (N1b at 10, 20, 34 percent across 50/50; N2b at 60/60; build-4, 16:06 to 16:16 BST): two recovery locks need a month-long partition plus equivocating keys above dust on BOTH sides (mining on one side only: 0 conflicts in every row) whose share exceeds the split's imbalance; measured: both sides recover at day 30.00 and the heal shows 2,800 to 2,889 conflicting locks under v4 recovery, 0 under v4 pause; at 34 percent the one-third bound (conflicts from minute 0 under every rule). The pair is a 3.11.4 conflict after the last certified checkpoint, never a reversal, and the equivocator is stripped.
- [x] Interrupt signing while mining continues, 34, 40, 45 percent for 24 hours and 34 percent for 31 days: every checkpoint stalled for the whole silence, first lock 0 minutes after the resume, 0 conflicts; the recovery adds nothing here, since a silent third keeps its share of the sliding table and the signers fail the two-thirds floor itself (N3).
- [x] Remove major operators at once, 35 and 50 percent of weight, 31 days: v3 and v4 recovery lock at day 30.00 at 35 percent; v4 pause never; at exactly half the recovery is a knife edge (one seed day 30.23, one never); 0 conflicts (N6). Old keys bought or stolen and withholding (worth 40 percent, the holder at 30 percent of hashrate): v2 day 20.9, v3 day 30.00, v4 pause never (one purchase is a permanent veto, the cost of the pause-only variant), v4 recovery day 30.00; compromised keys stripped by their owners' self-equivocation on day 1: the first lock the same day (N4).
- [x] Cross epoch boundaries while finality is paused: every seed is a chain block below the lead on the header's own chain, certified or not, so 744 hourly programs change through a 31-day pause and nothing waits (`docs/spec/finality-guarantees.md` section 8, consistent with `04-seeds-and-vdf.md` 4.3 and 4.4 and the era VDF record; O-4.3 closed). Derived; the harness reading of the program id on both sides of a paused boundary is owed (below).
- [x] The harness row on real nodes, the known-failed line (three igneumd of the 0.3.25 pair on the 60x file, `tools/finality-attacks/v3.mjs split50` with SPLIT 420 s past the 240-s expiry; build-4, 17:06 to 17:20 BST): both sides locked alone 192 to 198 s after the cut, 7 new locks each, 4/8/8 conflicting certificates logged, 8 locked indices disagreeing across the three nodes after the heal, locking resumed on both forks: the M3 fault on real fork choice, FAIL as the known-failed line must. [ ] The v4 line on the same command (no lock on either side past the expiry, 0 conflicts, one chain after the heal) needs the node change: one function (`frozen_table` without its drop) plus `continuity_met`, behind `finality_v4_activation_daa`, with the two unit tests named in `finality-guarantees.md` 6.7; the node lane, clock with its next cut after the founder's choice below.
- [ ] The founder's choice, one switch: the recovery as written (recommended: its failure needs an attacker, a month-long partition and equivocation the chain then strips, and it never touches certified history) or the pause only (strictly stronger safety; a sudden honest loss of a third pauses finality until succession, a strip or an operator's trusted certificate on the chain through the last lock).
**The plan's scenario table (igneum-2.0-plan.pdf, p. 19, "Consensus and failure behaviour") mapped to this row's evidence, gaps with clocks:**
| Failure scenario | Acceptance (the plan) | Evidence today | Gap and clock |
|---|---|---|---|
| Prolonged partition | no conflicting final histories within the stated fault assumptions; any loss of liveness explicit | N1 (31 days, three splits): 0 conflicting locks under v4, with the loss of liveness stated per cause in `finality-guarantees.md` section 5's table; the known-failed v3 line reproduced (2,679 to 2,870 conflicts at day 30.00); the fault assumptions in section 2 (under one third of the anchored and every sliding table, partial synchrony, no wall clock) | the node carries v3; the v4 change (one function and a switch) is the node lane's, its unit tests named in section 6.7; clock: the node lane's next cut after the founder's choice |
| Authority-set transition | verifiable continuity from the last certified history; a timeout alone is not evidence missing voters are gone | rule v4: the anchored table changes only by a certificate that passes it, a strip or a succession; the recovery certificate's five checks (section 6.4) are verifiable from the chain by any node or light client; N1 60/40 and 55/45, N6 35 percent, N4: the recovery at day 30.00 with 0 conflicts | the bound with an equivocator above dust on both sides measured (N1b, N2b, above); the light-client check of a recovery certificate (section 10's receipt already carries the voter table) is owed to the reference-apps lane |
| Signing stops, mining continues | defined checkpoint, weight and recovery behaviour, no contradictory certificates | N3: every checkpoint stalled for the whole silence (24 h and 31 days), weight unchanged (the silent set keeps mining), first lock 0 minutes after the resume, 0 conflicts; the behaviour stated in section 5 (the recovery adds nothing against a silent third, Q3's own floor holds) | none in the model; the harness "interrupt signing" row on real nodes (`--no-vote` miners holding a third) is owed: the same harness as the partition line, clock with the v4 node |
| Old voting keys compromised | a distinct analysis from newly arriving hashrate | N4 and section 6.6: a bought or stolen key's weight decays as its blocks age (share = b (1 - t/30) + r t/30, K), the recovery at day 30.00 under v4, the permanent veto under the pause-only variant stated; the self-strip (the owner equivocates once) ends the pause the same day; succession (W5) is the clean transfer | none in the model; not run on a network (O-3.17's forced double certificate is the related owed run) |
| Finality unavailable at a seed boundary | a seed and mining path that does not depend on an unavailable certificate | section 8: every seed (hour, week, era) is a chain block below the lead on the header's own chain, certified or not (`HeaderProcessor::epoch_seed`, `EraVdfManager::cut_block`, class v5's reference block); O-4.3 closed; the certified binding rejected with reasons | the harness reading of the program id on both sides of a boundary during a pause: owed, the same harness run with the epoch lines read from the node logs (clock with the harness line below) |
| Partitions reconnect | deterministic recovery; no quiet reversal of a guarantee labelled irreversible | N1, N2 (24 runs): every pre-heal lock kept, first lock 0 minutes after the heal, 0 post-heal stalls; 3.11.4 unchanged (no certificate deleted, downgraded or re-evaluated; a pair reported as `conflict`); the certificate-driven reorg measured on the fast-time harness (`c4.mjs`, 5 October) | none in the model; the integration run below |
The plan's integration test (ordering, finality, proof queues, voter tables and seed transitions together, not a simulation alone): not run by this lane today. What exists: the fast-time three-node harness (`tools/finality-attacks/v3.mjs`, real fork choice and finality, no proof queue), which ran the known-failed v3 partition line on build-4 (above); the proving layer and the voter-table folds run on the same node line but no harness drives all five together. Clock: the five-together harness is a D5 pin for the node lane with this lane's scenario list, not a Mac job.
**The miner-voted bring-forward as a mechanism (the rotation lane's layer 4, `docs/design/class-rotation-four-layers.md` sections 2 and 6, carried here as a D5 pin; status: a research-class prototype behind `rotation_v6_activation_daa` on a fork branch, the tally `vote_carries` with unit tests; nothing on any network):**
- Activation rule. A `FlipVote` is an item in the coinbase finality section (tag 8, the Leave item's shape, carried last so an older decoder stops cleanly): a BLS signature by a finality voter's key over `"igneum-flip-v1/" || chain_id || 0 || index || hash(C_i) || family_id || target_height`, where `family_id` is the NEXT scheduled family epoch's bank structure and `target_height` an hourly epoch boundary. A node tallies at every lock over 720 consecutive checkpoint indices (six hours; four on the fast-time file) by Q2's block reading, weighted by W2 at `C_i`: the vote carries when at every one of the last 240 indices the keys whose latest flip vote names the same `(family_id, target_height)` hold at least two thirds of active weight AND at least half of total 30-day weight. When it carries and the schedule admits the height (at least `rotation_vote_delay_daa`, 14,400 DAA s, ahead), the node installs the flip: the family epoch that was scheduled for a later height starts at `target_height`, with the draw layer 3 makes from that height's reference block; the installed flip is persisted in the finality state and re-installed on load, so a restarted node never computes the plain schedule beside peers that flipped (fault 7's class, 8 October 2026).
- "No veto" as a mechanism, not a label. The scheduled family epoch is a height written at genesis, not a vote; a target at or past the scheduled height is invalid and dropped; the tally has no negative vote; so nothing can delay a scheduled change and the only thing a vote can do is bring the next one forward. Abstention blocks a bring-forward (the two-thirds-of-active test), and that is all abstention can do.
- Coalition behaviour. A fleet at a third of weight blocks every bring-forward while it holds a third of active weight and cannot delay the schedule; if it goes silent it leaves the active denominator within a presence window and, at a third of total, pauses finality (which costs it its own locks). A fleet with majority weight is the finality problem itself (twenty days of 100 percent hashrate to two thirds) and could carry a bring-forward, which only hurts a chip; it cannot delay. The vote adds no new threshold to buy: a third of active weight is a third of the 30-day blocks.
- Partition handling. No flip vote counts while finality is paused (the tally runs at locks, over certified checkpoints), so a vote cannot carry one-sided: a partition shorter than the lead heals before the flip; one longer has paused finality on at least one side (the partition row above), the vote is void there and the schedule stands; a vote carried before the split flips on both sides at the same height from the same reference block. Under rule v4 a recovery lock counts as a lock.
- Old-client behaviour. The schedule is genesis-fixed, so a client that does not implement the tally computes the plain schedule and, after a carried flip, refuses the flipped family's blocks at `target_height`: the tally is therefore part of the consensus rule from the height `rotation_v6_activation_daa` names, under the digest arm every switch uses (a binary carrying the field peers with one that does not until the height), and the 2.0 line restarts at v2.0.0 with it, so there is no older client on the network once it is live; a node synced from a pruning proof cannot tally below its pruning point and takes the schedule (owed, the class signal's same gap). Replayed or forged votes: the tag separates a flip vote from a checkpoint vote, `chain_id` stops cross-network replay, `index || hash(C_i)` expires it with its checkpoint, two flip votes by one key at one index naming different targets are equivocation under 3.6's evidence shape.
- Pins: [ ] the 720-index tally against `sim/finality_v2.py` under the F2 eclipse, the bought-keys case and rule v4's pause and recovery (this lane, after the partition rows); [ ] the fast-time harness run of a carried flip across a partition (the rotation harness with `rotation_vote_window` 4), the node lane; [ ] the pruning-proof node's tally.
**The seed and VDF pipeline (`docs/spec/04-seeds-and-vdf.md`, `docs/analysis/era-vdf-2026-10-07.md`; carried here as a D5 pin):**
- The property it protects, stated: seed-selection protection and nothing else (the standing rule above: rotation is optional to the security argument). The miner who finds the last block before a reference block could compute the program that block implies, benchmark it, and withhold the block if the program is bad for it; with the delay (the class-group VDF, T at 300x the 2-second decision window at the reference core, 108,000,000 squarings for the era at the measured 30,000 per second) no candidate's draw is knowable inside the window, so withholding has the same expected program as publishing and only burns the block: gain 0 in every row of the grinding table (measured by the re-roll harness: fires with the VDF off, silent with it on, the era record's section 3). It is not an ASIC-resistance claim and is not counted in any resistance ratio.
- Behaviour when finality is unavailable at a seed boundary, defined: every seed (hour, week, era) is drawn from the last selected-chain block below the boundary less the lead, on the header's own selected chain, certified or not; a finality pause of any length changes nothing for rotation (the partition row's seed line, `finality-guarantees.md` section 8); the certified binding is rejected because it would couple the seed to finality liveness and would need a rule for a certificate that lands late, and the delay does not need it. Tested: the determinism and re-roll gates of the era record; owed: the harness reading of the program id on both sides of a boundary during a pause (the harness line below).
**User-facing rule, carried:** included, executed, proven and finalised are four distinct states (section 9 of the statement: the claim, the source of truth and whether each can be withdrawn; only finalised cannot), and a safe pause is reported as a pause (`finality_active` false, reason `paused`, or `recovered` for one window after a recovery lock), never as an unchanged guarantee. Where the interfaces differ today (the wallet's "finality not active" on a block under a lock, the explorer's `final` for finalised, the live page's missing executed and finalised, the receipt pages' "proven" in the verifier's sense) is listed there for the wallet, explorer, live and reference-apps lanes.
- Not guaranteed, stated: at or above one third of equivocating weight two certificates can coexist (reported, never resolved); a recovery lock's bound is the imbalance rule above, not one third; a loss of half or more of the last certified table has no in-protocol exit; an even split pauses until the heal; the first month; body availability and execution correctness (proven execution is not finality: the four interface words, included, executed, proven, finalised, are defined once in section 9 of the statement with where each interface shows them today).
## Pools, software and participation (launch requirements)
Owner: pool lane.

View file

@ -41,7 +41,7 @@ All in public, on the hashrate charts. 51% never reaches 2/3 while honest miners
- **Q2.** Participation of key k at index i, "block reading" (rule of 3 October 2026, ledger F3): the number of indices j in the presence window of i (Q1) for which a valid vote by k at index j appears in the past of C_i, divided by the number of indices in that window, capped at 1. A vote "appears in the past of C_i" when it is carried, as a vote or inside a certificate, by any block in the past of C_i, blue or red. A key whose first block is younger than the presence window counts 1. Votes are block payload: every block MUST carry every valid vote its producer has received for i_b or an index in its presence window (i_b the highest checkpoint index determined in the block's past) that is not already carried by a block in its past, up to the per-block vote bound of O-3.3; votes for one `(index, checkpoint hash)` pair MAY be aggregated inside the block into one BLS signature with a bitmap. A block that omits a vote it has received is not invalid (no node can prove what another received); the rule binds honest producers and the argument of 3.3.2 says why that is enough. This reading is objective, since every node computes it from the same past of C_i, and self-healing, since a missed index rolls out of the window after two hours of median time. The "cert reading" of the simulation (only votes inside certificates count) is a subset of it and was the reading the simulation ran with; the node-local "seen" reading is rejected as not objective and the "frozen" reading as total with extra steps (`sim/results_v2.md`, "Recommended parameters").
- **Q3.** Active weight at i is the sum over voters of weight x participation. Total weight at i is the sum of weight over all keys above dust. A certificate for index i locks when the unscaled weight of its signers is at least **2/3 of active weight** AND at least **2/3 of total weight** (the floor; 17/30 from 3 October 2026 until the founder raised it to two thirds on 4 October 2026, O-3.15). Both comparisons are inclusive: exactly two thirds locks. Both tests use weights and participation computed at C_i, so any node can verify a certificate from C_i's past. Since active weight never exceeds total weight, the second test implies the first: in plain words, a checkpoint locks when two thirds of all 30-day weight has signed it, and finality pauses whenever less than two thirds of that weight is connected and signing, with the chain continuing on proof of work meanwhile and the node reporting the pause (3.9). The active test and the participation of Q2 stay in the rule as the liveness-side report: they are what the node shows operators about who is present, and they are the test that would bind again if a later review lowered the floor.
- **Q4.** Certificate fold (rule v3, 4 October 2026, evening, ledger F22; replaces the grace timer of O-3.4): a certificate forms the moment Q3 is met, so lock latency is not held back. Once every voter has signed, or `certificate_fold` DAA seconds after the checkpoint's determination (3 on devnet, 6 on mainnet; a relay-latency allowance, not a clock of the protocol), a node that holds a certificate rebuilds it from every vote it has seen when that carries more weight and gossips it, and every node replaces a held certificate with a verified certificate over the same block that carries more signed weight. A block carries the heaviest certificate its producer holds; a block that carries a lighter one is as valid as before, since every certificate still verifies against the table at C_i. The lock is never withdrawn by a replacement (3.11.4): a replacement only adds signers over the same block. Why: on the 12-node cloud devnet of 4 October 2026 the first certificate was built median 1.24 s after the first determination with 7 to 10 of 12 signers, while the last of the 12 votes was issued median 1.45 s, p90 2.36 s after it (`infra/cloud-devnet/results/2026-10-04/f22-vote-timing.md`), so two checkpoints locked at 66.8% of total with every key connected. The fold carries the votes that arrive after quorum.
- **Q5.** The frozen weight table (rule v3, 4 October 2026, evening, ledger F21; active for checkpoints at or above `finality_v3_activation_daa`): let `C_f` be the highest certified checkpoint below i whose block is on the selected chain of C_i, and `T_f` the weight table at `C_f` (W2 at `C_f`, bans applied). A certificate for index i locks only if, in addition to Q3, its signers hold at least two thirds of `T_f` at the weights of `T_f`; a key outside `T_f`'s voter list (born since, under dust there, stripped) adds nothing. `T_f` stands while `daa(C_i) < daa(C_f) + 2,592,000` (one weight window); once a window has passed without a certified checkpoint the frozen table is empty and Q3 alone decides, as before v3. Before the first certificate there is no frozen table. In a connected network `C_f` is i - 1 or i - 2 and `T_f` differs from the table at C_i by a minute of blocks, so the test is Q3 with a 30-s lag. Across a partition `T_f` is the last table both sides certified together: a side holds its pre-split share of it whatever it mines afterwards, so no side under two thirds locks until that table has expired, 30 days after the last certified checkpoint (3.7 item 9).
- **Q5.** The frozen weight table (rule v3, 4 October 2026, evening, ledger F21; active for checkpoints at or above `finality_v3_activation_daa`): let `C_f` be the highest certified checkpoint below i whose block is on the selected chain of C_i, and `T_f` the weight table at `C_f` (W2 at `C_f`, bans applied). A certificate for index i locks only if, in addition to Q3, its signers hold at least two thirds of `T_f` at the weights of `T_f`; a key outside `T_f`'s voter list (born since, under dust there, stripped) adds nothing. `T_f` stands while `daa(C_i) < daa(C_f) + 2,592,000` (one weight window); once a window has passed without a certified checkpoint the frozen table is empty and Q3 alone decides, as before v3. **Superseded by rule v4 (8 October 2026, `docs/spec/finality-guarantees.md` section 6): the table is anchored, never expiring by time; after a full window with no certificate a lock needs Q3 and more than half of the anchored table (the majority-continuity recovery), and that expiry clause is the timeout the external review of 8 October 2026 found.** Before the first certificate there is no frozen table. In a connected network `C_f` is i - 1 or i - 2 and `T_f` differs from the table at C_i by a minute of blocks, so the test is Q3 with a 30-s lag. Across a partition `T_f` is the last table both sides certified together: a side holds its pre-split share of it whatever it mines afterwards, so no side under two thirds locks until that table has expired, 30 days after the last certified checkpoint (3.7 item 9).
### 3.3.1 Why the floor, and why two thirds, from the simulation
@ -132,7 +132,7 @@ Two votes by one key for different checkpoint blocks at one index are equivocati
6. The dust threshold excludes solo miners under 100 blocks a month from voting, not from rewards.
7. New honest miners are under-weighted for the whole window: after an overnight doubling the new cohort holds t/60 of weight on day t and the old cohort can lock without a single new signature for 19 days (`sim/results_v2.md` G). A doubling and a 1x renter are the same event to the rule, by design.
8. The simulation has no DAG: a partition side's checkpoint block is "the block at blue score 30 i in that view" and conflict counts are index collisions, not reorg depths. Real GHOSTDAG merge under the 3,600-s bound, DAA lag (of the order of an hour, approximate), VRF aggregator noise, uptime (the 0.978 resting participation is a guess), regional silent sets and the cost of keys are all outside the model.
9. The weight table is per view. A side of an honest partition sees only its own blocks after the split, so its share of its own 30-day table rises as `s + (1 - s) t / 30` on day t for a pre-split share s, and it holds two thirds of its own table from day `30 (2/3 - s) / (1 - s)`: day 10 at 50/50, day 5 for the 60 side of 60/40, day 7.8 for the 55 side of 55/45 (3.3.1, measured in `sim/results_v2.md` L4 and found first on the devnet by attack scenario 6A, where the old floor fell at 84 s of a young window). From that day the side locks alone and two sides can hold conflicting locks at the heal with no attacker. The heal does not undo it: the other side's blocks arrive and are merged red, which only raises each side's share of its own table (W2 counts blue blocks), each side certifies its own checkpoints, and F1 pins every node to the chain its certificates name, so the network heals and finality stays forked until an operator sets a trusted certificate (F5; 3.11.4). Measured on the devnet: a 50/50 split on a full 1,800-DAA window at 3 blocks/s held for 150 s and both sides locked alone at 205 and 215 s against a predicted W / (3R) = 200 s, leaving 23 locked indices in disagreement across three nodes with no equivocation (`docs/bench-log.md`, "finality floor 2/3", 6A long heal). Item 1's bound is about the weight each view counts; a partition that lasts a third of the window changes what the views count. Rule v3 (Q5, 4 October 2026 evening) adds the table frozen at the last certified checkpoint to the lock test, which a side cannot fill: under it neither side of a split under two thirds locks until a full window has passed without a certified checkpoint (day 30 after the last lock, `sim/results_v2.md` M2 and M3; on the devnet scale `W / R` of a side's DAA time instead of `W / (3R)`), the heal then resumes locking on one chain with 0 conflicting certificates, and a side at or above two thirds (the 4/2 split at 70/30) locks at once as before. A partition longer than a window still forks as described above. The exchange guidance of 3.9 therefore treats a pause combined with peer loss as proof of work, and 3.11.4 says what the node does with the pair.
9. The weight table is per view. A side of an honest partition sees only its own blocks after the split, so its share of its own 30-day table rises as `s + (1 - s) t / 30` on day t for a pre-split share s, and it holds two thirds of its own table from day `30 (2/3 - s) / (1 - s)`: day 10 at 50/50, day 5 for the 60 side of 60/40, day 7.8 for the 55 side of 55/45 (3.3.1, measured in `sim/results_v2.md` L4 and found first on the devnet by attack scenario 6A, where the old floor fell at 84 s of a young window). From that day the side locks alone and two sides can hold conflicting locks at the heal with no attacker. The heal does not undo it: the other side's blocks arrive and are merged red, which only raises each side's share of its own table (W2 counts blue blocks), each side certifies its own checkpoints, and F1 pins every node to the chain its certificates name, so the network heals and finality stays forked until an operator sets a trusted certificate (F5; 3.11.4). Measured on the devnet: a 50/50 split on a full 1,800-DAA window at 3 blocks/s held for 150 s and both sides locked alone at 205 and 215 s against a predicted W / (3R) = 200 s, leaving 23 locked indices in disagreement across three nodes with no equivocation (`docs/bench-log.md`, "finality floor 2/3", 6A long heal). Item 1's bound is about the weight each view counts; a partition that lasts a third of the window changes what the views count. Rule v3 (Q5, 4 October 2026 evening) adds the table frozen at the last certified checkpoint to the lock test, which a side cannot fill: under it neither side of a split under two thirds locks until a full window has passed without a certified checkpoint (day 30 after the last lock, `sim/results_v2.md` M2 and M3; on the devnet scale `W / R` of a side's DAA time instead of `W / (3R)`), the heal then resumes locking on one chain with 0 conflicting certificates, and a side at or above two thirds (the 4/2 split at 70/30) locks at once as before. A partition longer than a window forked as described above under v3 (`sim/results_v2.md` M3: both sides at day 30.00); under rule v4 (`docs/spec/finality-guarantees.md` section 6, 8 October 2026) it does not: the anchored table never expires, and after a window at most one side, the one holding more than half of the last certified table, can lock. The exchange guidance of 3.9 therefore treats a pause combined with peer loss as proof of work, and 3.11.4 says what the node does with the pair.
## 3.8 The first month
@ -214,7 +214,7 @@ What the floor removed. Under the 0.85 floor of 3 October 2026 the binding fract
The trade the floor sets was linear, and it was decided. With the floor at `f x 2/3` the partition bound is `4f/3 - 1` and the liveness bound of 3.11.3 is `1 - 2f/3` of weight silent: 13.3% and 43.3% at f = 0.85, 33.3% and 33.3% at f = 1. The founder chose f = 1 on 4 October 2026 (O-3.15): one third on both sides, a pause whenever less than two thirds of the weight is signing, no scenario in which a faction under a third can split finality.
What remains is the weight table itself (3.7 item 9). The bound above is about the total weight `T` as each view computes it from its own past. In a partition each side's window fills with its own blocks only, so a side with pre-split share `s` holds `s + (1 - s) t / 30` of its own table on day `t` and reaches two thirds of it on day `30 (2/3 - s) / (1 - s)`: 10 days at 50/50, 5 days for the 60 side of 60/40 (measured within the uptime shortfall in `sim/results_v2.md` L4; 4 days and 0 days under the old floor). From that day the side locks alone with `a = 0`, and two such sides hold conflicting certificates at the heal. That is the twenty-day event of 3.1 seen from inside a partition, with the clock started at the split: the floor at two thirds moved it from day 4 to day 10 and cannot remove it, because a view cannot count blocks it has never seen. Rule v3 (Q5) moves it to day 30 for every split under two thirds: the signers must also hold two thirds of the table frozen at the last certified checkpoint, which both sides share and neither can fill, and that table stands for one window. **S under v3:** with `a < 1/3` no two honest views hold conflicting certificates across any partition shorter than one weight window since the last certified checkpoint (`sim/results_v2.md` M2, M3, M5: 0 conflicts in 12-day splits, the first conflict at day 30.00 to 30.06 of a 31-day split, the 34% equivocator still conflicts from minute 14). Beyond a window the frozen table is empty and the paragraph above applies.
What remains is the weight table itself (3.7 item 9). The bound above is about the total weight `T` as each view computes it from its own past. In a partition each side's window fills with its own blocks only, so a side with pre-split share `s` holds `s + (1 - s) t / 30` of its own table on day `t` and reaches two thirds of it on day `30 (2/3 - s) / (1 - s)`: 10 days at 50/50, 5 days for the 60 side of 60/40 (measured within the uptime shortfall in `sim/results_v2.md` L4; 4 days and 0 days under the old floor). From that day the side locks alone with `a = 0`, and two such sides hold conflicting certificates at the heal. That is the twenty-day event of 3.1 seen from inside a partition, with the clock started at the split: the floor at two thirds moved it from day 4 to day 10 and cannot remove it, because a view cannot count blocks it has never seen. Rule v3 (Q5) moves it to day 30 for every split under two thirds: the signers must also hold two thirds of the table frozen at the last certified checkpoint, which both sides share and neither can fill, and that table stands for one window. **S under v3:** with `a < 1/3` no two honest views hold conflicting certificates across any partition shorter than one weight window since the last certified checkpoint (`sim/results_v2.md` M2, M3, M5: 0 conflicts in 12-day splits, the first conflict at day 30.00 to 30.06 of a 31-day split, the 34% equivocator still conflicts from minute 14). Beyond a window the frozen table was empty under v3 and the paragraph above applied; under rule v4 it never empties (`docs/spec/finality-guarantees.md` section 4: the bound has no time term; section 6.5 states the recovery certificate's own bound).
The second clause of S (chain of a lower certified checkpoint) follows from the first with F1 and C3. Once an honest node holds a certificate for `C_i` it never selects or signs a chain that misses `C_i`, and after GST every honest node holds every certificate within one block interval plus `Delta` (C3 carriage and gossip). A certificate at `j > i` off `C_i`'s chain therefore needs `q T` of weight that lacks `C_i`'s certificate, which before GST is a side of a partition, and the first clause already bounds any lock that side forms; after GST only the adversary lacks it, and `a < q`. The residual is honest votes for `C_i` in flight across a partition boundary in the `Delta` before the split, which strengthen `C_i`'s certificate and nothing else.
@ -235,7 +235,7 @@ The arithmetic. The floor needs `Y >= 2/3` of total, which no behaviour of the r
Why the adversary cannot block L within the model: withholding votes changes nothing above; equivocating strips its weight and lowers `T`; dropping votes from its own blocks delays a vote's entry into the DAG by one honest block, since Q2 needs one block in `C_i`'s past to carry it and honest producers carry every vote they receive; aggregating dishonestly costs nothing because anyone MAY aggregate (S1) and every honest node aggregates the votes it holds; buying keys moves weight between holders and leaves `Y`'s arithmetic as it is.
**When finality pauses.** Finality pauses whenever less than two thirds of the weight is connected and signing. In the model, whose honest keys are in outage 2.2% of the time, that is reached once about 32% of weight is silent (L1: 30% silent locks every checkpoint, 32% locks 88% of them, 33% locks 11% with a first gap of 49 minutes, 34% locks none for as long as it stays silent), and a key that keeps mining never ages out, so a 34% silent set holds the pause for as long as it stays silent (J and L1: 0 conflicts, the first lock 0 minutes after it returns). A set that stops mining ages out: with a share `x` gone and the survivors inheriting the block supply, the live share of total is `1 - x (30 - t) / 30` on day `t` and reaches two thirds on day `30 (1 - 1/(3x))`: 1.4 days at 35%, 10 days at 50% (L2 and D's total column). Under rule v3 (Q5) a set of one third or more that leaves at once also leaves the frozen table only when that table expires, so the first lock after such a departure comes on day 30 whatever `x` (M4: 30.00 days at 35% and at 50%); `L` is therefore: after GST, if honest voters holding two thirds of total weight AND two thirds of the frozen table are connected and signing, a checkpoint certifies within `T`; a departure that outweighs the frozen margin pauses finality for one window, and the node reports it. The chain does not stop: blocks, GHOSTDAG ordering (F2 among the tips through the last certified checkpoints) and execution continue on proof of work, every certified checkpoint stays binding, and the node reports `finality_active` false with the reason `paused` (3.9: the exchange guidance for that state is the finality depth in median time). The honest name for this state is "finality temporarily unavailable", and it is the state the test network showed for 12 checkpoints with one voter at 39.6% of total weight (3.11.7) and the state the floor test network showed on both sides of a 3/3 split (bench-log, "finality floor 2/3"). The litepaper says so in its Finality section and in "What Igneum does not claim" (R3.18).
**When finality pauses.** Finality pauses whenever less than two thirds of the weight is connected and signing. In the model, whose honest keys are in outage 2.2% of the time, that is reached once about 32% of weight is silent (L1: 30% silent locks every checkpoint, 32% locks 88% of them, 33% locks 11% with a first gap of 49 minutes, 34% locks none for as long as it stays silent), and a key that keeps mining never ages out, so a 34% silent set holds the pause for as long as it stays silent (J and L1: 0 conflicts, the first lock 0 minutes after it returns). A set that stops mining ages out: with a share `x` gone and the survivors inheriting the block supply, the live share of total is `1 - x (30 - t) / 30` on day `t` and reaches two thirds on day `30 (1 - 1/(3x))`: 1.4 days at 35%, 10 days at 50% (L2 and D's total column). Under rule v3 (Q5) a set of one third or more that leaves at once also leaves the frozen table only when that table expires, so the first lock after such a departure comes on day 30 whatever `x` (M4: 30.00 days at 35% and at 50%); `L` is therefore: after GST, if honest voters holding two thirds of total weight AND two thirds of the frozen table are connected and signing, a checkpoint certifies within `T`; a departure that outweighs the frozen margin pauses finality for one window, and the node reports it (under rule v4 the pause ends at the window only for a surviving majority of the anchored table, `docs/spec/finality-guarantees.md` section 5's table of causes). The chain does not stop: blocks, GHOSTDAG ordering (F2 among the tips through the last certified checkpoints) and execution continue on proof of work, every certified checkpoint stays binding, and the node reports `finality_active` false with the reason `paused` (3.9: the exchange guidance for that state is the finality depth in median time). The honest name for this state is "finality temporarily unavailable", and it is the state the test network showed for 12 checkpoints with one voter at 39.6% of total weight (3.11.7) and the state the floor test network showed on both sides of a 3/3 split (bench-log, "finality floor 2/3"). The litepaper says so in its Finality section and in "What Igneum does not claim" (R3.18).
### 3.11.4 Recovery and what "irreversible" means

View file

@ -149,8 +149,9 @@ Section 3.11 states the finality guarantees with their assumptions and derives t
| O-3.16 (text closed) | 3.3.1 ("the safety bound stays at 1/3 of weight for every event tested") and 3.7 item 1 ("safety holds with under one third") stated the connected-network bound only; under the 0.85 floor 3.11 item 2 gave 1/3 only while every honest voter's votes reached every honest node within 41 minutes, falling to 4/30 beyond that | Closed 4 October 2026 by O-3.15: at f = 1 the bound is one third in every view whatever the presence window says (two certificates need 4/3 of weight in signatures), and 3.3.1, 3.7, 3.11.2 and the litepaper say so. What remains is the window bound of 3.3.1 (a side that mines alone for a third of the window holds two thirds of its own table), stated there and in 3.7 item 9, measured in L4 and on the devnet (6A) | 3 (text) |
| O-3.17 | Two valid certificates at one index: 3.5's proposal (strike the equivocators, re-evaluate, treat the index as uncertified if neither or both lock) makes a verified lock revocable (R3.17, ledger F16). 3.11 item 4 replaces it: a verified certificate is never withdrawn, the node reports `finality_conflict`, clears `finality_active`, keeps following the certificate it verified first, and the split is resolved by operators through F5, as Kaspa resolves a finality conflict by notification and not by rule (`vendor/rusty-kaspa/consensus/notify/src/notification.rs`, `FinalityConflict`) | Replace the paragraph in 3.5 with 3.11 item 4; implement `finality_conflict` in the node; devnet test that forces a double certificate and checks that no node ever reports a lock it later withdraws. Closes the rule half of O-3.6; the devnet half stays | 3 |
| O-3.18 (narrowed) | The liveness bound T of 3.11 item 3 was derived under the block reading of Q2 (absent keys decay, present keys hold 1), which recovers more slowly than the cert reading the simulator runs. With the floor at 2/3 (O-3.15, 4 October 2026) the presence decay no longer enters T: a lock needs two thirds of total whatever participation says, so T = I + d + G + Delta (107 s) whenever two thirds is connected and signing, and below that finality pauses for as long as the shortfall lasts (silence) or until the missing weight ages out (churn, 30 (1 - 1/(3x)) days for a set holding x). What is left is the exit from a pause under the block reading: the first lock after the silent set returns is 0 minutes under the cert reading (`sim/results_v2.md` J and L1) | The O-3.3 re-run under the block reading reports the first lock after a 40% silent set returns and confirms or corrects the 0-minute figure | 3 |
| O-4.3 (decision) | Decided 3 October 2026 by 3.11 item 6: the seed checkpoint is the selected-chain block at the checkpoint blue score the lead rule names, certified or not, so a finality pause never stops the hourly program. Remaining: implement `seed_source` from the checkpoint block (the devnet keys the program on the header's `daa_score`, R3.26) and run an epoch boundary through a forced pause on the devnet | Implementation and the devnet pause test | 3 |
| O-4.3 (decision; closed 8 October 2026 by `docs/spec/finality-guarantees.md` section 8 for the epoch and the era alike: every seed is drawn from a chain block, never from a certificate) | Decided 3 October 2026 by 3.11 item 6: the seed checkpoint is the selected-chain block at the checkpoint blue score the lead rule names, certified or not, so a finality pause never stops the hourly program. Remaining: implement `seed_source` from the checkpoint block (the devnet keys the program on the header's `daa_score`, R3.26) and run an epoch boundary through a forced pause on the devnet | Implementation and the devnet pause test | 3 |
| O-3.19 (narrowed) | Q2 credits participation for "a valid vote by k at index j" and the node credits any vote at the index whatever block it names (3.10). A key can then vote for a block of its own at every index, keep participation 1 and its weight in the active denominator, and never add to a certificate. Under the 0.85 floor that pushed the honest lock threshold from 17/30 of total up to 2/3 of total; under the 2/3 floor (O-3.15, 4 October 2026) honest voters need 2/3 of total for every lock anyway, so the attack changes no lock and no bound. What it still distorts is the report: a key voting for private blocks reads as present. 3.11.1 reads Q2 as crediting only a vote that names C_j on the selected chain of C_i | Write the reading into Q2 and the node's participation count as a reporting rule; no re-run of C is needed for the lock threshold | 3 (reporting) |
| O-3.20 (opened and answered, 8 October 2026) | The long-partition boundary: Q5's frozen table expired one window after its checkpoint, a timeout, and `sim/results_v2.md` M3 showed both sides of a 31-day honest partition locking alone at day 30.00; the external review of 8 October 2026 (accepted by the founder, 16:1x UK) requires either a pause with the conditions that lift it or a disclosed, verifiable continuity rule | Answered by `docs/spec/finality-guarantees.md`: rule v4, the anchored table (never expiring by time) with the majority-continuity recovery (after a full window with no certificate, Q3 plus more than half of the anchored table, on the chain through the last certified checkpoint), simulated in scenario N; the founder chooses between the recovery and the pause-only variant (one switch); the node change (one function, `finality_v4_activation_daa`) and the harness rows are owed (that document's section 11) |
Count after this addition: section 3 has 19 items (O-3.15 to O-3.19 added; O-3.6 narrowed to the devnet test), section 4 keeps 9 with O-4.3 decided and awaiting implementation; total 66.

View file

@ -8,6 +8,7 @@
| 1 | `01-lottery-hash.md` | Measured (construction, vectors on three GPU vendors and two CPU references); Designed (header binding, day key, growth, era schedule); Open where marked | Seed words, generator, memory-hard cache and items, 32-lane unit and the wave64 rule, CPU verifier, epoch, day and era schedules, test vectors, determinism, conformance, the prototype-value list |
| 2 | `02-consensus.md` | Designed | The ordering layer as a delta on rusty-kaspa: 1 BPS and the steps, k, merge depth, DAA, header fields, emission, duplicate inclusion |
| 3 | `03-finality.md` | Designed (simulated without a DAG, not implemented, not reviewed) | Weight W1 to W5, checkpoints C1 to C5, quorum Q1 to Q4 with the 56.7% floor and the simulation that justifies it, the eclipse case closed by the floor (3.3.2), participation from votes in blocks (Q2, 3.4.1), sortition, fork choice, equivocation, residual risks, the first month, exchange guidance |
| 3a | `finality-guarantees.md` | Designed (8 October 2026; simulated in `sim/finality_v2.py` scenario N; the node change owed) | The finality boundary: assumptions, the two-thirds rule as the code tests it, Safety and Liveness with their arguments and the pause's duration by cause, rule v4 (the anchored table and the majority-continuity recovery) closing the 31-day partition fault of rule v3, what is not guaranteed, program rotation during a pause (seeds from chain blocks), the four interface words |
| 4 | `04-seeds-and-vdf.md` | Measured (primitive, one machine); Designed (pipeline); Open (fallback) | Class-group Wesolowski VDF, T from a genesis reference rate, 20-minute and 2-hour leads, proof format, who evaluates, fallback options |
| 5 | `05-fees-and-economics.md` | Designed | Base fee burned, priority fee 80/20 with per-frame attribution and the factory rule, proving pool, job market 90/10, no development fund, parameter signalling at 60%, upgrades at 90%, no stake, no treasury |
| 6 | `06-open-items.md` | Open | 60 items, each with the experiment or decision that closes it and its gate; O-2.8 closed and O-3.3, O-3.7, O-5.1, O-5.2 and O-5.6 narrowed on 3 October 2026 |

View file

@ -0,0 +1,207 @@
# Igneum protocol specification: finality guarantees and the long-partition boundary (rule v4)
Spec version 0.1, 8 October 2026, the finality boundary lane (branch `finality-boundary`). Status: Designed. Simulated at the checkpoint level (`sim/finality_v2.py` scenario N, rules `v3`, `v4 pause`, `v4 recovery`; the tables in `sim/results_v2.md`, "Rule v4"). Not implemented: the node change is one function and one switch (section 6.7). Not externally reviewed: this document is the answer to the external review the founder accepted on 8 October 2026 at 16:1x UK, whose finding is quoted in 6.1. Section 3 of the specification (`docs/spec/03-finality.md`) stays the rule; where this document and section 3 differ, this document is the statement and section 3 carries a pointer (Q5, 3.7 item 9, 3.11.2, 3.11.3). Labels as in the rest of the specification: **measured** (a simulator table or a network run, cited by scenario), **derived** (arithmetic shown in place), **designed** (a rule with no measurement yet).
One paragraph for a reader with a minute. A checkpoint is final when two thirds of all 30-day mining weight has signed it, counted twice: against the sliding table at the checkpoint and against the table anchored at the last certified checkpoint. When too much of that weight is missing, finality pauses and the chain keeps running on proof of work; nothing ever reverses a certified checkpoint. The pause does not end on a timer, because a timer cannot tell a node whether the missing miners are gone or on the other side of a partition. It ends when the missing weight returns, when it hands itself over, when it is stripped, or, after a full window with no certificate, when signers holding more than half of the last certified table certify the chain through it: the majority-continuity recovery, a proof any node checks from the chain. Program rotation never waits for finality: every seed is drawn from a chain block, certified or not.
## 1. Scope
This document states, in one place and in the form an external reviewer can check line by line: the assumptions (section 2), the two-thirds rule exactly as the node tests it (3), the safety and liveness statements with their arguments (4, 5), the long-partition rule chosen and defended with the fault it closes (6), what is not guaranteed (7), the single answer to program rotation during a pause (8), and the four words every interface uses for a transaction's state (9). The test table (10) ties each claim to a scenario, known-failed first. Section 11 lists what is owed.
"The rule" means W1 to W6, C1 to C5, Q1 to Q5 of section 3 with the change of 6.2 here, S1, F1 to F5, 3.6 and 3.11.4.
## 2. Assumptions
### 2.1 Weight and the eligible signer set
- Weight is blocks: a key's weight at checkpoint `C` is the number of blue blocks in the trailing 30-day window of `C`'s past that name the key (W2; the simulator counts DAA time, the node counts DAA score along the selected chain's mergesets, the rule of 3 October 2026 denominates in past-median time). Nothing but blocks changes it: no stake, no bond, no coins.
- The **sliding table** `T(i)` at index `i` is the weight of every key above dust (100 blue blocks in the window, W3) and not stripped, computed at `C_i` from its own past. The **anchored table** `T_f` is the sliding table at `C_f`, the highest certified checkpoint whose block lies on the selected chain of `C_i` (Q5's "frozen table", renamed here because under this document it does not expire). Both are functions of the chain, so every node with `C_i`'s past computes the same tables, and a certificate carries enough (index, block, bitmap over the canonical voter list) for anyone to verify it against them.
- **How the eligible set changes, and over what window.** A key enters when its window count reaches 100 blue blocks (about 9 to 10 days for the smallest honest key, 20 days for every key of a 1,000-key Pareto network from zero history, measured in `sim/results_v2.md` A). A key leaves the sliding table by ageing out (every block leaves the window 30 days after it was mined, whoever holds the key), by dust, by the equivocation strip (3.6: zero weight from the first block in `C`'s past that carries the evidence, for one window), by succession (W5: its weight moves once to a successor that both keys signed for, a function of the chain, carried in any block) and by the leave item where that rule is active (`finality_leave_activation_daa`; set at DAA 93,600 on Devnet 3's object). The anchored table changes in exactly two ways: it is **replaced** when a certificate passes it (then `T_f` becomes the table at the new certificate), and it is **reduced** by a strip or moved by a succession, both functions of the chain (the node applies "the bans known now" to `T_f`, section 3's Q5 row). It is never changed by time. The window over which the set can turn over completely is therefore one weight window, 30 days, **and only while certificates keep forming**; while no certificate forms, the anchored table holds the set as it was at the last certified checkpoint.
- Keys are free (W6): every rule draws by weight, never per key.
### 2.2 The adversary
Controls keys holding a fraction `a` of the anchored table and of every sliding table at the indices in question (`a` the largest such fraction). May equivocate, withhold, drop votes and certificates from its blocks, aggregate as it likes, buy or steal old keys with their history, delay its own messages, mine privately. May not forge BLS signatures or produce blocks without hashrate. Its share of a view's active weight is not bounded by `a` alone, which is why the floor binds on total weight (3.3.2).
### 2.3 Delay
Partial synchrony: after an unknown global stabilisation time (GST) every message between honest nodes arrives within a one-way bound `Delta`; before GST messages may be delayed arbitrarily. A partition or an eclipse is a period before GST for the nodes involved. The simulator ran `Delta` = 2 s between regions (0.5 and 5 s swept). **No rule in this document reads a wall clock**: every interval is blue score, DAA score or past-median time, and the only duration the rule names, one weight window, is the window itself. "A full window with no certificate" is `daa(C_i) >= daa(C_f) + 2,592,000`, a fact of the chain.
### 2.4 Honest hashrate
A majority of hashrate follows GHOSTDAG honestly (section 2). Finality adds a bound in weight, not hashrate.
### 2.5 The model's limits
The simulator has no DAG (a side's checkpoint is "the block at blue score 30 i in that view"; conflicts are index collisions), its honest keys are in outage 2.2 percent of the time, participation is the cert reading (a subset of the node's block reading, which is never lower), and a partition side counts only the blocks it has seen (`+local`, the real node's window). Each side retargets at once (`+daa`), the worst case for every clock the rule has. These are the assumptions of every table cited below.
## 3. The rule as the code has it
The node (`consensus/src/processes/finality.rs`, `lock_test`; the constants in `consensus/core/src/finality.rs`, `FinalityParams`, 2/3 since 4 October 2026 on the devnet-v4 line and every node line since; `quorum_met`, `floor_met` and `locks` are pure functions with the unit test `floor_is_two_thirds_of_total_and_inclusive`) locks a certificate for index `i` over block `C_i` when, with `signed` the weight of its signers at the table of `C_i`:
```
3 x signed x P >= 2 x active_num Q3, the active test (active_num = sum over voters of weight x participation count, P the presence window)
3 x signed >= 2 x total Q3, the floor (total = the sliding table T(i))
3 x signed_f >= 2 x total_f Q5, the anchored table (signed_f = the signers' weight AT THE WEIGHTS OF T_f; total_f = T_f)
```
All three inclusive: exactly two thirds locks. The floor implies the active test (active weight never exceeds total), so in plain words **a checkpoint locks when two thirds of all 30-day weight at the checkpoint, and two thirds of all 30-day weight at the last certified checkpoint, has signed it**. The active test and participation (Q2) stay as the liveness-side report of who is present.
What the node does today with the third line that this document changes (the Q5 row of 3.10): `frozen_table` finds the highest locked index below `i` whose block is an ancestor of `C_i`, takes `voters_at` of that block with the bans known now, **and drops it when `daa(C_i) >= daa(C_f) + weight_window`**, after which the first two lines alone decide. That drop is the timeout of 6.1. Rule v3 is behind `finality_v3_activation_daa`; rule v4 (6.2) is the same function without the drop plus the recovery test, behind `finality_v4_activation_daa` (6.7).
## 4. Safety
**S.** As long as the adversary holds less than one third of the anchored table and less than one third of the sliding table in every view, honest nodes never hold two certificates at one index naming different blocks, and never hold a certificate whose checkpoint block is off the chain of a lower certified checkpoint. S holds before and after GST, under a partition of any length and an eclipse of any length, and does not depend on the presence window.
The argument (derived). Two certificates at index `i` on different blocks each carry signatures of at least `2/3 T_f` by the anchored table (Q5), so the weight that signed both is at least `4/3 T_f - T_f = T_f / 3`; an honest key signs one block per index, so that weight is equivocating, and `a >= 1/3`. The same arithmetic on the sliding table gives the same bound in every view. Under rule v3 the Q5 line vanished one window after `C_f`, and from that point each side of a partition tested only against a sliding table it had filled with its own blocks (`s + (1 - s) t / 30` of it on day `t`, two thirds of it by day `30 (2/3 - s) / (1 - s)`, 3.7 item 9), so two honest sides with `a = 0` both passed at day 30: the measured fault of 6.1. Under rule v4 the anchored line never vanishes, so the bound has no time term. The recovery test of 6.2 is the one place where a certificate can lock with less than two thirds of the anchored table; its own bound is stated in 6.5 and counted in 7, and it never applies before a full window without a certificate.
The second clause (chain of a lower certified checkpoint) follows from F1 and C3 as in 3.11.2: a node that holds a certificate for `C_i` never selects or signs a chain that misses it, and a recovery certificate must lie on the chain through `C_f` by construction (6.2 item 3).
Measured: 0 conflicting locks in every honest partition of every tested length (E, I, L4, M1, M2), in every eclipse (L3), with equivocators up to 33 percent across a 50/50 split for 360 minutes (H at the 2/3 floor); 2 to 54 conflicts at 34 percent (the bound, H); the N1 rows of this document at 31 days.
## 5. Liveness
**L.** After GST, if honest voters holding at least two thirds of the sliding table at `C_i` AND at least two thirds of the anchored table `T_f` are mutually connected within `Delta` and signing the chain through `C_f`, a new checkpoint certifies within `T = I + d + G + Delta` = 107 s (`I` = 30 s, `d` = 60 s, `G` = 15 s, `Delta` = 2 s; simulated lock latency after the checkpoint block median 2.5 s, p99 4.6 s, A; the test network median 0.80 s). Below that there is no bound: finality pauses, the chain continues on proof of work, and the node reports `finality_active` false with the reason.
The argument (derived). The floor and the anchored test need the stated fractions and nothing the rest of the network does can raise or lower them (silence leaves both tables unchanged; equivocation lowers both; buying keys moves weight between holders). The active test is implied. `I + d` is the wait for the next determination, `G + Delta` the vote round trip. Withholding changes nothing above; dropping votes from a block delays a vote's entry into the DAG by one honest block; aggregating dishonestly costs nothing because anyone MAY aggregate.
**How long a pause lasts, by cause** (the honest statement a reviewer asked for; each row measured in the scenario named, under rule v4 with the recovery of 6.2, and the same under the pause-only variant where it differs):
| Cause of the pause | Lasts until | v4 recovery | v4 pause only (6.5) |
|---|---|---|---|
| A set of a third or more is silent but keeps mining (J, N3) | it signs again; first lock 0 minutes after, 0 conflicts. The recovery adds nothing here: the silent set keeps its third of the SLIDING table, so the signers fail Q3's own floor (66 percent at 34 percent silent), and the recovery relaxes only the anchored test, never Q3 (N3: no lock in 31 days under either v4) | for as long as it is silent | for as long as it is silent |
| A set leaves gradually while locks continue (M4's gradual case) | nothing: every certificate re-anchors the table and the leavers age out of both tables | same | same |
| A set of a third or more stops mining and signing at once (M4, N6) | the survivors hold more than half of `T_f`: day 30 after the last certificate (N6, 35 percent: day 30.00); exactly half or more gone: never by rule, only by succession, a strip or an operator's certificate (7); at exactly half it is a knife edge (N6, 50 percent: one seed at day 30.23, the other never) | day 30 (35 percent); the knife edge (50 percent) | never |
| An honest partition (N1, N2) | the heal: first lock 0 minutes after, 0 conflicts; or, at day 30, on the side that holds more than half of `T_f` (the 60 of 60/40), the other side never, 0 conflicts | day 30 on one side at most | the heal only |
| Keys worth a third or more bought or stolen and withholding (K, N4) | day 30, the honest majority of `T_f` recovering; a stolen key's owner can strip it by self-equivocation the same day (N4) | day 30 | never for a purchase; the same day for a strip |
| The first month (3.8) | the window is full (`min_daa`) | same | same |
## 6. The long-partition rule: the anchored table and the majority-continuity recovery
### 6.1 The fault this closes
The documented case (`sim/results_v2.md` M3, 4 October 2026, measured): a 31-day honest partition under rule v3. For 30 days neither side locks (each holds half of the frozen table). At day 30.00 the frozen table expires, each side tests against the sliding table it filled with its own blocks, and **both sides lock alone: 2,679 to 2,701 conflicting locks in the 50/50 split, 2,822 to 2,870 in the 60/40 split, the first at day 30.00 to 30.06**, with no attacker. The heal cannot undo them (3.11.4: no lock is ever reversed), so finality stays forked until an operator acts. A set that departs at once shows the other face of the same clause: M4's survivors lock at day 30.00 whether the departed weight is gone or on the other side of a partition.
The review's finding, which the founder accepted on 8 October 2026 at 16:1x UK: the long-partition boundary must be explicit. When enough historical voting weight disappears, the network either preserves safety and pauses, or resumes under a disclosed, verifiable continuity or recovery rule; a timeout alone cannot tell a node whether missing miners are gone or on the other side of a partition. Acceptance: a new authority set cannot override the last certified history without a clearly specified, verifiable continuity rule.
The expiry clause of Q5 is exactly a timeout alone. Rule v4 removes it and adds the one recovery that is not a timeout.
### 6.2 The rule (designed)
Rule v4 replaces Q5's expiry with the following, active for checkpoints at or above `finality_v4_activation_daa`:
1. **The anchored table never expires by time.** `T_f` is the sliding table at `C_f`, the highest certified checkpoint on the selected chain of `C_i`, with the strips and successions known at `C_i` applied. It stands until a certificate that passes it replaces it. A certificate for `i` locks only if its signers hold at least two thirds of `T_f` at `T_f`'s weights, in addition to Q3, **for as long as `daa(C_i) < daa(C_f) + 2,592,000`** (one weight window, as under v3), and
2. **The majority-continuity recovery.** Once `daa(C_i) >= daa(C_f) + 2,592,000` with no certificate formed between `C_f` and `C_i` on this chain, a certificate for `i` locks when its signers hold at least two thirds of the sliding table `T(i)` (Q3, both tests) AND **strictly more than half** of `T_f` at `T_f`'s weights (`2 x signed_f > total_f`), AND
3. `C_f` is an ancestor of `C_i` on the selected chain (the certificate names a chain through the last certified history; a certificate for a chain that misses any lock the node holds is a conflict under 3.11.4, as before).
4. A lock under item 2 is a **recovery lock**: it re-anchors `T_f` at `C_i` (so the next certificate needs two thirds again), it is carried, verified and followed exactly like any other certificate (C3, C4, F1, the certificate-driven reorg of 3.5), and the node reports `finality_reason` `recovered` with the index and the signed share of the old `T_f` for one window after it, then `active`.
5. Before the first certificate of the chain there is no anchored table and Q3 alone decides (3.8's first-month rule applies).
In one sentence: the last certified table is the authority until a new certificate carries the consent of two thirds of it, or, after a full window of silence, of more than half of it; nothing else, and no clock, can replace it.
### 6.3 Why a pause, and why this recovery and not a timeout
A node inside a partition sees exactly what a node after a departure sees: the same chain, the same missing votes, the same elapsed window. No local rule can tell the two apart, which is the review's point, and any rule that resumes on elapsed time alone resumes on both sides of a partition and forks (6.1). The anchored table is therefore the default: when enough weight is missing, finality pauses and stays paused. The chain does not stop (5).
The recovery relaxes the anchored test only; Q3 (two thirds of the sliding table) always stands, so a set that keeps mining and withholds its votes pauses finality for as long as it does so under every rule (N3), and the recovery reaches only weight that has stopped producing blocks on this chain: departed, partitioned away, or the decayed purchase of N4. The majority test is the one resumption that is safe without knowing which case it is in, because it is **asymmetric by construction**: two certificates at one index each carrying more than half of `T_f` need more than `T_f` in total, so some key signed both (derived). Under a partition with no equivocator, at most one side can hold more than half of the last certified table, whatever each side mined since; the 50/50 split holds exactly half on each side and stays paused until the heal (N1, measured: never in 31 days). After a departure, the survivors recover exactly when they are the majority of the table the chain last agreed on (N6: 35 percent gone, day 30; 50 percent gone, never). Bought or stolen keys that withhold lose their veto at day 30 to the honest majority (N4), where the pause-only variant hands one purchase a permanent veto.
### 6.4 The continuity proof, and who can verify it
A recovery certificate is verifiable by anyone holding the chain, with no operator, no committee and no out-of-band input: (i) `T_f` from `C_f`'s past (W2 at `C_f`, bans and successions as the chain carries them at `C_i`), (ii) `T(i)` from `C_i`'s past, (iii) `C_f` an ancestor of `C_i` (reachability), (iv) `daa(C_i) - daa(C_f) >= 2,592,000` and no certificate between (the node's own locks on this chain), (v) the bitmap over the canonical voter list at `C_i` and the aggregate signature. A light client that holds `C_f`'s certificate and the header chain checks the same five facts (section 10's receipt already carries the voter table with weights). This is the "disclosed, verifiable continuity rule" of the acceptance line: the new authority set, the signers of `T(i)`, cannot certify anything unless a majority of the old authority set, `T_f`, signed with them, and never on a chain that misses `C_f`.
### 6.5 The cost, stated, and the one-line alternative
The recovery certificate's safety bound is weaker than one third. Two conflicting recovery locks need a partition that has lasted a full window with no certificate on either side AND equivocating keys that (i) hold a share of `T_f` exceeding the split's imbalance and (ii) are still in BOTH sides' canonical voter lists at the recovery index, which means they mined at least dust (100 blue blocks in the window, W3) on each side: a certificate's bitmap is over the voter list at `C_i` (C3), so a key that mined on one side only is, after a full window, not a voter on the other side whatever its anchored weight. Each side of a 50/50 split with an equivocator at `a` that mines on both holds `(1 - a) / 2 + a` of `T_f`, more than half for any `a > 0`; in a 60/40 split the 40 side needs `a > 0.2`. Measured (N1b, N2b, five rows each over two seeds): an equivocator mining on one side only never produces a recovery conflict (one side recovers at day 30.00, the other never, 0 conflicts, at 10 and 20 percent across 50/50 and in the 40/40 case); one mining on both sides makes both sides recover at day 30.00 and the heal shows 2,800 to 2,889 conflicting locks under `v4 recovery` and 0 under `v4 pause`; at 34 percent both sides lock from minute 0 under every rule (the one-third bound). Every pre-heal lock kept and the heal locks at 0 minutes in every row. In every such case the equivocator is stripped at the heal (3.6), the history through `C_f` is untouched (item 3), and the pair is handled as 3.11.4 says. The one-third bound of section 4 is intact for every certificate formed within a window of the last one, which is every certificate of a connected network.
The alternative the founder can choose by deleting item 2 is the **indefinite pause** (`v4 pause` in the simulator, `P.recovery = False`; in the node, the recovery test left out). Its guarantees are strictly stronger: no certificate ever forms with less than two thirds of the last certified table, so the one-third bound holds for every certificate for ever. Its costs, measured: one purchase of keys worth a third of a window is a permanent veto on finality (N4: never in 31 days, against day 30 with the recovery), a sudden honest loss of a third of weight (a pool folding with its keys) pauses finality permanently absent succession or an operator's trusted certificate (N6: never, against day 30), and a 60/40 partition that outlasts a window pauses the 60 side until the heal instead of recovering at day 30 (N1).
**This document recommends the recovery (items 1 to 5 as written)**, because its failure needs an attacker, a month-long partition and equivocation that the chain then punishes, while the pause's failure needs no attacker and has no exit inside the protocol; and because the recovery never touches certified history, which is what the acceptance line protects. The founder chose "the pause over the fork" on 4 October 2026 (ledger F21) against a fork that needed no attacker; this recommendation keeps that choice (the honest 31-day partition never forks under either variant) and adds a bounded, disclosed exit. The decision is the founder's at the 18:00 UK report; the simulator and the node carry both as one switch.
### 6.6 How it composes with the rest of the rule
- **No certified checkpoint is ever reversed (3.11.4)** stands unchanged: a recovery lock adds a certificate, it never withdraws one; a node holding two valid certificates at one index keeps the first, reports `conflict`, and the protocol does not pick.
- **The certificate-driven reorg (3.5, ledger C4)** carries recovery certificates: a node on the other side of a healed 60/40 partition, holding no lock since `C_f`, verifies the 60 side's recovery certificate against `T_f` and `T(i)` from the block's own past and moves to it. Measured: every pre-heal lock kept, first lock after the heal 0 minutes, 0 post-heal stalls (N1).
- **Succession (W5) and the strip (3.6)** are the two in-protocol ways the anchored table changes without a certificate, both functions of the chain: a miner that retires hands its weight to a successor (which then counts in `T_f` at the old key's weight), and the owner of a compromised key equivocates with it once to remove it from every table (N4: finality resumes the day the evidence is carried).
- **The trusted certificate (F5)** is the operator's exit for the cases no rule covers (half or more of `T_f` gone for good). It gains one check: a configured trusted certificate MUST lie on the chain through every lock the node holds, else it is refused; an operator cannot be handed a certificate that overrides certified history.
- **The exchange guidance (3.9)** gains one row: `finality_reason` `recovered` means a lock formed under 6.2 item 2 within the last window; an operator who prefers the pause-only reading treats it as `paused` for that window. The pause row itself is unchanged: a pause is proof of work, the reorg bound is the finality depth in median time.
- **Layer 4 of class rotation** (the emergency miner vote, `docs/design/class-rotation-four-layers.md`): no flip vote counts while finality is paused, and a recovery lock counts as a lock; the schedule stands throughout (8).
### 6.7 The node change
One function and one switch, for the node lane; nothing here lands on any network until the founder sets the height. `frozen_table` (the Q5 row of 3.10) keeps its reference and loses its drop; `evaluate` and `ingest_off_chain` test `floor_met(frozen_signed, frozen.total)` while `daa(C_i) < daa(C_f) + weight_window` and `continuity_met(frozen_signed, frozen.total)` (`2 x signed > total`, a pure function with its own inclusive-boundary unit test) after, provided no lock exists between; a lock that passed by `continuity_met` is stamped `recovered` in the record and `getFinalityCheckpoints` reports `finality_reason` `recovered` with `anchored_index` and `anchored_signed_share` for one window. Switch `finality_v4_activation_daa` (never on every network until set; the digest arm entered only when set, so a binary carrying the field peers with one that does not, the rule of every switch since 0.3.20). Unit tests, known-failed first: the M3 shape (A at 60 percent and B at 40 percent lock together, B leaves, A alone must not lock under v4 pause ever and must lock under v4 recovery only once the last lock is one window old; under v3 it locks at the window, the known-failed line); and a 50/50 shape that never locks under either v4. The fast-time harness row is `tools/finality-attacks/v3.mjs split50` extended past the window (`SPLIT` above 120 DAA at 60x), where v3 must conflict and v4 must not.
## 7. What is NOT guaranteed
1. **At or above one third of equivocating weight** two valid certificates can exist at one index; the rule reports and does not resolve (3.11.4).
2. **A recovery lock** is bounded as 6.5 states, not by one third: a month-long partition plus an equivocator outweighing the split's imbalance can produce two recovery locks after `C_f`. The history through `C_f` is never touched.
3. **A loss of half or more of the last certified table at once** has no in-protocol exit: finality pauses until the weight returns, hands over or is stripped, or an operator configures a trusted certificate on the chain through the last lock (F5 with 6.6's check). The chain runs on proof of work meanwhile.
4. **An exactly even partition** (each side exactly half of `T_f`) pauses until the heal. Half is not more than half.
5. **The first month** (3.8): no certificate until the window is full; proof of work guidance applies.
6. **Body availability and execution correctness**: a certificate is a statement about a header chain by voters who validated it; availability and pruning are F3 and section 2, execution is section 7's proofs. The "proven" word of section 9 is a different claim from "finalised".
7. **Everything the model excludes** (2.5): no DAG, the 2.2 percent outage guess, the cert reading, no VRF noise, no cost of keys. The young-window form of the sliding bound (`W / R` of a side's DAA time) is a devnet fact, not a mainnet one.
8. **Participation credited for any vote at the index** (O-3.19), the block-reading re-run (O-3.18), the launch-month simulation (O-3.1), the forced double certificate on a network (O-3.17) remain open as section 6 of the specification lists them.
9. **Clock.** Nothing here reads wall-clock time, so nothing here is guaranteed in wall-clock terms: "30 days" is 2,592,000 DAA seconds of the chain, which a retarget lag can stretch or shrink in real time by the amount section 2's controller allows.
## 8. Program rotation when finality is unavailable: the single answer
**Mining and validation continue, and every seed is drawn from a chain block, never from a certificate.** This is one rule for all four layers of class rotation and for both VDFs, and it closes O-4.3 for the epoch as the era VDF lane closed it for the era:
- The hourly program's seed source is the reference block of the epoch: the last selected-chain block below the boundary less the lead, walking down from the header's own selected parent (`HeaderProcessor::epoch_seed` today keys the devnet's epoch on the header's own past, lead 600; class v5's `C_w` is the same block for the hour; 4.3 step 1 names the checkpoint block at that score, certified or not, by the decision of 3 October 2026, 3.11.6). The era's cut block is the same shape at lead 7,200 (`EraVdfManager::cut_block`, `class_signal::seed_below`, 4.4 step 1). The week's and the family epoch's reference blocks in `docs/design/class-rotation-four-layers.md` section 2 are the same shape at their leads. Every one is a function of the header's own past, so two nodes validating one header derive one program, and a header on a chain that reorged across the reference names another block and is validated under that block's seed.
- A finality pause therefore changes nothing for rotation: through a pause of any length, including the first month and the 31-day partition of N1, the program changes every hour, the parameter era every week, the family set every 180 days, on both sides of a partition, each side under the block its own chain names. A later certificate confirms the seed the certified chain used or discards a branch F1 already discarded; it never changes the program of any block on the certified chain (3.11.6).
- The one thing that stops during a pause is layer 4's emergency vote (no flip vote counts while finality is paused, by that document's own rule); the scheduled family epoch is a height, not a vote, and stands.
- The certified binding (the design document's wording, the era record's section 4 alternative) is rejected for both VDFs: it couples the seed to finality liveness (a month-long pause would hand every epoch the same stale checkpoint), it needs a second rule for a certificate that lands late, and the grinding defence does not need it, since the delay makes any candidate's draw unknowable whichever block is the cut.
Consistent with `docs/spec/04-seeds-and-vdf.md` 4.3 step 1 and 4.4 step 1 and with `docs/analysis/era-vdf-2026-10-07.md` section 4. O-4.3 closes with this document; the devnet row "seeds during a pause" of 3.11.7 is still owed as a harness run (11).
## 9. The four interface words
Defined once, for every interface (the wallet, the explorer, the live page, the receipt and light pages, the miner app, the node's status word), from the weakest claim to the strongest. Each word is a claim about one transaction; a stronger word implies every weaker one; no interface may show a word stronger than the chain's own (the wallet's ledger P17 rule, kept).
| Word | Claim | Source of truth | Can it be withdrawn |
|---|---|---|---|
| **included** | a block in the DAG carries the transaction | the including block (`includingBlock` in the receipt's igneum section) | yes: the block can be red or the chain can move; until executed it is an announcement, not a result |
| **executed** | the EVM ran it in a chain block with a number; the receipt (status, gas, the fee split) exists | the chain block of that number (`t.number`, the receipt) | yes: a reorg above the last lock can change the chain block of a number; the receipt then changes with it |
| **proven** | the chain block's execution has been proved: every shard verified or paid (section 7) | the proof records of the chain block (`shards` verified or paid) | yes, for the same reason as executed: a proof is of a block, and the block can leave the chain; it adds "the result was checked", not "the result is permanent" |
| **finalised** | the chain block is at or under the latest locked checkpoint's blue score (a chain block under the lock, or merged by one), in the past of `last_certified` | the lock (a certificate verified against the tables, 3 and 6.4) | **no** (3.11.4): no node ever reports as not final a block it reported as final; a conflict is reported as `conflict`, never as a withdrawal |
Two surrounding states: **pending** (no block carries it) and **failed** (executed, the execution reverted, the fee still paid), and the network state word **finality paused** (the chain is running on proof of work; applies only to blocks ABOVE the last lock). A block under a locked checkpoint is **finalised** whether or not finality is active afterwards; "finality not active" is a statement about the network, never about a block that is already under a lock.
Where each interface shows them today (read from the worktree at box master `5cd69d3d`), and where it does not, for the UI lanes:
| Interface | Shows | Does not, or differs |
|---|---|---|
| Wallet, History page (`app/igneum-wallet/ui/app.js` 131 to 147, `stateWord`; `view.test.mjs` 117) | all four, plus pending and failed, one word per row, the wallet's own verified "finalised" winning over the node's word | shows **finality not active** for a transaction "under a locked checkpoint; finality is paused on the network" (line 144): by this section that transaction is **finalised** and the pause is a network state; the wallet lane should move the pause to the status strip (line 98 already shows "paused" there) and keep the row's word |
| Node transaction status (`node_status.state` from the app's `GET /api/tx/<hash>`, the words the wallet switches on) | included, executed, proven, finalised, finality not active | the same "finality not active" word for a block under a lock; the node should report `finalised` for the block and `paused` for the network, in two fields |
| Explorer, transaction page (`site/tx.html` 370 to 400) | "Status" = the receipt's success or failure; "Lock state" `final` or `not final` with the reason; "Proof" x of y shards paid; "Including block ... executed under chain block N" | none of the four words as the row's state; `final` where this section says `finalised`; no single state word for the transaction (the reader assembles it from three rows) |
| Explorer, block page (`site/block.html` 386 to 420) | "N transactions executed" chip; "Lock state" `final` or `not final`; "Proof" shards | `final` for `finalised`; "included" is not said of the carried transactions (the page says "carried") |
| Explorer API (`site/api/explorer.mjs`, `lockState`) | `final: true/false` with a reason; shard states | no words; a consumer maps `final` to `finalised` itself |
| Live page (`site/live.html` 423 to 651) | included, excluded, pending (the block's colour), proven, "locked checkpoint", "selected chain" | **executed** and **finalised** are absent: a block under the lock reads "locked checkpoint" only when it is the checkpoint itself; blocks under it have no word |
| Receipt and light pages (`site/receipt.html`, `site/light.html`, `site/lc/app.js`) | "proven" in the verifier's sense ("a receipt proven against the finality certificate", "Proven balance"); "final" through the certificate check | **proven** here means "verified by this page against the certificate", which is this section's **finalised** plus a local check; the word collides with the proof-of-execution sense; the reference-apps lane should say "verified final" (or "finalised, verified here", the wallet's phrase) and keep "proven" for execution proofs |
| Miner app (`app/igneum-app/ui/app.js` 505 to 509, 1398) | "proven" for shard pieces and segments ("0 assigned, 0 proven, 0 paid") | no transaction states, correctly: the app has no transactions; the word is the proving sense |
| Site copy ("Mined by GPUs. Proven by fire.", every page's footer and image alt) | the brand line | not a state word; unchanged |
Rule for the lanes: one vocabulary, four words plus pending and failed, the network state shown separately as `finality active`, `paused` or `recovered`; `final` in the explorer becomes `finalised`; the live page gains `executed` and `finalised` for blocks under the lock; the receipt pages keep `proven` for execution proofs only.
## 10. Test table
Each claim, the scenario, the known-failed line first where there is one, and the result. Simulator rows are `sim/finality_v2.py --scenarios N --seeds 7,11` at the 2/3 floor, each side retargeting and counting only its own blocks, run on build-3 under the lease tool; the tables are in `sim/results_v2.md`, "Rule v4". Where a row says "pending" the run had not landed when this version was written; the 20:00 UK version carries the numbers.
| Claim | Scenario | Known-failed line | Result |
|---|---|---|---|
| (a) The 31-day partition at the window: no conflicting locks under the chosen rule | N1: 50/50, 60/40, 55/45 for 31 days, v3 against v4 pause and v4 recovery | v3: both sides lock alone at day 30.00, 2,679 to 2,870 conflicts (M3, re-run in N1, reproduced to the checkpoint) | **measured**: v4 pause: no side locks in 31 days, 0 conflicts, every pre-heal lock kept, first lock 0 minutes after the heal, 0 post-heal stalls, every split; v4 recovery: 50/50 the same; 60/40 and 55/45 the larger side recovery-locks once at day 30.00 and then locks normally, the smaller side never, 0 conflicts, kept, heal 0 minutes |
| (a) The recovery bound | N1b: 50/50 for 31 days with a 10, 20, 34 percent equivocator, mining on one side and on both | 34 percent: conflicts from minute 0 under every rule (the one-third bound: 87,428 to 87,915 conflicts in 31 days) | **measured** (build-4, 16:06 to 16:16 UK): on one side only, 0 conflicts (one side recovers at day 30.00, the other never); on both sides, both recover at day 30.00 and 2,800 to 2,889 conflicts under v4 recovery, 0 under v4 pause; every pre-heal lock kept, heal 0 minutes |
| (b) 40/40/20 under the active-set rules | N2: honest three-way for 150 minutes and 31 days; 40/40 with a 20 percent equivocator reaching both for 31 days; N2b the same equivocator mining on both sides | the 60/60 case with the equivocator mining on both sides under v4 recovery at day 30 (6.5) | **measured** (N2): honest three-way: no side locks for 150 minutes or 31 days under either v4, 0 conflicts, kept, heal 0 minutes; 60/60 with the equivocator mining on one side: v4 pause never, v4 recovery one side at day 30.00, the other never, 0 conflicts; N2b **measured**: the same equivocator mining on both sides: v4 pause 0 conflicts, v4 recovery both sides at day 30.00 with 2,835 to 2,860 conflicts (the bound of 6.5) |
| (c) Signing stops while mining continues | N3: 34, 40, 45 percent for 24 h under v4 recovery; 34 percent for 31 days under both v4 | none expected (as J) | **measured**: 24 h: every checkpoint stalled (2,880 to 2,889), longest gap 1,440 minutes, first lock 0 minutes after the resume, 0 post-resume stalls, 0 conflicts, 0 recovery locks; 31 days at 34 percent: no lock under either v4 for the whole silence (89,286 to 89,318 stalled), first lock 0 minutes after the resume, 0 conflicts; under v4 recovery that first post-resume lock passed by the majority test (the anchored checkpoint was a window old), so a node would report `recovered` for one window after a resume that follows a pause longer than a window, a reporting fact and not a weakening |
| (d) Old keys compromised against fresh hashrate | N4: keys worth 40 percent withhold, holder at 30 percent of hashrate, 31 days, v2, v3, v4 pause, v4 recovery; the self-strip on day 1 | v4 pause: never (the permanent veto, 6.5) | **measured**: first lock v2 day 20.9, v3 day 30.00, v4 pause never in 31 days (89,262 to 89,373 stalled), v4 recovery day 30.00 (one recovery lock); the holder's share decays to 0.300 under every rule; self-strip: the first lock on day 0 (571 to 736 stalled checkpoints before the evidence is carried), 0 conflicts |
| (e) Finality paused across an epoch boundary: seeds advance, mining continues, certified history intact | derived from N1 and N3: blocks and checkpoints continue through the pause (every stalled checkpoint in the tables is a block the chain mined), 744 hourly boundaries in a 31-day pause, each seeded by its reference block (8); the fast-time harness row (11) | none | derived; harness run owed |
| (f) The heal: a deterministic path that never reverses a lock | N1, N2, N6: every pre-heal lock kept, first lock after the heal, post-heal stalls; the certificate-driven reorg (3.11.7, `c4.mjs`) | none | **measured**: every pre-heal lock kept in every row of N1 and N2 (24 runs), first lock 0 minutes after every heal, 0 post-heal stalls, 0 conflicts under both v4 rules; a sudden departure of 35 percent: v3 and v4 recovery lock at day 30.00, v4 pause never (N6); at 50 percent the recovery is a knife edge (one seed day 30.23, one never) |
| The harness line on real nodes: the known-failed v3 partition past the window | `tools/finality-attacks/v3.mjs split50`, 0.3.25 node pair, 60x file, SPLIT 420 s (the frozen table expires 240 s after the last lock) | both sides lock alone, conflicting certificates at the heal, disagreeing locks | **measured** (build-4, 17:06 to 17:20 UK): both sides locked alone 192 to 198 s after the cut, 7 new locks each; 4/8/8 conflicting certificates logged, 8 disagreeing locked indices after the heal, locking resumed on both forks: FAIL by the scenario's criterion, the known-failed line; the v4 line waits on the node change (6.7) |
| The epoch 20 template fault (today's fault 6) | the node lane's fast-time gate: every epoch boundary a template test; the release-rules record | a header refused for missing state counted as a PoW strike | node lane's regression, outside this lane's harness time (11) |
| The cold-start deadlock (today's fault 7) | the node lane's kept-datadir gate on a chain paused over ten minutes | the sink-age guard on a node holding the chain | node lane's regression (11) |
## 11. Owed
1. The fast-time harness rows still owed: `v3.mjs split50` past the window under v4 (the known-failed v3 line ran on build-4 at 17:06 UK, section 10), a pause across an epoch boundary reading the program id on both sides (row (e)), and the two regressions of 8 October on a node pair carrying the hotfix, each under `lease pool`.
2. The node change of 6.7 on the node line, behind `finality_v4_activation_daa`, with the two unit tests and the known-failed v3 line.
3. The 720-index tally of layer 4 against the simulator (that document's section 11), under the pause and the recovery.
4. The interface changes of 9 by the wallet, explorer, live and reference-apps lanes.
5. Section 3 of the specification: Q5's expiry clause, 3.7 item 9's last sentence ("a partition longer than a window still forks"), 3.11.2's "beyond a window the frozen table is empty" and 3.11.3's departure row to point here (O-3.20).

View file

@ -69,7 +69,7 @@ The Igneum 2.0 plan (p. 16) names six negative tests every validator must pass.
| (3) a wrong chain, epoch or statement binding, replayed or misbound work | `enforced_a_record_signed_for_another_network_pays_nothing` (the chain); `enforced_a_replayed_record_pays_nothing_the_second_time` (replay); `assignment_follows_the_window_and_records_check_against_native_execution` (a wrong block, a wrong shard, a stale record outside the window, a wrong statement); the epoch binds through the sortition's epoch seed and the chain block in the statement | executor | green 16:15 UK |
| (4) a changed payout identity | `enforced_an_altered_payout_address_pays_nothing` (altered after signing: the signature; re-signed: the statement binds the payout) | executor | green 16:15 UK |
| (5) a duplicate proof reward: exactly the permitted payment outcome | `enforced_a_duplicate_of_a_paid_record_by_another_key_pays_nothing`; the honest record paid once in (1)'s test | executor | green 16:15 UK |
| (6) incorrect rewards or consensus inputs: derivation authenticated, not only execution over supplied inputs | `enforced_a_statement_over_altered_rewards_or_payouts_is_vetoed_native_derivation_is_the_check` (a statement whose post-root came from execution over other rewards or payouts is not the native statement and pays nothing: the native veto, every node's own derivation) | executor, branch proving-payment of the fork at 421bb852 (on release-2.0.0-node's c04674fe), igneum-exec 67 passed on build-2 at 17:10 UK | PENDING as the plan means it: the derivation is authenticated by every node's own execution, not inside the proof; the proof-side closure is P22's stages 1 to 3 (section 7), phase 2 |
| (6) incorrect rewards or consensus inputs: derivation authenticated, not only execution over supplied inputs | `enforced_a_statement_over_altered_rewards_or_payouts_is_vetoed_native_derivation_is_the_check` (a statement whose post-root came from execution over other rewards or payouts is not the native statement and pays nothing: the native veto, every node's own derivation) | executor, branch proving-payment of the fork at 421bb852 (on release-2.0.0-node's c04674fe), igneum-exec 67 passed on build-2 at 17:10 UK | team-tested for what the test holds (the derivation authenticated by every node's own execution: a statement over other rewards or payouts pays nothing); PENDING for the proof side (the derivation inside the aggregator guest, P22's stages 1 to 3, section 7, phase 2), the served label until stage 3 |
The boundary sentence of the plan, carried in every served text that names the rule: a proof of execution is not a proof of authenticated consensus inputs, canonical history or data availability. What the rule proves today is that the carried record's statement is the native statement of this node's own execution and that an SP1 proof of that statement verifies; what consensus inputs the execution used, which history is canonical and whether the data is available are each the node's own reading, not the proof's.

View file

@ -95,6 +95,13 @@ class P:
self.frozen = False # True: rule v3 (4 Oct 2026, ledger F21): a lock also needs 2/3 of the weight table FROZEN at the view's last
# certified checkpoint, signers counted at their frozen weights; the frozen table expires one window (30 days)
# after its checkpoint, after which the sliding table alone applies (as today)
self.anchored = False # True: rule v4 (8 Oct 2026, the finality boundary lane): the frozen table is ANCHORED, never expiring by
# time; it is replaced only by a certificate that passes it. Without P.recovery this is the indefinite pause.
self.att_both = False # True: an equivocating key mines on EVERY side of a partition (its hashrate split by the draw), so it stays above
# dust in every side's sliding table and remains a voter on both; False: it mines on the first side only (as H, I, M5)
self.recovery = False # True (with anchored): the majority-continuity recovery: once the anchored table's checkpoint is one window
# old with no certificate since, a certificate locks when its signers hold 2/3 of the sliding table (Q3) AND
# MORE THAN HALF of the anchored table at its weights; the lock re-anchors the table. Recorded as a recovery lock.
self.__dict__.update(kw)
def label(self):
@ -109,6 +116,10 @@ class P:
s += "+local"
if self.frozen:
s += "+frozen"
if self.anchored:
s += "+anchored"
if self.recovery:
s += "+recovery"
return s
@ -130,6 +141,7 @@ class View:
self.excl = np.zeros(n) # blocks mined off this side since the split, unseen by it (only used with P.local)
self.frozen_wt = None # rule v3: the weight table at this view's last certified checkpoint (None before the first)
self.frozen_slot = -1
self.recovery_locks = [] # rule v4 with recovery: (idx, slot) of every lock that passed by the majority-continuity test
def clone_ring_from(self, other):
self.ring[:] = other.ring
@ -163,6 +175,7 @@ class Sim:
self.next_sid = 1
self.records = [] # (slot, idx, sid, latency_s or -1, signed/denom, denom/total)
self.conflicts = [] # (idx, slot the second certificate existed)
self.recoveries = [] # (idx, slot, sid) of every recovery lock (rule v4 with recovery)
self.q_on = 1.0 / p.outage
self.q_off = np.zeros(0)
@ -219,6 +232,8 @@ class Sim:
def _set_masks(self, v):
v.key_mask = np.isin(self.region, v.regions)
v.mine_mask = v.key_mask.copy()
if self.p.att_both:
v.mine_mask |= self.equiv
# ------------------------------------------------------------- partitions
def split(self, groups):
@ -255,6 +270,8 @@ class Sim:
newest = max(views, key=lambda v: v.frozen_slot)
m.frozen_wt = None if newest.frozen_wt is None else newest.frozen_wt.copy()
m.frozen_slot = newest.frozen_slot
for v in views:
m.recovery_locks.extend(v.recovery_locks)
# certificates and conflicts
for v in views:
for idx, (sid, slot, lat) in v.certs.items():
@ -288,6 +305,8 @@ class Sim:
side_tot = self.hash[v.mine_mask].sum()
if side_tot > 0:
blocks[v.mine_mask] = self.rng.poisson(BLOCKS_PER_CP * self.hash[v.mine_mask] / side_tot)
# att_both: the equivocator's draw lands once (the last side's), seen by every side it mines on; its sliding weight is
# therefore one side's draw, about its full share, which overstates it there but is immaterial to the anchored test
else:
blocks = self.rng.poisson(BLOCKS_PER_CP * self.hash / tot) if tot > 0 else np.zeros(self.N)
hp = (slot // SLOTS_PER_HOUR) % WINDOW_HOURS
@ -352,13 +371,22 @@ class Sim:
ratio = signed_w / denom if denom > 0 else 0.0
# rule v3 (frozen): signers also need 2/3 of the table frozen at the view's last certified checkpoint, counted at
# their frozen weights, while that checkpoint is less than one window old
wf, need_f = None, 0.0
if p.frozen and v.frozen_wt is not None and (self.slot - v.frozen_slot) < WINDOW_HOURS * SLOTS_PER_HOUR:
felig = (v.frozen_wt >= p.dust) & ~self.stripped
total_f = float(v.frozen_wt[felig].sum())
if total_f > 0:
wf = np.where(felig, v.frozen_wt, 0.0)[vi]
need_f = p.quorum * total_f
wf, need_f, rec = None, 0.0, False
if (p.frozen or p.anchored) and v.frozen_wt is not None:
age = self.slot - v.frozen_slot
expired = age >= WINDOW_HOURS * SLOTS_PER_HOUR
# v3: the table expires one window after its checkpoint. v4: it never expires; after a window with no certificate the
# majority-continuity test (more than half of the anchored table) applies when P.recovery, else two thirds stands forever.
if p.anchored or not expired:
felig = (v.frozen_wt >= p.dust) & ~self.stripped
total_f = float(v.frozen_wt[felig].sum())
if total_f > 0:
wf = np.where(felig, v.frozen_wt, 0.0)[vi]
if p.anchored and p.recovery and expired:
need_f = 0.5 * total_f + 1e-9 # strictly more than half: two such certificates share a signer
rec = True
else:
need_f = p.quorum * total_f
best = None
if denom > 0 and vi.size > 0:
w = wt[vi]
@ -384,9 +412,12 @@ class Sim:
mask = np.zeros(self.N, dtype=np.int8)
mask[vi[arr <= seal]] = 1
v.certs[idx] = (v.sid, self.slot, lat)
if p.frozen:
if p.frozen or p.anchored:
v.frozen_wt = wt.copy()
v.frozen_slot = self.slot
if rec:
v.recovery_locks.append((idx, self.slot))
self.recoveries.append((idx, self.slot, v.sid))
self._push(v, idx, mask)
self.records.append((self.slot, idx, v.sid, lat, ratio, denom / total if total > 0 else 0.0))
else:
@ -1124,13 +1155,14 @@ def run_partition2(seed, fracs, att_share, dur_min, p, pre_min=60, post_min=180)
for v in sim.views:
for idx, c in v.certs.items():
before.setdefault(idx, set()).add(c[0])
side_rec = [len(v.recovery_locks) for v in sim.views]
sim.heal()
merged = sim.views[0].certs
kept = all(idx in merged for idx in before)
sim.run(post_min * 2)
recs = sim.recs()
res = dict(conflicts=len(sim.conflicts), kept=kept, pre_locks=len(before),
att_share=sim.share([att]) if att is not None else 0.0)
res = dict(conflicts=len(sim.conflicts), kept=kept, pre_locks=len(before), side_recovery=side_rec,
recoveries=len(sim.recoveries), att_share=sim.share([att]) if att is not None else 0.0)
res["first_conflict_min"] = (min(c[1] for c in sim.conflicts) - t_split) / 2.0 if sim.conflicts else None
res["side_first_lock"] = []
res["side_locks"] = []
@ -1214,7 +1246,7 @@ def run_silent_resume(seed, frac, hours, p, pre_h=3, post_h=3):
fr = first_lock_after(recs, t1)
return dict(got=got, stalls=stalls_between(recs, t0, t1), first_lock=None if fl is None or fl >= t1 else (fl - t0) / 2.0,
gap=lock_gaps_min(recs, t0, t1), resume=None if fr is None else (fr - t1) / 2.0,
post_stalls=stalls_between(recs, t1, sim.slot), conflicts=len(sim.conflicts),
post_stalls=stalls_between(recs, t1, sim.slot), conflicts=len(sim.conflicts), recoveries=len(sim.recoveries),
locked_share=float(((recs[:, 0] >= t0) & (recs[:, 0] < t1) & (recs[:, 3] >= 0)).sum()) / max(1, int(((recs[:, 0] >= t0) & (recs[:, 0] < t1)).sum())))
@ -1242,9 +1274,10 @@ def scenario_j(args):
return "\n".join(out)
def run_acquired(seed, bought, att_hash, signs, days, p):
def run_acquired(seed, bought, att_hash, signs, days, p, strip_day=None):
"""An attacker buys keys holding `bought` of window weight and starts mining at `att_hash` of network hashrate.
The sellers keep their rigs and mine on under fresh keys."""
The sellers keep their rigs and mine on under fresh keys. strip_day: the keys were COMPROMISED, not sold, and their
owners strip them by self-equivocation (3.6) on that day (the evidence lands in the chain and the keys leave every table)."""
sim, rng, _ = build_honest(p, seed)
# the bought set is picked by hashrate, which the warm start turns into weight at the same share
mask, got = pick_weight_subset(rng, sim.hash, bought)
@ -1271,6 +1304,8 @@ def run_acquired(seed, bought, att_hash, signs, days, p):
last13 = None
for day in range(1, days + 1):
s0 = sim.slot
if strip_day is not None and day == strip_day:
sim.stripped[bidx] = True
sim.run(SLOTS_PER_DAY)
sh = sim.share(owned)
recs = sim.recs()
@ -1280,8 +1315,10 @@ def run_acquired(seed, bought, att_hash, signs, days, p):
if above13 is None:
above13 = day
recs = sim.recs()
fl = first_lock_after(recs, t0)
return dict(got=got, series=series, above13=above13, last13=last13, stalls=stalls_between(recs, t0, sim.slot),
max_share=max(s for _, s, _ in series), end_share=series[-1][1], conflicts=len(sim.conflicts))
max_share=max(s for _, s, _ in series), end_share=series[-1][1], conflicts=len(sim.conflicts),
recoveries=len(sim.recoveries), first_lock_days=None if fl is None else (fl - t0) / float(SLOTS_PER_DAY))
def scenario_k(args):
@ -1357,7 +1394,7 @@ def run_churn(seed, frac, days, p):
st = stalls_between(recs, t_event, sim.slot)
later = stalls_between(recs, fl, sim.slot) if fl is not None else 0
return dict(got=got, first_lock_days=None if fl is None else (fl - t_event) / float(SLOTS_PER_DAY), stalls=st, later=later,
live_share=float(sim.share(np.flatnonzero(~gone))), conflicts=len(sim.conflicts))
live_share=float(sim.share(np.flatnonzero(~gone))), conflicts=len(sim.conflicts), recoveries=len(sim.recoveries))
def scenario_l(args):
@ -1540,8 +1577,194 @@ def scenario_m(args):
return "\n".join(out)
def rule_v4(delay, **kw):
"""Rule v4 (8 Oct 2026, the finality boundary lane): v3 with the frozen table anchored (never expiring by time)."""
base = dict(denom="active", pmode="cert", floor=FLOOR_F, delay=delay, daa="full", local=True, frozen=True, anchored=True)
base.update(kw)
return P(**base)
def rule_v4r(delay, **kw):
"""Rule v4 with the majority-continuity recovery."""
return rule_v4(delay, recovery=True, **kw)
RULES_N = (("v3", rule_v3), ("v4 pause", rule_v4), ("v4 recovery", rule_v4r))
def _days(xs):
xs = list(xs)
return "never" if all(x is None for x in xs) else span((x for x in xs if x is not None), "%.2f")
def _part_rows(seeds, delay, name, fr, att, dur_min, rules=RULES_N, post_min=180, **pkw):
rows = []
for lab, mk in rules:
rs = [run_partition2(sd, fr, att, dur_min, mk(delay, **pkw), pre_min=60, post_min=post_min) for sd in seeds]
n = len(fr)
fl = [[None if r["side_first_lock"][i] is None else r["side_first_lock"][i] / 1440.0 for r in rs] for i in range(n)]
rows.append([name, pct(att, 0) if att else "none", dur_min / 1440.0 if dur_min >= 1440 else "%d min" % dur_min, lab,
" / ".join(_days(col) for col in fl),
" / ".join(span([r["side_recovery"][i] for r in rs]) for i in range(n)),
span(r["conflicts"] for r in rs),
"never" if all(r["first_conflict_min"] is None for r in rs) else span((r["first_conflict_min"] / 1440.0 for r in rs if r["first_conflict_min"] is not None), "%.2f") + " d",
"yes" if all(r["kept"] for r in rs) else "NO", span_min(r["post_first_lock_min"] for r in rs), span(r["post_stalls"] for r in rs)])
return rows
PART_HDR = ["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"]
def scenario_n1(args):
"""(a) The 31-day partition at the frozen table's expiry: v3 is the documented known-failed case (results M3); v4 pause and v4 recovery."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### 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 %s, %d-day partitions, each side retargets and counts only its own blocks" % (",".join(str(s) for s in seeds), days), ""]
out.append("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.")
out.append("")
rows = []
for name, fr in (("50/50", [0.5, 0.5]), ("60/40", [0.6, 0.4]), ("55/45", [0.55, 0.45])):
rows += _part_rows(seeds, args.delay, name, fr, 0.0, days * 1440)
out.append(md_table(PART_HDR, rows))
return "\n".join(out)
def scenario_n1b(args):
"""(a, continued) The same 31-day 50/50 split with an equivocator: the recovery bound (any equivocator above the split's imbalance)."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### N1b. The 31-day 50/50 split with an equivocator holding a of total, v4 pause against v4 recovery; seeds %s" % ",".join(str(s) for s in seeds), ""]
out.append("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).")
out.append("")
out.append("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.")
out.append("")
rows = []
for a in (0.10, 0.20, 0.34):
rows += _part_rows(seeds, args.delay, "50/50, equivocator on side 1 only", [0.5, 0.5], a, days * 1440, rules=RULES_N[1:])
rows += _part_rows(seeds, args.delay, "50/50, equivocator mining on both sides", [0.5, 0.5], a, days * 1440, rules=RULES_N[1:], att_both=True)
out.append(md_table(PART_HDR, rows))
return "\n".join(out)
def scenario_n2b(args):
"""(b, continued) 40/40 with a 20 percent equivocator mining on both sides."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### N2b. The 40/40 split with a 20%% equivocator reaching and MINING on both sides (60/60 of the anchored table), %d days; seeds %s" % (days, ",".join(str(s) for s in seeds)), ""]
rows = []
rows += _part_rows(seeds, args.delay, "40/40 + 20% equivocator on both sides", [0.4, 0.4], 0.20, days * 1440, rules=RULES_N[1:], att_both=True)
out.append(md_table(PART_HDR, rows))
return "\n".join(out)
def scenario_n2(args):
"""(b) The 40/40/20 split under the active-set rules as implemented (Q2 block reading approximated by cert, Q3, Q5 anchored)."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### 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 %d days; seeds %s" % (days, ",".join(str(s) for s in seeds)), ""]
out.append("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.")
out.append("")
rows = []
rows += _part_rows(seeds, args.delay, "40/40/20 honest", [0.4, 0.4, 0.2], 0.0, 150, rules=RULES_N[2:])
rows += _part_rows(seeds, args.delay, "40/40/20 honest", [0.4, 0.4, 0.2], 0.0, days * 1440, rules=RULES_N[1:])
rows += _part_rows(seeds, args.delay, "40/40 + 20% equivocator on both (60/60)", [0.4, 0.4], 0.20, days * 1440, rules=RULES_N[1:])
out.append(md_table(PART_HDR, rows))
return "\n".join(out)
def scenario_n3(args):
"""(c) Signing stops while mining continues: 24 h at 34, 40, 45 percent under v4 recovery, and 31 days at 34 percent under both v4 rules."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
out = ["### N3. Signing stops while mining continues (as J), under rule v4; seeds %s" % ",".join(str(s) for s in seeds), ""]
out.append("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.")
out.append("")
rows = []
for frac in (0.34, 0.40, 0.45):
h = 1 if q else 24
rs = [run_silent_resume(sd, frac, h, rule_v4r(args.delay)) for sd in seeds]
rows.append([pct(frac, 0), "v4 recovery", h, span_min(r["first_lock"] for r in rs), span(r["stalls"] for r in rs),
span((r["gap"] for r in rs), "%.0f"), span_min(r["resume"] for r in rs), span(r["post_stalls"] for r in rs),
span(r["recoveries"] for r in rs), span(r["conflicts"] for r in rs)])
for lab, mk in RULES_N[1:]:
h = 2 if q else 31 * 24
rs = [run_silent_resume(sd, 0.34, h, mk(args.delay), pre_h=1, post_h=1) for sd in seeds]
rows.append([pct(0.34, 0), lab, h, span_min(r["first_lock"] for r in rs), span(r["stalls"] for r in rs),
span((r["gap"] for r in rs), "%.0f"), span_min(r["resume"] for r in rs), span(r["post_stalls"] for r in rs),
span(r["recoveries"] for r in rs), span(r["conflicts"] for r in rs)])
out.append(md_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"], rows))
return "\n".join(out)
def scenario_n4(args):
"""(d) Old voting keys compromised against fresh hashrate: bought or stolen keys worth 40 percent withhold; the self-strip."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### 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, %d days; seeds %s" % (days, ",".join(str(s) for s in seeds)), ""]
out.append("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.")
out.append("")
rows = []
for lab, mk in (("v2", rule_p),) + RULES_N:
rs = [run_acquired(sd, 0.40, 0.30, False, days, mk(args.delay)) for sd in seeds]
rows.append([lab, "withhold", _days(r["first_lock_days"] for r in rs), span(r["stalls"] for r in rs), span(r["recoveries"] for r in rs),
span((r["end_share"] for r in rs), "%.3f"), span(r["conflicts"] for r in rs)])
rs = [run_acquired(sd, 0.40, 0.30, False, days, rule_v4(args.delay), strip_day=1) for sd in seeds]
rows.append(["v4 pause", "withhold, owners self-strip on day 1", _days(r["first_lock_days"] for r in rs), span(r["stalls"] for r in rs),
span(r["recoveries"] for r in rs), span((r["end_share"] for r in rs), "%.3f"), span(r["conflicts"] for r in rs)])
out.append(md_table(["rule", "the keys", "first lock after the purchase, day", "stalled checkpoints", "recovery locks", "holder's share at the end", "conflicting locks"], rows))
return "\n".join(out)
def scenario_n6(args):
"""(f) Churn: a set stops mining and signing at once (the sudden honest departure); and the heal rows of N1 carry the deterministic recovery path."""
seeds = seeds_of(args)[:2]
q = getattr(args, "quick", False)
days = 2 if q else 31
out = ["### N6. A set holding x of weight stops mining and signing at once (as M4), %d days; seeds %s" % (days, ",".join(str(s) for s in seeds)), ""]
out.append("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.")
out.append("")
rows = []
for frac in (0.35, 0.50):
for lab, mk in RULES_N:
rs = [run_churn(sd, frac, days, mk(args.delay)) for sd in seeds]
rows.append([pct(frac, 0), lab, days, _days(r["first_lock_days"] for r in rs) + ("" if any(r["first_lock_days"] is not None for r in rs) else " in %d days" % days),
span(r["stalls"] for r in rs), span(r["recoveries"] for r in rs), span(r["conflicts"] for r in rs)])
out.append(md_table(["departed weight", "rule", "days run", "first lock after the event, day", "stalled checkpoints", "recovery locks", "conflicting locks"], rows))
return "\n".join(out)
def scenario_n(args):
return "\n\n".join(f(args) for f in (scenario_n1, scenario_n1b, scenario_n2, scenario_n2b, scenario_n3, scenario_n4, scenario_n6))
SCENARIOS = {"A": scenario_a, "B": scenario_b, "C": scenario_c, "D": scenario_d, "E": scenario_e, "F": scenario_f, "G": scenario_g,
"H": scenario_h, "I": scenario_i, "J": scenario_j, "K": scenario_k, "L": scenario_l, "M": scenario_m}
"H": scenario_h, "I": scenario_i, "J": scenario_j, "K": scenario_k, "L": scenario_l, "M": scenario_m,
"N": scenario_n, "N1": scenario_n1, "N1B": scenario_n1b, "N2": scenario_n2, "N2B": scenario_n2b, "N3": scenario_n3, "N4": scenario_n4, "N6": scenario_n6}
def main(argv=None):

View file

@ -678,3 +678,127 @@ M5. The equivocator across a 50/50 split (as H), v3: each side holds (1 - a)/2 +
| 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 `recovered` for 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

View file

@ -10,6 +10,7 @@
| the red watcher fires on cancelled and timed-out runs too (`ci-red.yml`, `red-watch.mjs`) | The watcher's `if` missing any of failure, cancelled, timed_out, or the conclusion not handed to the record step (the self-test reads the workflow file); the line names the kind: CI red, CI cancelled, CI timed out. | 7 October 2026 |
| gh's active account is the stored Igneum entry (`gh-account-check.sh`, in Igneum's own gh directory `~/.config/gh-igneum` through `gh-env.sh`, never the founder's) | A push or a landing from this Mac while Igneum's gh directory names any other account as active, or none (the refusal names the one step: the founder or main stores the Igneum token there with `GH_CONFIG_DIR=~/.config/gh-igneum gh auth login --with-token`; no lane does); skipped with a line while `github-suspended` stands. RULE: no lane switches gh accounts on this Mac, ever; the second owner's login belongs to other projects and must never touch Igneum; the stored entry's name is in ~/.config/igneum/gh-user, never in the repository. | 7 October 2026, 21:41 UK: a lane switched gh to the other login during the suspension; nobody could say which |
| rule 24: a landing that touches a .rs, Cargo.toml, Cargo.lock, build.rs or .cargo/config checks and tests its crates on the box first (`rule24-crate-gate.sh`, called by `merge-to-master.sh` before the merge and by the hook on a push of master or release-*) | A touched crate that does not `cargo check` or whose suite is red on the box at gate priority; a file under no crate (the root .cargo/config) runs the core pair igneum-pow and app/igneum-app; a vendor crate is named and left to its lane; docs/ and site/ alone (docs-only-check.sh) run nothing; any other diff takes the full gate alone. Class: master's igneum-pow stopped compiling at 17:07 UK under landings that never built it | 8 Oct 2026 |
| no landing on the public host while the marker stands (`pre-push.sh` `forgejo_master_frozen`, `merge-to-master.sh` `forgejo_master_refusal`); the hook binds every remote rule to the remote's URL | A push of master to git.igneum.network, or a `--remote` naming it, while `tools/ci/github-suspended` stands: its master is a rewritten copy replaced at cut-over, so the landing would be lost (a branch pushed there for safekeeping passes). Before the fix the hook matched the remote NAME, so `git push origin master` bound neither the GitHub refusal nor the CI rule; the self-test now drives the hook by name through a fixture repo. Also: `gate-manifest-check.sh` and four other pipefail checks no longer pipe a file-sized producer into `grep -q` (GNU sed took SIGPIPE on an early match and the check read it as a missing run line on the Linux runners and boxes); `mirror_master` fast-forwards every box with a build-server file after a landing on ANY remote (before, only a GitHub landing fanned out, so a box landing left build-3 and build-4 at a tip 23 hours old), as a `--no-verify` copy of the master the gate already passed (a stale mirror had re-run the full gate for six minutes per box) | 8 Oct 2026 |
| kill by exact command or pid file (owed as a check) | 6 October 2026, 21:09Z: a Mac-side `pkill -f <log file name>` matched nothing (the log name was a redirect, not part of the command line), the roll-everything script lived on and wiped a box it had been told to hold. Rule: a job is stopped by its pid file (`tools/fleet/fleet-bg.sh start|stop <name>`) or by a pattern anchored on its exact command line (`^python3 -u /root/fleet/in/box-prover.py`), never by a word that may or may not appear in it. The check that flags a `pkill -f`/`pgrep -f` whose literal is a path or a name that never starts a command line is owed to the CI lane |

View file

@ -67,6 +67,7 @@ income per tier: the public table equals its inputs, the schedule arithmetic
hash-origin report: a known-finished day and a known-failed day
harness summaries never carry a raw 64-hex key (the writer's own redaction and check)
docs-only pushes skip the compile-or-compute CI jobs (the changes job's classifier)
rule 24: a landing that touches a .rs, Cargo.toml, build.rs or .cargo file checks and tests its crates on the box first; docs and site alone skip it (self-test)
the public ledger (docs/ledger-public.md) is what docs/fud-ledger.md generates: one row per item, no commit ids, times or team names (self-test first)
the ledger page reads both entry heading forms (M1 and AP-F8-1) so no in-house pass row is dropped from /ledger (known-failed first)
every workflow job carries timeout-minutes (site 15, changes 10, pow 60, sims 45; the hung-job class of 7 October 2026)

View file

@ -29,6 +29,7 @@ docs/analysis/class-v6
# 8 October 2026: the miner window audit (an internal design record: the founder's order, lane ids, the build plan)
docs/design/app-audit-2026-10-08.md
docs/analysis/class-v6/coexistence-model.md
docs/analysis/class-v6/operator-simulation.md
# 8 October 2026: the public shape of the pre-2.0 ledger, kept as history at the Igneum 2.0 reset (not served, not linked;
# docs/ledger-public.md is now generated from docs/fud-ledger-2.0.md)
docs/ledger-public-pre-2.0.md

View file

@ -203,6 +203,10 @@ else
echo "merge-to-master: the remote $REMOTE is not GitHub (a mirror); the box gate stamp on ${SHA:0:8} is the verdict${IGNEUM_MASTER_EXCEPTION:+ (exception: $IGNEUM_MASTER_EXCEPTION)}"
VERDICT="gate: green on ${SHA:0:8}, recorded by tools/ci/pre-push.sh; landed on the $REMOTE mirror${IGNEUM_MASTER_EXCEPTION:+ under the exception declared by main: $IGNEUM_MASTER_EXCEPTION}"
fi
# rule 24 (8 October 2026, 17:2x UK): a diff that touches a .rs, Cargo.toml, Cargo.lock, build.rs or .cargo/config runs cargo check
# and the crate suite on the box at gate priority for every crate it touches before the merge; docs/ and site/ alone skip it
BASE=$(git merge-base "$SHA" "$REMOTE/master" 2>/dev/null || git rev-parse "$REMOTE/master")
bash tools/ci/rule24-crate-gate.sh "$BASE" "$SHA" || { echo "merge-to-master: REFUSED by rule 24: a touched crate does not check or its suite is red (above); fix on the branch and retry" >&2; exit 1; }
for i in $(seq 1 "$TRIES"); do
git fetch -q "$REMOTE" master; TIP=$(git rev-parse "$REMOTE/master")
if git merge-base --is-ancestor "$SHA" "$TIP"; then echo "merge-to-master: ${SHA:0:8} is already on $REMOTE/master $(git log -1 --format=%h "$REMOTE/master")"; exit 0; fi

View file

@ -166,6 +166,7 @@ tree_checks() {
run "hash-origin report: a known-finished day and a known-failed day" node --test tools/observer/hash-origin.test.mjs
run "harness summaries never carry a raw 64-hex key (the writer's own redaction and check)" node infra/fast-time/lib/redact-keys.mjs --self-test
run "docs-only pushes skip the compile-or-compute CI jobs (the changes job's classifier)" bash tools/ci/docs-only-check.sh --self-test
run "rule 24: a landing that touches a .rs, Cargo.toml, build.rs or .cargo file checks and tests its crates on the box first; docs and site alone skip it (self-test)" bash tools/ci/rule24-crate-gate.sh --self-test
run "the public ledger (docs/ledger-public.md) is what docs/fud-ledger.md generates: one row per item, no commit ids, times or team names (self-test first)" bash -c 'node tools/ledger/export-public.mjs --self-test && node tools/ledger/export-public.mjs --check'
run "the ledger page reads both entry heading forms (M1 and AP-F8-1) so no in-house pass row is dropped from /ledger (known-failed first)" node tools/ledger-page.mjs --self-test
run "every workflow job carries timeout-minutes (site 15, changes 10, pow 60, sims 45; the hung-job class of 7 October 2026)" bash tools/ci/workflow-timeouts-check.sh --self-test
@ -398,6 +399,14 @@ FAKEGH
defer*) echo "pre-push gate: a merge of green-stamped ${verdict#defer } onto the remote tip: the light gate here, the full gate in CI on landing:"
structural_checks; never_push_checks; finish "merge of a green branch (full gate deferred to CI)" ;;
*) echo "pre-push gate: a push to master or release-*, the full gate (the same checks CI runs)${verdict:+ ($verdict)}:"
# rule 24: the crates the push touches check and pass their suites on the box before the push (merge-to-master.sh runs the
# same before its merge; here for a direct push of master or a release line)
while read -r lref lsha rref rsha; do
case "$rref" in refs/heads/master|refs/heads/release-*) ;; *) continue ;; esac
[ "$lsha" != 0000000000000000000000000000000000000000 ] && [ "$rsha" != 0000000000000000000000000000000000000000 ] || continue
git cat-file -e "$rsha" 2>/dev/null || continue
bash "$GATE_ROOT/tools/ci/rule24-crate-gate.sh" "$rsha" "$lsha" || { echo "pre-push gate: REFUSED by rule 24: a touched crate does not check or its suite is red (above)" >&2; exit 1; }
done <<<"$REFS"
STAMP=1; structural_checks; tree_checks; finish "push to master or release-*" ;;
esac
else

102
tools/ci/rule24-crate-gate.sh Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env bash
# Rule 24 (main through the coordinator, 8 October 2026, 17:2x UK): a landing whose diff touches a .rs file, a Cargo.toml, a
# Cargo.lock, a build.rs or a .cargo/config runs `cargo check` and the crate's suite ON THE BOX (tools/build-remote.sh at gate
# priority) for every crate it touches, before the merge; the documents-only gate (docs-only-check.sh: every path under docs/ or
# site/) is the only diff that skips it; any other diff (tools, infra, workflows) takes the full gate alone, as before.
# The class behind it: master's igneum-pow stopped compiling at 17:07 UK under landings that never built it.
#
# tools/ci/rule24-crate-gate.sh <base> <head> [--dry] the crates the diff base..head touches, then check + test each on the box
# (--dry: print the plan, run nothing); exit 0 green or nothing to run,
# 1 on a red crate, 2 on a bad argument
# tools/ci/rule24-crate-gate.sh --self-test
# A file maps to the nearest ancestor directory holding a Cargo.toml with [package]; a file under a workspace root with no nearer
# package maps to that root (cargo runs the workspace). A crate under vendor/ is a fork crate with its own lane: named, never run
# here. RULE24_RUNNER swaps the runner (the self-test records calls); it receives <crate dir> <check|test>.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd -P)"; ME="$HERE/$(basename "$0")" # absolute: the self-test calls it from a fixture directory
code_file() { case "$1" in *.rs|*/Cargo.toml|Cargo.toml|*/Cargo.lock|Cargo.lock|*/build.rs|build.rs|*/.cargo/config|*/.cargo/config.toml|.cargo/config|.cargo/config.toml) return 0 ;; esac; return 1; }
crate_of() { # <path relative to the repo root> -> the crate dir ("." for the root) or "" when none
local d; d=$(dirname "$1"); local ws=""
while :; do
if [ -f "$d/Cargo.toml" ]; then
if grep -q '^\[package\]' "$d/Cargo.toml"; then printf '%s' "$d"; return; fi
[ -n "$ws" ] || grep -q '^\[workspace\]' "$d/Cargo.toml" && ws="${ws:-$d}"
fi
[ "$d" = . ] && break; d=$(dirname "$d")
done
printf '%s' "$ws"
}
plan() { # <base> <head> -> lines "crate <dir>" / "vendor <dir>" / "documents-only" / "no-crate"
local base="$1" head="$2" files f c seen="" any_code=0
files=$(git diff --name-only "$base" "$head" --) || { echo "rule24: cannot diff $base..$head" >&2; return 2; }
if [ -z "$files" ]; then echo "no-crate"; return 0; fi
if [ "$(printf '%s\n' "$files" | bash "$HERE/docs-only-check.sh")" = "code=false" ]; then echo "documents-only"; return 0; fi
while IFS= read -r f; do
[ -n "$f" ] || continue; code_file "$f" || continue; any_code=1
c=$(crate_of "$f")
if [ -z "$c" ]; then # a code-class file under no crate (the root .cargo/config): the core pair every cut pairs with
for c in ${RULE24_CORE_CRATES:-igneum-pow app/igneum-app}; do case " $seen " in *" $c "*) continue ;; esac; seen="$seen $c"; echo "crate $c"; done; continue
fi
case " $seen " in *" $c "*) continue ;; esac; seen="$seen $c"
case "$c" in vendor/*) echo "vendor $c" ;; *) echo "crate $c" ;; esac
done <<<"$files"
[ "$any_code" = 1 ] || echo "no-crate"
}
run_crate() { # <dir> <check|test>
local dir="$1" what="$2"
if [ -n "${RULE24_RUNNER:-}" ]; then "$RULE24_RUNNER" "$dir" "$what"; return; fi
case "$what" in
check) ( cd "$dir" && IGNEUM_AGENT="${IGNEUM_AGENT:-rule24}" bash "$HERE/../build-remote.sh" --no-fetch --priority gate -- check ) ;;
test) ( cd "$dir" && IGNEUM_AGENT="${IGNEUM_AGENT:-rule24}" bash "$HERE/../build-remote.sh" --no-fetch --priority gate -- test --release ) ;;
esac
}
if [ "${1:-}" = --self-test ]; then
d=$(mktemp -d); trap 'rm -rf "$d"' EXIT; fails=0; calls="$d/calls"; : > "$calls"
runner="$d/runner.sh"; printf '#!/usr/bin/env bash\necho "$1 $2" >> %q\n[ "$1" = bad ] && [ "$2" = test ] && exit 1\nexit 0\n' "$calls" > "$runner"; chmod +x "$runner"
( cd "$d" && git init -q -b master . && mkdir -p a/src bad/src docs site tools/ci vendor/f/src ws/m/src .cargo
printf '[package]\nname = "a"\nversion = "0.1.0"\n' > a/Cargo.toml; : > a/src/lib.rs
printf '[package]\nname = "bad"\nversion = "0.1.0"\n' > bad/Cargo.toml; : > bad/src/lib.rs
printf '[workspace]\nmembers = ["m"]\n' > ws/Cargo.toml; printf '[package]\nname = "m"\nversion = "0.1.0"\n' > ws/m/Cargo.toml; : > ws/m/src/lib.rs
printf '[package]\nname = "f"\nversion = "0.1.0"\n' > vendor/f/Cargo.toml; : > vendor/f/src/lib.rs
echo x > docs/x.md; echo y > site/y.html; echo z > tools/ci/z.sh; printf '[build]\n' > .cargo/config.toml
git add -A && git -c user.name=t -c user.email=t@t commit -q -m base && git tag base
echo 1 >> a/src/lib.rs && echo d >> docs/x.md && git -c user.name=t -c user.email=t@t commit -qam "a and a doc" && git tag t1
echo d >> docs/x.md && echo s >> site/y.html && git -c user.name=t -c user.email=t@t commit -qam "docs and site" && git tag t2
echo t >> tools/ci/z.sh && git -c user.name=t -c user.email=t@t commit -qam "a tool" && git tag t3
echo 1 >> vendor/f/src/lib.rs && echo 1 >> bad/src/lib.rs && git -c user.name=t -c user.email=t@t commit -qam "a fork crate and a bad crate" && git tag t4
printf 'x = 1\n' >> ws/Cargo.toml && git -c user.name=t -c user.email=t@t commit -qam "a workspace root file" && git tag t5
printf '[build]\njobs = 2\n' > .cargo/config.toml && git -c user.name=t -c user.email=t@t commit -qam "cargo config" && git tag t6 ) >/dev/null 2>&1
cd "$d"
[ "$(bash "$ME" base t1 --dry)" = "crate a" ] || { echo "self-test failed: a .rs change beside a doc did not name its crate: $(bash "$ME" base t1 --dry)"; fails=1; }
[ "$(bash "$ME" t1 t2 --dry)" = "documents-only" ] || { echo "self-test failed: docs and site alone were not documents-only: $(bash "$ME" t1 t2 --dry)"; fails=1; }
[ "$(bash "$ME" t2 t3 --dry)" = "no-crate" ] || { echo "self-test failed: a tools change was read as a crate: $(bash "$ME" t2 t3 --dry)"; fails=1; }
[ "$(bash "$ME" t3 t4 --dry | sort | tr '\n' ';')" = "crate bad;vendor vendor/f;" ] || { echo "self-test failed: the fork crate was not set aside or the bad crate not named: $(bash "$ME" t3 t4 --dry | tr '\n' ';')"; fails=1; }
[ "$(bash "$ME" t4 t5 --dry)" = "crate ws" ] || { echo "self-test failed: a workspace root file did not map to the workspace: $(bash "$ME" t4 t5 --dry)"; fails=1; }
[ "$(bash "$ME" t5 t6 --dry | tr '\n' ';')" = "crate igneum-pow;crate app/igneum-app;" ] || { echo "self-test failed: a root .cargo/config change did not map to the core pair: $(bash "$ME" t5 t6 --dry | tr '\n' ';')"; fails=1; }
: > "$calls"; RULE24_RUNNER="$runner" bash "$ME" base t1 >/dev/null 2>&1 || { echo "self-test failed: a green crate was red"; fails=1; }
[ "$(tr '\n' ';' < "$calls")" = "a check;a test;" ] || { echo "self-test failed: check then test were not run on the crate: $(tr '\n' ';' < "$calls")"; fails=1; }
: > "$calls"; RULE24_RUNNER="$runner" bash "$ME" t3 t4 >/dev/null 2>&1 && { echo "self-test failed: a red crate suite passed the gate"; fails=1; }
grep -q '^bad test$' "$calls" || { echo "self-test failed: the red crate's suite never ran"; fails=1; }
grep -q '^vendor' "$calls" && { echo "self-test failed: a fork crate was run here"; fails=1; }
: > "$calls"; RULE24_RUNNER="$runner" bash "$ME" t1 t2 >/dev/null 2>&1 || { echo "self-test failed: documents-only was red"; fails=1; }
[ ! -s "$calls" ] || { echo "self-test failed: documents-only ran a crate"; fails=1; }
[ "$fails" = 0 ] && echo "self-test passed: a .rs, Cargo.toml, build.rs or .cargo change names its crate (workspace root when no nearer package; the core pair for a root config; a vendor crate is set aside); docs and site alone are documents-only and run nothing; a tools change runs nothing; a touched crate gets cargo check then its suite on the box and a red suite fails the gate"
exit $fails
fi
[ $# -ge 2 ] || { echo "usage: rule24-crate-gate.sh <base> <head> [--dry] | --self-test" >&2; exit 2; }
BASE="$1"; HEAD="$2"; DRY=0; [ "${3:-}" = --dry ] && DRY=1
rc=0; out=$(plan "$BASE" "$HEAD") || rc=$?; [ "$rc" = 0 ] || exit 2
if [ "$DRY" = 1 ]; then printf '%s\n' "$out"; exit 0; fi
case "$out" in
documents-only) echo "rule24: documents-only (docs/ and site/ alone): no crate suite"; exit 0 ;;
no-crate) echo "rule24: no .rs, Cargo.toml, build.rs or .cargo change: the full gate alone"; exit 0 ;;
esac
red=0
while read -r kind dir; do
[ -n "$kind" ] || continue
if [ "$kind" = vendor ]; then echo "rule24: $dir is a fork crate (its own lane's suites); not run here"; continue; fi
echo "rule24: $dir: cargo check on the box at gate priority"; run_crate "$dir" check || { echo "rule24: RED: $dir does not check" >&2; red=1; continue; }
echo "rule24: $dir: the crate suite on the box at gate priority"; run_crate "$dir" test || { echo "rule24: RED: $dir's suite" >&2; red=1; }
done <<<"$out"
[ "$red" = 0 ] && echo "rule24: every touched crate checks and its suite is green"
exit $red