- 03-finality: Q2 participation from every vote seen in blocks (votes are block payload); 3.3.2 the eclipse case closed by the 56.7% floor with the sim v2 F2 numbers; 3.4.1 why aggregators cannot grind participation. - 07-execution (new): EVM semantics on the DAG, shard sortition (8 provers, 10 s, then open, no shard bond), bridges (none official, no bridged stablecoins at genesis, proof bridge with the consensus proof in phase two). - 08-client-security (new): reproducible builds, release key in genesis and in hardware, no silent updates, consensus only by 90% signalling, notarised builds, official sources with the hash, seed confirmed before mining, hardware wallet, the permanent seed line. - 00, 02, 05, README, 06: cross-references, O-2.8 removed, O-3.3, O-3.7, O-5.1, O-5.2, O-5.6 narrowed, O-8.1 added, counts kept at 60. - Ledger: eight Status lines, status table, count table, overclaims 38, 40, 71. - FUD fixes: rows 23, 26, 38, 62, 64, 66, 67, 70, 72, section 3 and 4. - Site: bridge and stablecoin sentences no longer launch features; the seed line wherever the app appears. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
11 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) |
Designed, unchanged from Kaspa |
| Finality depth (backstop only) | 43,200 DAA s | FINALITY_DURATION |
Designed; 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).
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 a new reader of DAA scores, 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,536,000 DAA s (365 days) | Designed, prototype value: the design document says "1 billion a year"; the day count is this specification's choice |
| Emission in the first two years | 1,000,000,000 IGN per year | Designed |
| Halving interval | 63,072,000 DAA s (2 years), for ever | Designed |
| 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 | Designed |
| Base unit | Open (O-2.6): the EVM layer implies 10^18 per IGN; docs/fork-map.md b2 wrote cap_sompi = 4e9 x 1e8. One of the two is chosen before any coinbase code is written |
Emission per DAA second at DAA score t:
E(t) = ramp(t) * floor(10^9 * UNIT / 31,536,000) >> floor(t / 63,072,000)
ramp(t) = min(1, 1/10 + 9/10 * t / 2,592,000) evaluated in integers as a rational with denominator 25,920,000
With UNIT = 10^18 the pre-ramp rate is about 31.7098 IGN per DAA second (Designed). The geometric series sums to 4 x 10^9 IGN; the ramp withholds 0.45 x 30/365 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 blocks or timestamps: more blocks never means more coins and miner-chosen timestamps cannot mint (hostile review table, "Emission per wall-clock second invites timestamp games"). Payment is to blue blocks only, 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. Red blocks are unpaid. 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:
- The first copy of a transaction (by transaction hash) in the ordered sequence executes and pays its fees.
- Every later copy is deduplicated before execution and before proving. It executes nothing and pays nothing.
- A later copy still occupies block space (mass) in the block that includes it, so the including miner bears the cost.
- 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.