igneum/sim/difficulty/README.md
igneum-labs e17d3816ba Difficulty: the live oscillation of 4 October, its cause, the DAG replay and rule v2 (reference window 600 behind a height switch)
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>
2026-10-04 13:26:23 +00:00

6.1 KiB

sim/difficulty

Difficulty controller simulator for Igneum (3 October 2026). Model, candidates, results and the devnet record behind docs/analysis/difficulty-2026-10-03.md and section 2.3 of docs/spec/02-consensus.md.

Files

File What
sim.py the simulator: block production from a hash-rate profile through six controllers, metrics as markdown
devnet-2026-10-03.csv every header of the overnight devnet (observer node wRPC, ws://127.0.0.1:28640): hash, DAA score, blue score, timestamp, bits, difficulty (2^255 / target, the node's convention), log2 target, epoch, chain flag, parent count, selected parent
results.md raw simulator output: synthetic set, timestamp-noise set, tuning sweeps, record replay, test-network comparison
testnet-igneum-2026-10-03.csv, testnet-kaspa-2026-10-03.csv every header of the two 3-node CPU test networks (Igneum rule, 20 min; Kaspa rule, 10 min), same columns
records/live-2026-10-04.csv every header of live devnet v4 through 11:18 UTC on 4 October 2026 (observer node wRPC, records/pull_live.py): the columns above plus blue work, mergeset blues and reds, vote key hash
records/live-2026-10-04-hashrate.csv the hash-rate truth of the same hours: every STATUS line of the four GPU workers from the log intake (last-30-s MH/s every 30 s, run id, which node the worker mined through)
testnet_v2.py the 3-node CPU test network of rule v2 and its height switch (ports 29600 and up, /tmp/igneum-diffv2)

docs/analysis/difficulty-2026-10-04-oscillation.md holds the live finding, the DAG replay and rule v2.

Model

Chain model (every synthetic profile and the attacks): one selected chain, DAA score = height, target 1 block per second, solve times exponential with mean (expected hashes per block) / (hash rate). A hash-rate profile is piecewise constant in time (miners joining and leaving) or keyed to the 3,600-block epoch (program cost steps). Timestamps are the true times in milliseconds; --ts-noise N adds uniform +-N ms miner clock jitter.

Metrics per step: first time the 121-block centred mean of the expected block rate is within 10% of target; "settled" = the first time it then stays within 10% for 100 blocks; overshoot past target on the far side; blocks spent above 2x and below 0.5x target rate; worst inter-block gap. Steady state: standard deviation of the expected rate ratio over a steady hour, blocks-per-minute coefficient of variation (Poisson alone gives 0.129 at 60 blocks per minute).

DAG model (--live, 4 October 2026): miners on nodes, each fetching templates from its node with the staleness of igneum-miner's job queue, blocks carrying the parents and the node's stamp of their template, GHOSTDAG with every merged block blue, the rule run exactly as the node runs it (chain walk from the virtual's selected parent, sampled window per mergeset). The hash-rate schedule comes from the records CSV. Fitted once to the record's merge fraction (--dag-delay); compared per UTC window on block rate, DAG width, difficulty band, std of log difficulty, peaks over 15%, blocks per minute, lane flips.

Controllers

Name Rule
kaspa KIP-4 sampled DAA exactly as the fork runs it: samples at every 4th block, 661 samples, average target of the window without its earliest block, times measured over expected, genesis bits held until 150 samples (600 blocks)
monero Monero next_difficulty: 720 blocks, timestamps sorted, 60 cut from each end, lag 15
lwma60, lwma120 Zawy LWMA-1 (2018 reference), linear weights, solvetimes in [-132 s, 6 T]
igneum the Igneum rule of spec 2.3 (two lanes, work-over-time estimators, epoch windows, 3% harden and 10% ease clamps)
igneum-literal the same with the brief's literal trigger (short-window rate off target by 25%), kept to show the chatter
igneum-v2 rule v2 (4 October 2026): the reference lane is the epoch lane over the newest 600 blocks of the epoch; the sampled long lane is not consulted
igneum:key=value+... any variant: ns, trig, trig_exit (hysteresis), soft=a/b (blend), median3, clamp_window (ms), ref_window, long_min, clamp_pct, ease_pct, cap, k0

Runs

python3 sim.py                                   synthetic set, every controller
python3 sim.py --ts-noise 500                    the same with +-500 ms timestamp jitter
python3 sim.py --quick                           CI smoke run: up50 and warmup-hard, kaspa and igneum (about a minute)
python3 sim.py --tune                            parameter sweep for the Igneum rule
python3 sim.py --record devnet-2026-10-03.csv --events 2026-10-03T19:28:05+00:00,2026-10-03T19:42:40+00:00
                                                 exact replay check of Kaspa's rule on the record,
                                                 then the record as a hash-rate profile through every controller
python3 sim.py --dump up50:igneum                per-block trajectory (t ms, height, D, hash rate, rate ratio)
python3 sim.py --live records/live-2026-10-04.csv --hashrate records/live-2026-10-04-hashrate.csv \
               --controllers igneum,igneum-v2 --seed 7 --dag-delay 0.75 --live-end 10:58
                                                 the live devnet replayed through the DAG model against the record
python3 testnet_v2.py --minutes 28 --activation 900 --out /tmp/igneum-diffv2/run1
                                                 3 CPU nodes: a 2x step under v1, the switch at DAA 900, steps under v2

Requirements: Python 3 only (numpy not needed). The full synthetic set takes about two minutes.

Limits

The chain model has no DAG (one chain, no parallel blocks, no reds), no network latency, no miner template refresh delay, honest timestamps unless --ts-noise. The DAG model has no reds, one fitted staleness scale, and the Mac's hash rate as a constant. The exact-replay check of Kaspa's rule matches the record bit for bit while the devnet was a chain and diverges once it widened past one block per second, because a chain-only replay cannot see the merged blocks that the sampled window includes.