igneum/docs/spec/02-consensus.md
igneum-labs 09900b9432 Review round 3: spec rules for F15, F17, F14, P11, E9, E10, G9; ledger and fixes rows
The four public sentences (F18, P13, E11, L7 in site/litepaper.html and site/index.html) were swept into the concurrent commit cd604db; this commit carries the rest.

Spec 02: finality depth (43,200 DAA s, 12 h of median time) named as the reorg bound, merge depth as a merge limit only, simnet reorg test for gate 2; emission follows the code (365.25-day year, 63,115,200-s halving, 31.688 IGN per DAA second, reds inside the DAA window paid to the merger, E paid per block so coins are blocks times E).
Spec 03: W2 and Q1 denominated in past-median time, weight as a share of each 60-s bucket; W6 keys are free, weight is the only Sybil-resistant quantity; 3.8 and 3.9 point exchanges at the finality depth.
Spec 06: O-3.14, the finality simulation with the DAA in the loop under a pulsed rental (gate 3).
Spec 07: shard sortition draws by weight (blue blocks drawn uniformly from the window); proof-record validity is relative to the carrying block's own selected-parent chain; 1-key-versus-1,000-keys test.
Spec 08: release-key chain, rotation signed by the current key, revocation signed by the previous key, both published in a block; policy before the client ships.
Ledger: status lines for F18, P13, E11, L7, P11, F17, F14, M14, F15, E9, E10, G9; P8 cross-reference. Fixes: section 2.2 rows 75 to 88, with M15, M20 and P12 marked code, owner consensus engineer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:44:40 +00:00

14 KiB

Igneum protocol specification, section 2: the ordering layer

Spec version 0.1, 3 October 2026. Status of this section: Designed. The base (rusty-kaspa at commit 01b532e8b553523216471682649693af92f0fd16, v2.1.0) is built and has run a 3-node devnet at Kaspa's own parameters (docs/bench-log.md, entry "rusty-kaspa base build and 3-node devnet"). No fork point is implemented. docs/fork-divergence.md does not exist yet; when it does, it records what was changed against this section.

This section is written as a delta on rusty-kaspa. Everything not named here is Kaspa's rule at the forked commit. File and line references are those of docs/fork-map.md, which must be re-checked after any vendor update.

2.1 Block rate and GHOSTDAG parameters

Parameter Launch value Kaspa source Label
Target block rate 1 block per DAA second consensus/core/src/config/bps.rs, Bps::<1> (fork map f1) Designed
GHOSTDAG k 18 Kaspa's k table at 1 BPS (fork map f1) Designed, taken from Kaspa's table (delta 0.01, network delay bound 5 s, constants.rs:13 to 16)
Max parents 10 same Designed
Mergeset size limit 180 same Designed
Merge depth 3,600 DAA s (3,600 blocks at 1 BPS) MERGE_DEPTH_DURATION (fork map e1); check_bounded_merge_depth, post_pow_validation.rs:79 Designed, unchanged from Kaspa. Bounds which red blocks a block may merge; it is not a reorg bound (ledger F15, round 3)
Finality depth 43,200 DAA s (12 hours at 1 BPS) FINALITY_DURATION; virtual_processor/processor.rs:1626 Designed. The reorg bound a user relies on whenever no certified checkpoint is newer: the virtual switches to any heavier selected chain forked above this depth (ledger F15). Live finality is section 3
Pruning depth 108,000 DAA s PRUNING_DURATION Designed; MUST stay above the longest checkpoint gap and MUST NOT pass the latest certified checkpoint (section 3, F3)
Coinbase maturity 100 blocks bps.rs:119 to 121 Designed
Block-rate steps 1, then 4, then 10 per second design document, decisions table Designed. Each step is a planned fork with its own test campaign (as Kaspa's Crescendo), taken only once proving lag holds under 60 s. Each step re-derives k, max parents and mergeset limit from Kaspa's table; the DAA-second schedules of sections 1, 3, 4 and 5 do not move

Every Kaspa fork activation (ForkActivation) is always() on Igneum: there is no history to replay (fork map f2). Own genesis, network prefix, ports and seeders (network.rs:42 to 60, 238 to 252).

The reorg bound (ledger F15, round 3). Merge depth limits what an old block can merge; it does not stop the virtual switching to a heavier selected chain. The depth that stops a switch is the finality point, FINALITY_DURATION, 43,200 DAA s, and that is the bound users and exchanges rely on whenever no certified checkpoint is newer (section 3.9). It is stated to users in past-median time, 12 hours, because DAA seconds run fast under a retarget lag (ledger M14; docs/review/round-3-2026-10-03.md, "Tonight's devnet", where DAA time ran 2.9x the wall clock for 17 minutes). Whether to lower FINALITY_DURATION and accept Kaspa's finality-conflict handling at the lower depth is decided at gate 2 with a simnet test: a heavier private chain forked 2, 6 and 13 hours back is released and the node must refuse it at the depth this section names.

2.2 Proof of work (fork points a1 to a5)

Fork point Kaspa today Igneum
a1, a2 PowHash cSHAKE256 then KHeavyHash matrix Both deleted. The lottery hash of section 1 replaces them. kaspa_pow::State carries the igneum_pow::Epoch for the header's epoch (program by epoch of the header's DAA score, dataset by day of the header's DAA score, section 1.12)
a3 pow <= target on a Uint256 Section 1.10: hash64 <= target64. The mapping from the 256-bit target and the block level for pruning proofs are Open (O-2.4)
a4 Header validated in isolation Header validation gains a dependency on chain state: the epoch seed (section 4). The seed source is named in the header (section 2.4) so the dependency is checkable from the header plus its past. During IBD and pruning-proof validation (pruning_proof/validate.rs:192) the node MUST be able to derive the program for any header from headers and certificates alone; this is the hardest fork point (fork map, risk High) and is Open until the devnet proves it (O-2.5)
a5 Pre-PoW hash over 12 fields Adds vote_key_hash, seed_source and proof_ref (section 2.4) so all three are committed by the nonce. Every header-hash test vector changes, genesis included

What the nonce commits to: the pre-PoW header hash H enters the register initialisation (section 1.6, Open O-1.9). The VDF requirement that the seed checkpoint commits to the full block hash including the nonce (proto-vdf/README.md) is satisfied by Kaspa's Header.hash covering the nonce.

2.3 Difficulty adjustment (fork points c1, c2)

Kaspa's sampled DAA (KIP-4) is kept as the retarget: average target of a sampled window, new_target = avg x measured / expected, clamped to MAX_DIFFICULTY_TARGET = 2^255 - 1, minimum window 150 samples (difficulty.rs:97 to 198).

Parameter Value Label
Window duration 2,641 DAA s (Kaspa's DIFFICULTY_WINDOW_DURATION) Designed, prototype value, to be fixed at gate 2. The design document says "about 2,600 blocks, roughly 45 minutes"
Sample interval 4 s, 661 samples Kaspa's constants, kept
Keyed to DAA score, never wall-clock Designed

What fixes the window: the hash rate steps at every epoch because programs differ in cost (35 to 48 Mhash/s across seeds on the M5 Max, docs/bench-log.md first-run entry; 228 vs 185 Mhash/s for 104 vs 128 loads on the RTX 5090). With a 44-minute window a 30% step means roughly half an epoch at the wrong block rate (fork map c1). Two remedies, decided at gate 2 by a simpa run (fork map c2): shorten the window (candidate 1,800 s), or equalise cost per program through the exact-load-count rule of section 1.4.2, which this specification prefers because it removes the step rather than tracking it.

The 30-day vote-weight window of section 3 is denominated in past-median time since 3 October 2026 (section 3.1 W2, ledger F14), so a retarget lag cannot age it; it is not a retarget change.

2.4 Header (fork point d)

Kaspa's Header (version, parents, hash_merkle_root, accepted_id_merkle_root, utxo_commitment, timestamp, bits, nonce, daa_score, blue_score, blue_work, pruning_point) plus:

Field Size Meaning Label
vote_key_hash 32 bytes Hash (the chain's BLAKE2b-based Hash) of the producer's BLS12-381 G1 compressed public key (48 bytes). The first block that uses a key reveals the key itself in the coinbase payload. Vote weight accrues to this key (section 3, W1) Designed. The design document says a 32-byte hash; docs/fork-map.md row d says a 48-byte key. This specification takes the hash: 32 bytes x 86,400 blocks/day = 2.8 MB/day of header growth against 4.1 MB with the key
seed_source 32 bytes Hash of the checkpoint block from which the header's epoch program seed was derived (section 4.3) Designed, proposed here, Open (O-4.3)
proof_ref 32 bytes Hash of the highest aggregated block proof the producer knows Forward reference. The proof format, what "highest" means and the validation rule belong to the chunked proving protocol, which is out of scope for 0.1. Until that protocol is specified the field is all zeros and unvalidated

Also edited (fork map d): p2p p2p.proto:76 to 89, convert/header.rs:13, 45; RPC rpc.proto:25, model/header.rs:85, 105, 248; genesis headers; the headers store (serde, re-sync needed); header mass.

Certificates (section 3, C3) and votes (section 3, Q2) travel in the block body, not the header: every block carries the highest certificate its producer knows and every valid vote it has received for the presence window that is not already in its past, and a block whose selected chain does not pass through every certified checkpoint in its past is invalid (post-PoW validation, fork map e2).

2.5 Emission (fork points b1, b2, b3)

Designed (design document, "The token" and "Difficulty, block timing and proving cadence"). No pre-deflationary phase, no month table, no treasury.

Parameter Value Label
Hard cap 4,000,000,000 IGN, approached and never reached Designed
Year 31,557,600 DAA s (365.25 days) Decided 3 October 2026 (ledger E9, round 3): the specification follows the code, consensus/core/src/igneum.rs SECONDS_PER_YEAR; the earlier 365-day prototype value is withdrawn. The design document says "1 billion a year"
Emission in the first two years 1,000,000,000 IGN per year Designed
Halving interval 63,115,200 DAA s (2 years of 365.25 days), for ever Decided (ledger E9), HALVING_INTERVAL_SECONDS
Launch ramp linear from 10% at genesis to 100% at DAA second 2,592,000 (30 days) Designed
Split 80% block producer, 20% proving pool. A red block inside the DAA window pays its 80% to the miner of the block that merges it and its 20% to the pool Designed; the red rule Decided 3 October 2026 (ledger E10, round 3), coinbase.rs:102 to 109
Base unit Open (O-2.6): the code keeps Kaspa's 8 decimals (SOMPI_PER_KASPA, devnet v0), under which the cap is 4 x 10^17 units and fits a u64; 18 decimals (the EVM convention) puts the cap at 4 x 10^27 and needs a wider type. docs/fork-map.md b2 wrote cap_sompi = 4e9 x 1e8. Decided before the first testnet genesis

Emission per DAA second at DAA score t:

E(t) = ramp(t) * floor(10^9 * UNIT / 31,557,600) >> floor(t / 63,115,200)
ramp(t) = min(1, 1/10 + 9/10 * t / 2,592,000)      evaluated in integers as a rational with denominator 25,920,000

The pre-ramp rate is 31.68808781 IGN per DAA second at 8 decimals (BASE_SUBSIDY_PER_SECOND_SOMPI = 3,168,808,781, asserted by the code's own test) and about 31.688 IGN at any unit (Decided, ledger E9). The geometric series sums to 4 x 10^9 IGN; the ramp withholds 0.45 x 2,592,000 / 31,557,600 x 10^9 = about 37 million IGN that are never minted, and integer floors withhold a negligible further amount, so the cap is a strict bound.

Emission is keyed to DAA score, not to timestamps: E is a function of DAA score and miner-chosen timestamps cannot mint (hostile review table, "Emission per wall-clock second invites timestamp games"). E is paid per block, so coins are blocks times E: when the controller lets the block rate run above target, short-run emission runs above schedule by the same factor, as on every proof-of-work chain, and the cap is unaffected because the halving schedule and the ramp are in DAA seconds (Decided 3 October 2026, ledger E10, round 3; the earlier sentence "more blocks never means more coins" is withdrawn, and the devnet of 3 October 2026 minted 4.7x the schedule for eight minutes during a retarget lag, docs/review/round-3-2026-10-03.md, "Tonight's devnet"). Payment is through the merging block's coinbase as in Kaspa (coinbase.rs:97 to 142): the coinbase of block B pays, for each blue block M in B's mergeset, E(daa_score(B)) split 80% to M's miner and 20% to the proving pool; for each red block in B's mergeset that is inside the DAA window, the same E split 80% to B's own miner and 20% to the pool (coinbase.rs:102 to 109, Kaspa's rule, Decided, ledger E10); a red outside the DAA window earns only its coinbase-level fees, of which Igneum has none (fees are in section 5). How the DAA-score increment is apportioned among the blues of one mergeset at higher block rates is Open (O-2.7); at 1 BPS the mergeset is usually one block.

The 20% proving share is paid to the prover set recorded for the proven block, which is known 20 to 60 s after the block (design document, "Proving lag"). The coinbase payload format gains prover outputs (fork map b3, risk High). The rule that the pool is paid as a fixed amount per block divided among shards by consensus proving cost is in section 5.3.

Fees are not in the coinbase; they are in the execution layer (section 5).

2.6 Duplicate inclusion for the EVM layer

Designed (hostile review table, "Parallel blocks include the same transaction"). Blocks carry transactions only and make no claim about state. The execution layer orders transactions by the GHOSTDAG ordered sequence (the selected chain's mergeset order, Kaspa's), and:

  1. The first copy of a transaction (by transaction hash) in the ordered sequence executes and pays its fees.
  2. Every later copy is deduplicated before execution and before proving. It executes nothing and pays nothing.
  3. A later copy still occupies block space (mass) in the block that includes it, so the including miner bears the cost.
  4. Transactions from one account are subject to the EVM nonce rule over the same ordered sequence: a transaction whose nonce is not the account's next nonce at its position is skipped, not failed, and pays nothing. This is Kaspa's "skip conflicting spends" pattern applied to the EVM.

Block number, timestamp, blockhash, coinbase and prevrandao over the ordered sequence are fixed in section 7.1 (ledger P5, closed 3 October 2026).

2.7 What the devnet showed and did not show

Measured (docs/bench-log.md, devnet entry): three kaspad nodes at Kaspa's devnet parameters (10 BPS, k 124) held identical block counts, DAA scores and sink hashes at 18 of 19 ten-second samples under a 27 MH/s CPU miner; the DAA raised difficulty from genesis bits at block 6,018 and the block rate fell from 56 to 62 blocks/s toward the 10 BPS target. GHOSTDAG k was not exercised (a single serial miner never produced parallel blocks); a second miner is the next step. Nothing in that run used an Igneum parameter.