Finality guarantees and the long-partition boundary (rule v4): docs/spec/finality-guarantees.md, pointers in section 3, O-3.20, O-4.3 closed, README row
The external review of 8 October 2026 (accepted 16:1x UK): the boundary must be explicit; a timeout alone cannot tell a node whether missing miners are gone or partitioned. Rule v4: the frozen table is anchored (never expires by time) and, after a full window with no certificate, a lock needs Q3 plus more than half of the anchored table on the chain through the last certified checkpoint (the majority-continuity recovery, verifiable from the chain). Safety and Liveness stated with their arguments, the pause's duration by cause, what is not guaranteed, the single answer on program rotation during a pause (seeds from chain blocks, O-4.3 closed), the four interface words and where each interface shows them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
513bca993b
commit
47c76537cb
4 changed files with 213 additions and 5 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
206
docs/spec/finality-guarantees.md
Normal file
206
docs/spec/finality-guarantees.md
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# 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`; Devnet 3 from DAA 93,600, about 20:01 UK on 8 October 2026). 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 | at day 30 of silence the signing majority (more than half of `T_f`) recovers (N3) | 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); exactly half or more gone: never by rule (N6, 50 percent), only by succession, a strip or an operator's certificate (7) | day 30 (35 percent); never (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 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 whose share of `T_f` exceeds the split's imbalance: each side of an `s / (1 - s)` split with an equivocator at `a` holds `(1 - a) / 2 + a` of `T_f` in the 50/50 case, more than half for any `a > 0`; in a 60/40 split the 40 side needs `a > 0.2` to reach half. Measured (N1b, N2): at 31 days a 50/50 split with a 10 or 20 percent equivocator conflicts under `v4 recovery` and never under `v4 pause`; the 40/40 split with a 20 percent equivocator reaching both sides (60/60 of `T_f`) the same. 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) | pending |
|
||||
| (a) The recovery bound | N1b: 50/50 for 31 days with a 10, 20, 34 percent equivocator | 34 percent: conflicts from minute 0 under every rule (the one-third bound) | pending |
|
||||
| (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 | the 60/60 case under v4 recovery at day 30 (6.5) | pending |
|
||||
| (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) | pending |
|
||||
| (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) | pending |
|
||||
| (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: every pre-heal lock kept, first lock after the heal, post-heal stalls; the certificate-driven reorg (3.11.7, `c4.mjs`) | none | pending |
|
||||
| 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 on a box: `tools/finality-attacks/v3.mjs split50` past the window under v4 (the N1 shape on real nodes, three nodes, the 60x file), 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 (`/srv/artefacts/0326-f8da7515` on build-1), each under `lease pool` on build-2 or build-3; queued behind the sweeps at the time of writing.
|
||||
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).
|
||||
Loading…
Reference in a new issue