Live devnet v4: a second RTX 5090 joining 7 minutes into an epoch left the whole-epoch reference lane polluted for the hour; the short lane read 11 to 25% above it and the 25% trigger flipped between the two for 40 minutes (102M to 164M, 54 to 81 blocks a minute). Record and hash-rate truth under sim/difficulty/records/. sim.py gains a DAG model (miners on nodes with igneum-miner's template staleness, GHOSTDAG, the rule as the node runs it) and --live replay: std of log difficulty 0.115 against the record's 0.134, 4.3 peaks of 1.31x against 4 of 1.37x. The brief's candidates (short lane 240/360, ease clamp 3%, clamp once per DAA second, hysteresis, median of three) leave 0.09 to 0.13; capping the reference lane at the newest 600 blocks of the epoch gives 0.026 with no flips. Rule v2 = that cap, epoch lane only, behind difficulty_v2_activation_daa (devnet-v4 fork). Attack suite and synthetic set before and after, 3-node test network of the switch (testnet_v2.py), analysis document, spec 2.3, bench-log entry, ledger M24, fast-time file carries the new field. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| override-60x.json | ||
| README.md | ||
| simnet.mjs | ||
Fast time: the devnet at 60x for test networks
override-60x.json is the Igneum devnet with every clock-like consensus parameter divided by 60 and every block
count unchanged. A test network on it reaches its first finality lock in about three minutes instead of two hours and
crosses an hourly program epoch every minute, so a scenario that took hours of wall time takes minutes. 4 October 2026.
Flags
Node (devnet-v4 line, vendor/igneum-node-v4, built in vendor/igneum-node/target-integration):
igneumd --devnet --devnet-suffix=950 --override-params-file=infra/fast-time/override-60x.json ...
The file is complete: every field OverrideParams accepts is present (consensus/core/src/config/params.rs), so a
reader sees the whole profile in one place. Merge it with skip_proof_of_work: true for a network without miners
(the harnesses do this), or lower genesis_bits for CPU miners (the devnet value 0x1d100000 is the GPU difficulty).
Harnesses: node tools/finality-attacks/run.mjs s3 --fast-time, node tools/harness/run.mjs s3 s4 --fast-time
(or IGNEUM_FAST_TIME=1). Both then take the node from vendor/igneum-node/target-integration/release because the
file carries the PoW schedule fields that only the devnet-v4 line knows; IGNEUMD=... overrides. The in-process
simulator takes the same file: igneum-harness-sim --override-params-file infra/fast-time/override-60x.json.
Proof script: node infra/fast-time/simnet.mjs (3 nodes on ports 29500 and up, igneum-devnet-950, data
/tmp/igneum-fast-time; records the first lock and the first program swap; see "Measured" below).
Miners: igneum-miner follows the epoch length and lead its node reports in every template (pow_epoch), no flag.
The dataset day is not in the template, so a real-hash miner on a fast-time network takes IGNEUM_POW_DAY_MS=1440000
in its environment (the node reads it from the file). The environment variables IGNEUM_POW_EPOCH_BLOCKS,
IGNEUM_POW_EPOCH_LEAD and IGNEUM_POW_DAY_MS remain the fallback for a node without a params file; the file wins.
What scales and what does not
Time parameters, divided by 60 (devnet value, 60x value):
| Parameter | Devnet | 60x | Note |
|---|---|---|---|
finality.weight_window (W2, DAA s) |
7,200 | 120 | the 2-hour weight window becomes 2 minutes |
finality.min_daa (C5, F1, DAA s) |
7,200 | 120 | equal to the window on every network, so the first lock waits for a full window |
finality.equivocation_ban (3.6, DAA s) |
7,200 | 120 | |
finality.aggregator_fallback (DAA s) |
15 | 1 | 15/60 rounds up to the floor of one second |
finality.presence_window (Q1, checkpoints) |
20 | 1 | a count of checkpoints, but it stands for 20 x 30 = 600 DAA s of presence; 10 s is under one checkpoint, so the floor of one |
pow_epoch_blocks (DAA s) |
3,600 | 60 | the hourly program epoch becomes a minute (new in the node, 4 Oct 2026: a consensus param carried by the override file; before, only an environment variable) |
pow_epoch_lead (DAA s) |
600 | 10 | the seed is knowable one lead before the boundary; the miner confirms it a quarter of the lead later (3 DAA s at 60x, 150 on the devnet) |
pow_day_ms (ms) |
86,400,000 | 1,440,000 | the dataset day of the 256 MiB cache becomes 24 minutes (new consensus param, same change) |
blockrate.merge_depth (DAA s) |
3,600 | 60 | Kaspa's merge-depth bound, one hour of DAA score |
blockrate.finality_depth (DAA s) |
43,200 | 720 | Kaspa's pruning-side finality, 12 hours |
blockrate.pruning_depth (DAA s) |
108,000 | 13,838 | 30 hours / 60 = 1,800 is under Kaspa's anticone finalisation bound (finality + 2 x merge + 4 x mergeset x k + 2k + 2 = 13,838 at k 18, mergeset 180), and Bps::pruning_depth takes the larger, so this does |
blockrate.coinbase_maturity (s) |
100 | 2 | 100 s / 60 rounds up |
deflationary_phase_daa_score |
0 | 0 | the devnet has no deflationary phase |
difficulty_v2_activation_daa |
never (18446744073709551615) |
never | a height, not a clock: difficulty rule v2 (4 Oct 2026, docs/analysis/difficulty-2026-10-04-oscillation.md) switches on at this DAA score; a test network sets it in its merged file (sim/difficulty/testnet_v2.py uses 900) |
Unchanged, and why:
| Parameter | Value | Why it stays |
|---|---|---|
blockrate.target_time_per_block |
1,000 ms | the unit: a 60x network still makes one block per second; 60 blocks per second is a different DAG (k, parents, mergeset) and not what is being tested |
blockrate.ghostdag_k, max_block_parents, mergeset_size_limit |
18, 10, 180 | DAG shape at 1 block/s |
finality.checkpoint_interval, checkpoint_depth |
30, 20 | blue-score counts: a checkpoint every 30 blocks, determined 20 blocks later |
finality.dust, aggregators |
5, 8 | a block count and a count of keys |
difficulty_window_size, min_difficulty_window_size, difficulty_sample_rate |
661, 150, 4 | sample counts of Kaspa's sampled DAA (the reference lane of the dual rule); the controller needs its samples to estimate hash rate, and the 120-block LWMA lane, the 8-block warm-up, the 600-block reference start and the per-block clamps (igneum::difficulty) are block counts by design |
past_median_time_window_size, past_median_time_sample_rate, timestamp_deviation_tolerance |
27, 10, 132 | the past-median rule's window in samples; Igneum's own 10 s future and back bounds are constants of the rule |
genesis_bits |
0x1d100000 | the devnet's GPU difficulty; a CPU test network sets its own (0x1f010000 = 2^16 hashes per block) |
skip_proof_of_work |
false | the harnesses turn it on themselves |
mass, lane, script and payload limits, pruning_proof_m, max_block_level, crescendo_activation, storage_mass_parameter, pre_deflationary_phase_base_subsidy |
devnet | sizes and rule switches, not clocks |
Consequences to remember when reading a fast-time result: the merge depth (60 s) is now far under the difficulty
window (2,641 s), so a low-difficulty side chain older than a minute cannot merge, which the devnet tolerates for an
hour; the presence window of one checkpoint means "finality active" needs a lock within the last checkpoint; the
partition bound of spec 3.3.1 is W / (3R) = 13 s at 1 block/s per side (200 s on the devnet), so a split longer than
that locks on both sides as the arithmetic says (docs/bench-log.md, 4 Oct 2026, 6A long heal).
The node unit test fast_time_60x_file_is_the_devnet_at_60x (consensus/core/src/config/params.rs) reads this
file and checks every rule above against DEVNET_PARAMS; override_params_carry_the_pow_schedule and
pow_schedule_defaults_and_clamps check the new fields and that the devnet defaults are untouched.
Measured (4 October 2026, Apple M5 Max shared with four other agents and the live devnet, load 40 to 60)
node infra/fast-time/simnet.mjs --secs 240: three devnet-v4 nodes (target-integration/release/igneumd, built
from devnet-v4 a5ef8b07), three vmine voters sharing 1 block/s, one real-hash CPU miner thread on node 0.
| Event | Wall time from the first node's start | Chain position |
|---|---|---|
| next epoch seed reported in the template | 56.1 s | DAA 53 (boundary 60, lead 10, quarter-lead confirm 3) |
| first hourly program swap: template epoch index 0 to 1 | 65.1 s | DAA 60 |
| CPU miner's new program and 256 MiB cache ready for the new seed | 66.1 s (397 ms compile) | DAA 60 |
| first finality lock | 185.5 s | checkpoint 5, blue score 150, DAA 149 (checkpoint 4 sat at DAA 119, one under min_daa 120, so it could not certify) |
Sinks agreed on all three nodes at the end (DAA 171). On the devnet the same two events come at DAA 3,600 (one hour) and DAA 7,200 plus a checkpoint (two hours and change).
Harness scenarios, same binaries, before and after:
| Run | Devnet profile | 60x profile |
|---|---|---|
tools/finality-attacks s3 (dishonest aggregators, six voters at 6 blocks/s) |
32 min for the seven-scenario catalogue at SCALE 0.6 on the 3 Oct node (bench log 4 Oct 2026, s3 alone 180 s plus bring-up); on today's rule (min_daa = window) the window fills at DAA 7,200 = 20 min at 6 blocks/s, so s3 at full scale shows no lock inside its 300 s |
113 s wall, 16 locks per node, 0 conflicting certificates, lock hashes agree across nodes, median lock latency 1,018 ms: PASS |
tools/harness s3 (partition and heal in the in-process simulator, igneum-harness-sim of the devnet-v4 line) |
983 s wall for the four cuts (120, 600, 1,800 and 3,700 s virtual: 46, 151, 400 and 831 s wall, two at a time), all PASS | 43 s wall for the three cuts (10, 30 and 62 s virtual: 18, 20 and 26 s wall), all PASS, the 62-s cut beyond the 60-s merge depth converged with a 34-block reorg |
The 3 Oct catalogue run reported 3 to 47 s wall per s3 cut on the ordering-layer-only harness branch; the
devnet-v4 simulator carries the execution layer and costs about 70 ms per block on this loaded machine, hence the
983 s. The before and after above are the same binary on the same day.