- docs/bench-log.md: "6 October 2026, 18:40Z to 20:41Z, the live devnet's finality pause": the timeline from the hub's own log and the hands' logs (local time +01:00: both hands stopped 18:30:28Z, returned 18:41:54Z and 18:42:07Z, relaunched under launchd 18:45:57Z and 18:46:39Z), the cause in the code (rule v3's frozen table at lock 6842, 42.7 percent of it held by 20 keys that stopped at 17:20 to 18:30Z, expiring at DAA 216,402), what was ruled out with the line that rules it out (the hands, the aggregators, the "level is 0" rejects = the Igneum Wallet app's bundled 5 October igneumd on this Mac), and what "paused" meant per tier - docs/fud-ledger.md: F27 (the incident as a critic will post it, with the fix and the operational rule) and the F19 correction: a silent 40 percent under rule v3 stalls the full window, 30 days on mainnet, not "day 19 to 20" (the v2 arithmetic) - docs/spec/03-finality.md: W7, the departure announcement, and the Q5 note - docs/plans/finality-leave.md: the 0.3.16 plan (what changes, why this candidate, the Devnet 2 gate, what it does not do, per tier) - tools/finality-attacks/leave.mjs: the harness case on the v3 runner's shape (45 percent of weight stops with leaves; --silent is the known-failed case), designed and not yet run (it needs W7 binaries on the harness host); lib/net.mjs gains the noLeave option - infra/fast-time/override-60x.json: leave_delay 10 and the leave switch at never, explicit Fork: branch finality-pause-node on 4c6b129d (vendor/igneum-node-finpause). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
7.4 KiB
The departure announcement (spec 03 W7): plan for 0.3.16
Written 6 October 2026, 21:2xZ, by the finality owner, during the live devnet's finality pause (18:40Z to about 20:41Z).
Cause and candidates: docs/analysis/horizon/finality-and-weight.md (Horizon lane 3). Incident: docs/bench-log.md
"6 October 2026, 18:40Z to 20:41Z, the live devnet's finality pause"; ledger F27 and the F19 correction. Code: fork
branch finality-pause-node on 4c6b129d (release 0.3.14's node), worktree
/Users/joshm/Projects/igneum-wt-finality-pause/vendor/igneum-node-finpause.
1. What changes
| Piece | Where | What |
|---|---|---|
| The leave item | consensus/core/src/finality.rs |
Leave { daa, pubkey, signature } (152 bytes) signed under DST_LEAVE over `"igneum-leave-v1/" |
| The switch | consensus/core/src/config/params.rs |
finality_leave_activation_daa on Params and OverrideParams (never on mainnet, devnet, simnet; 0 on the testnet, which is born on every switch), in the digest with leave_delay |
| The rule | consensus/src/processes/finality.rs |
leaves_at (like bans_at: a key has left at C when a block in C's past carries its leave and daa(e) + leave_delay <= daa(C) < daa(e) + window); voters_at applies leaves with the bans (the sliding table); frozen_floor subtracts the keys that left at C from the frozen table's denominator and their signatures from its numerator (Q5); ingest_leave (one record per key, carriers dated), submit_leave, carriage in template_section LAST and only once the rule is active; persisted state layout 3 (leaves), layouts 2 and 1 read and upgraded |
| p2p | protocol/flows/src/v10/finality.rs, flow_context.rs, v17/, service.rs |
KIND_LEAVE = 3; protocol version 17; broadcast_finality_leave to peers at 17 or later only (an older relay flow returns a protocol error on an unknown kind and drops the peer); every peer still reads leaves from blocks |
| RPC | rpc/* |
submitFinalityLeave (hex of the wire bytes; accepted, reason), op 157, proto ids 1129 and 1130 |
| The miner | igneum/miner/src/main.rs |
mine sends the leave for every identity when secs run out unless --no-leave; igneum-miner leave <grpc url> <label> by hand (a node back from an unplanned stop, an operator retiring a key) |
| The node's first lines | kaspad/src/daemon.rs |
Finality leave rule from the override file: ... from checkpoint DAA score N and the Finality v2 (...) line ends with leave rule (W7, delay 3600 DAA) from checkpoint DAA N |
Nothing in block validity changes. A block carrying a leave is as valid as before on every version; a 0.3.14 or 0.3.15 node's section decoder stops at the leave's tag and keeps the votes, certificates and evidence before it, which is why the template puts leaves last. Before the activation no node carries a leave in a block, so an old node never meets the tag; after it every node is new by the 95 percent signal rule (or the floor height), so the voter tables agree.
2. Why this and not the others
Section 4.2 of the Horizon analysis: a rule that removes weight on what a view has NOT seen (a vote missing for T hours, a decaying denominator, a hysteresis floor) gives each side of a partition a different denominator and reopens the double lock (467 to 473 conflicting locks in the 50/50 split under fast decay; the 13.3 percent equivocator bound of 3 October back under hysteresis). A leave is seen, not inferred: a partition side never sees the other side leave. In the simulator (3 seeds, mainnet scale) the leave rule gives a first lock 1 hour after a 34, 45 or 50 percent departure where v3 waits 30 days, 0 conflicting locks in every partition, eclipse and equivocator row, and an attacker who buys keys to make them leave is worse off than one who signs with them (w + L must still reach 2/3).
3. Gate (Devnet 2, the 0.3.16 crossing)
| Step | Pass line |
|---|---|
Unit tests on igneum-build-1 (cargo test -p kaspa-consensus --lib finality, -p kaspa-consensus-core --lib) |
a_signed_leave_ends_the_pause_after_the_delay_and_a_silent_departure_holds_it_for_the_window passes: the known-failed case (a silent 40 percent pauses until the frozen table expires) and the fixed case (lock within leave_delay plus a few checkpoints), and a leave before activation removes nothing; leave_item_round_trips_and_an_older_decoder_keeps_the_items_before_it passes |
| Digest test | ("finality leave height", ...) and ("finality leave delay", ...) move the digest; the fast-time profile keeps the devnet switch |
| Devnet 2, the node cut's standing checks | a MINING 0.3.16 node beside a 0.3.15 node on the live file for ten minutes: the old node accepts the new node's blocks, the hub's reject count unchanged (no leave is carried before activation); a 0.3.16 node restarted mid-window re-syncs from an old peer |
Devnet 2, the W7 case (the fast-time harness, tools/finality-attacks, a new leave.mjs on the v3 runner's shape: N nodes, the switch at 0 in the override, leave_delay 10 in the 60x profile) |
45 percent of weight stops with leaves (their mine runs end): first lock within leave_delay + 1 checkpoint of the first carrier; the same 45 percent stopped with --no-leave: no lock until the frozen table expires (the known-failed case); 0 conflicting certificates in the 50/50 and 60/40 splits and the 34 percent eclipse (the s4 shape) with leaves in flight |
| Activation on the live devnet | by the 95 percent signal rule with a floor height, miners first, hands last; the override object gains "finality_leave_activation_daa": N7 |
The harness case is designed here and not yet run: it needs 0.3.16 binaries on the harness host (the box builds Linux binaries; the Mac's harness runs macOS ones), so it is the first thing the Devnet 2 crossing does.
4. What it does not do, stated
A crash, a power cut or a region going dark still age out over the window under Q5 (two hours on the devnet, 30 days on
mainnet); the app's Stop and the fleet library send the leave, and a node back from an unplanned stop sends it on
return (igneum-miner leave), which shortens the pause from that point. Staging a departure under 10 percent an hour
keeps finality on with no protocol change (every lock re-freezes the table); the fleet rule of 20:0x UK stands beside
this. Its interaction with W5 succession (O-3.11) and with a key that leaves and keeps mining (its blocks count again
once the leave is a window old; before that it is out of every table) is written into spec 03 W7 and needs the
cryptographer's read.
5. Per tier
| Tier | What changes |
|---|---|
| Home miner, rig (Windows, Linux, macOS; any card) | the app sends the leave on Stop and on a clean update restart, so a clean exit never holds the network; a crash still ages out over 30 days unless the operator sends the leave on return, which the app will offer |
| Pool | one leave per server on maintenance; a pool that moves servers without one holds the network's finality for a window |
| Holder | fewer and shorter pauses: an orchestrated departure of any size ends its pause in an hour |
| Rollup customer, bridge | the same; anything that waits for a certified checkpoint waits an hour at most for a clean departure instead of a month |
| Node operator | one more item type, one more RPC, protocol version 17; the persisted finality state upgrades itself on first start |