10 KiB
10 KiB
Igneum fork map for rusty-kaspa
Base: rusty-kaspa commit 01b532e8b553523216471682649693af92f0fd16 (v2.1.0, 22 Sep 2026), cloned to vendor/rusty-kaspa on 3 Oct 2026.
Line numbers are from that commit. Re-check them after any vendor update. "Igneum change" follows the design paragraph in CLAUDE.md.
Risk: how much of the node's logic the change touches and how easy it is to get wrong. Nothing here has been implemented yet.
The six fork points
| # | What | File (vendor/rusty-kaspa/) | Lines | Today in Kaspa | Igneum changes it to | Risk |
|---|---|---|---|---|---|---|
| a1 | PoW hash, stage 1: PowHash = cSHAKE256("ProofOfWorkHash") over pre-PoW header hash, timestamp, nonce |
crypto/hashes/src/pow_hashers.rs |
4, 9 to 37 | Keccak f1600 with a fixed initial state | Replaced by the random-program GPU hash: the kernel is drawn per ~1 h epoch from a VDF seed of a certified checkpoint, reads a 256 MB RandomX-style cache (8 dependent reads per item), warp-unit CPU-verifiable | High. New primitive, needs bit-exact GPU and CPU paths (proto-metal and proto-cuda already agree on one program). |
| a2 | PoW hash, stage 2: KHeavyHash = 64x64 4-bit matrix multiply then cSHAKE256("HeavyHash") |
crypto/hashes/src/pow_hashers.rs; consensus/pow/src/matrix.rs |
39 to 60; Matrix::generate 28 to 37, heavy_hash 101 to 125 |
Matrix from xoshiro seeded by the pre-PoW hash, rank-64 check | Deleted. The program generator replaces the matrix; the epoch seed replaces the per-block matrix seed | Medium. Pure removal, but kaspa_pow::State callers assume a per-header precompute. |
| a3 | PoW state and check: State::new, calculate_pow, check_pow, calc_level_from_pow |
consensus/pow/src/lib.rs |
19 to 56, 58 to 77 | pow <= target on a Uint256 from the heavy hash |
Same interface, new body. State must carry the epoch program (looked up by header DAA score or timestamp), not a matrix |
Medium. Block level (used by pruning proofs, 74 to 77) assumes a uniform 256-bit output; the new hash must keep that. |
| a4 | PoW validation call site in header processing | consensus/src/pipeline/header_processor/pre_ghostdag_validation.rs |
16 to 22 (validate_header_in_isolation), 102 to 106 (check_pow_and_calc_block_level) |
RuleError::InvalidPoW unless skip_proof_of_work |
Unchanged shape, but the check needs the epoch program for that header's epoch, so it gains a dependency on chain state (the VDF-certified checkpoint) | High. Header validation today needs nothing beyond the header. An epoch lookup during IBD and pruning-proof validation (consensus/src/processes/pruning_proof/validate.rs:192) must be deterministic from headers alone. |
| a5 | Pre-PoW header hash (what the nonce commits to) | consensus/core/src/hashing/header.rs |
7 to 30 (hash_override_nonce_time), 33 to 35 (hash) |
BlockHash over version, parents, 3 roots, timestamp, bits, nonce, daa_score, blue_score, blue_work, pruning_point | Adds the vote key (see d) between pruning_point and the end, so the vote key is PoW-committed |
Low. Mechanical, but every header hash test vector changes, genesis hashes included. |
| b1 | Block subsidy schedule | consensus/src/processes/coinbase.rs |
23 (SECONDS_PER_MONTH), 25 to 26 (table type), 222 to 234 (calc_block_subsidy), 236 to 255 (subsidy_month), 280 (SUBSIDY_BY_MONTH_TABLE, 426 entries) |
Pre-deflationary flat subsidy, then a 426-month table (approximate reading: yearly halving in 12 monthly steps, see the table itself) | Hard cap 4 billion, halving every two years from genesis, no pre-deflationary phase, no table: a closed form base >> (daa_score / blocks_per_two_years) at 1 BPS. NO emission treasury |
Medium. Simple maths, but bps_history and Crescendo per-BPS table rescaling (76 to 78) must be stripped rather than kept half-alive. |
| b2 | Subsidy parameters | consensus/core/src/config/params.rs; consensus/core/src/config/bps.rs |
params 637 to 638 (mainnet deflationary_phase_daa_score, pre_deflationary_phase_base_subsidy), 342 to 344 (fields); bps 131 to 137 |
50 KAS per second split by BPS | New fields: cap_sompi (4e9 x 1e8), halving_interval_blocks, launch ramp (30 days) |
Low. |
| b3 | Coinbase payee and split | consensus/src/processes/coinbase.rs |
97 to 142 (expected_coinbase_transaction), 144 to 220 (payload serialize, modify, deserialize) |
One coinbase output per blue block to that block's miner, red blocks unpaid | 80/20 lottery/proving split: the 20% goes to the prover set recorded for the proven block, so the coinbase gains prover outputs. Fees: base fee burned in full, priority fee 65/15/15/5 with the 5% dev share per call frame (fee logic lives in the execution layer, not here) | High. Changes coinbase payload format and the "miner data" validation of every peer; the prover set must be known at coinbase construction time (20 to 60 s lag). |
| c1 | Difficulty adjustment (sampled DAA, KIP-4) | consensus/src/processes/difficulty.rs |
97 to 135 (SampledDifficultyManager), 166 to 198 (calculate_difficulty_bits), 211 to 223 (calc_work) |
Average target of a 661-sample window (sample every 4 s), new_target = avg x measured / expected, clamp to max_difficulty_target, min window 150 samples |
Kept as the retarget, but the hash speed jumps at every ~1 h epoch (bench: 35 to 48 Mhash/s across seeds on the same GPU) so the window must be short enough to track within an epoch, or the kernel generator must equalise cost per program. The 30-day vote-weight window (finality rule v2) is a new reader of DAA scores, not a retarget change | Medium. A per-epoch 30% hashrate step with a 44-minute window means roughly half an epoch at the wrong block rate. |
| c2 | DAA constants | consensus/core/src/config/constants.rs |
40 to 44 (MAX_DIFFICULTY_TARGET 2^255 - 1), 54 (MIN_DIFFICULTY_WINDOW_SIZE 150), 57 (DIFFICULTY_WINDOW_DURATION 2641 s), 60 (sample interval 4 s), 63 (sampled size 661) |
As listed | Candidates: window 1800 s or shorter, keep 4 s samples; evaluate in simpa before deciding | Low. |
| d | Block header struct (vote key field) | consensus/core/src/header.rs |
154 to 172 (Header), 176 to 207 (new_finalized), 210 to 212 (finalize) |
12 fields, hash cached | Add vote_key: [u8; 48] (BLS12-381 G1 compressed public key; the finality vote weight is blue blocks per vote key over a flat 30-day DAA window, dust threshold 100 blocks). Every block carries it; equivocation evidence strips that key's weight for 30 days |
High by spread, low by depth. Also edit: p2p wire protocol/p2p/proto/p2p.proto:76 to 89 and protocol/p2p/src/convert/header.rs:13, 45; RPC rpc/grpc/core/proto/rpc.proto:25 and rpc/core/src/model/header.rs:85, 105, 248; genesis headers consensus/core/src/config/genesis.rs; DB store consensus/src/model/stores/headers.rs:24 (serde, re-sync needed); header mass. 48 bytes x 86,400 blocks/day = 4.1 MB/day extra. |
| e1 | Merge depth and finality depth constants | consensus/core/src/config/constants.rs |
70 (FINALITY_DURATION 43,200 s), 73 (PRUNING_DURATION 108,000 s), 81 (MERGE_DEPTH_DURATION 3600 s), 84 (PRUNING_PROOF_M 1000) |
Scaled by BPS in bps.rs:88 to 108 (at 10 BPS: merge 36,000 blocks, finality 432,000, pruning 1,080,000) |
Merge depth kept at Kaspa's 3,600 s (design: fork choice is GHOSTDAG among tips through all certified checkpoints under merge-depth 3,600 s). Finality depth becomes a backstop only: the live finality is the 30-s certified checkpoint (2/3 of ACTIVE weight over a 2-hour presence window). Pruning depth must stay above the longest checkpoint gap | Medium. |
| e2 | Where depth is enforced | consensus/src/processes/block_depth.rs; consensus/src/pipeline/header_processor/post_pow_validation.rs; consensus/src/pipeline/virtual_processor/processor.rs |
block_depth 51 to 69 (calc_merge_depth_root, calc_finality_point); post_pow 79 to 100 (check_bounded_merge_depth, kosherizing blues); processor 377 to 390 (virtual_finality_point) |
Depth from blue score only | virtual_finality_point returns the latest certified checkpoint when one is newer than the depth point; virtual selection must refuse tips that do not descend from every certified checkpoint. The hidden-block n^2 penalty is NOT added (removed in review round 2, it broke DAG determinism) |
High. Touches the virtual processor and finality-violation handling, which assume finality is a pure function of the DAG. |
| f1 | BPS and GHOSTDAG k | consensus/core/src/config/bps.rs |
24 (TenBps), 38 to 46 (k table: 1 BPS => 18, 10 BPS => 124), 49 to 54 (target_time_per_block = 1000 / BPS), 57 to 73 (max parents), 75 to 86 (mergeset limit), 119 to 121 (coinbase maturity) |
k = 124 at 10 BPS (delta 0.01, network delay bound 5 s, constants.rs:13 to 16) |
1 block/s at launch: Bps::<1> gives k = 18, 1000 ms blocks, 10 max parents, mergeset limit 180, merge depth 3,600 blocks, finality 43,200 blocks, pruning 108,000 blocks, coinbase maturity 100 blocks |
Low. This is the code path Kaspa mainnet ran before Crescendo. |
| f2 | Where the network picks its BPS | consensus/core/src/config/params.rs |
180 to 215 (BlockrateParams), 643 (mainnet BlockrateParams::new::<10>()), 645 (pre_crescendo_target_time_per_block: 1000), 648 (crescendo_activation), 699, 742, 783 (testnet, simnet, devnet) |
10 BPS everywhere, Crescendo and Toccata fork activations | Igneum mainnet params: BlockrateParams::new::<1>(), ForkActivation::always() for every past Kaspa fork (no history to replay), own genesis, own DNS seeders, own address prefix and ports (consensus/core/src/network.rs:42 to 60, 238 to 252) |
Low to medium. The fork-activation plumbing (bps_history, ForkedParam) is woven through coinbase, difficulty and mass; strip it in one pass. |
Notes from the 3 Oct 2026 devnet run (see docs/bench-log.md)
- Devnet and simnet in this commit run
BlockrateParams::new::<10>(): 10 BPS, k 124, 100 ms blocks, merge depth 36,000 blocks, finality depth 432,000, pruning depth 1,080,000, coinbase maturity 200 (params.rs 783, 806 to 815). Simnet also setsskip_proof_of_work: true(params.rs 737) and allows 64 parents (742). - Devnet genesis bits
0x1e21bc1c(genesis.rs 193) means about 248,663 expected hashes per block, held fixed until 150 samples x 40 blocks = 6,000 blocks (difficulty.rs 170 to 177). kaspadships no miner.cli/src/modules/miner.rsonly spawns an externalkaspa-cpu-minerbinary (daemon/src/cpu_miner/mod.rs:52 to 89). The 3-node test used a 150-line miner built onkaspa-pow::State(real kHeavyHash), kept outside the repo.