igneum/docs/fork-map.md
igneum-labs e18d16e91b 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

31 lines
10 KiB
Markdown

# 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.