igneum/docs/fork-map.md
igneum-labs fcd8f820a9 Finality quorum: 2/3 of active weight and at least 56.7% of total, per simulation v2
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 16:15:15 +00:00

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 sets skip_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).
  • kaspad ships no miner. cli/src/modules/miner.rs only spawns an external kaspa-cpu-miner binary (daemon/src/cpu_miner/mod.rs:52 to 89). The 3-node test used a 150-line miner built on kaspa-pow::State (real kHeavyHash), kept outside the repo.