diff --git a/docs/design/class-v5-stored-state.md b/docs/design/class-v5-stored-state.md index c3704c653..db5466eca 100644 --- a/docs/design/class-v5-stored-state.md +++ b/docs/design/class-v5-stored-state.md @@ -32,6 +32,9 @@ | 13:2x | The coordinator's correction: the hot-set bound is NOT satisfied by sub-version 1 (F8's re-gate: nine of the first 30 seeds over 1.2x, p31 at 29.3x, 0.19 percent hot-set programs; saturated values pass saturation-preserving writers); v5 inherits the gap by merge and takes sub-version 2 by merge when it lands; the AP-F4-1 and AP-F1-1 rules come in the next round; the lane stops here | | 12:5x to 13:21 | The amended class v4 taken in: ca3-v4-amend 8c728ca3 merged into class-v5 (4e737543) and release-0.3.20-node into class-v5-node (699db5a2); the source rule's class key sets the state flag aside so v5 and its rungs draw under it (a unit test pins the v5 chain draw equal to the amended v4's with no lossy-sourced load, the unamended v3 stream the known-failed case); generator 5 keeps `program_id(5, seed, attempt)` with no sub-version (the hash lane's reading: the suffix separates two streams inside generator 4); class v5 is object byte 6 above the amended v4's 5 (the fork, the harness, the default byte); igneum-pow 67 unit and 39 integration tests on igneum-build-2, the packs test with the re-exported control pack, the flip case PASS again with bytes 6,6,6 (the flip at epoch 8 on 3 of 3, 183 v5 blocks, the stale miner off on 59 of 59, the stateless node stopped at 480, equal roots) | | 10:4x | The Counter ASIC coordinator relays the project lead's ruling of option A on AP-F8-1: the injecting-source load draw lands in class v4 itself on the hash lane's branch ca3-v4-amend (0.3.19); class v5 takes it by merging that branch once its commit is up, not by a second implementation; the page's section 11 row stands as the record of the bound and its gate | +| 19:4x | The flip-stale harness re-run (base 30500) read FAIL on `v5_ids_equal_the_cli_v5_id`, `sinks_agree`, `block_counts_agree`: the fork binaries on build-1 date from 18:56 UK, before AP-F4-1 (19:27) and AP-F1-1 (19:37), so the miners' v5 ids differ from the CLI's at redrawn epochs (9 and 10 differ, 11 equal). Binary skew; the fork rebuilds after (c''') and the case runs again on the matched pair. The harness itself: the 60x file now carries other lanes' override fields (`sig_scheme`, `sig_scheme_activation_daa`, `finality_succession_activation_daa`, `latency_ladder_cache_rung`) that the fork's `OverrideParams` (deny_unknown_fields) refuses; the harness drops the fields the fork source does not name and says so in its log (1c02465c) | +| 19:5x | Main's order through the coordinator: class v5's acceptance carries the fix for the residual hot-set class (adv-accept's seed 100767, section 14), the cheapest fix first (the raised per-site floor) against the per-site hot-item test, chosen by the lower clean rejection rate. (c''') in at ab6f980b: `MIN_DISTINCT_RATIO_V5 = 0.995`, known-failed first on the exemplar, the census test `v5_hot_census` behind the number | +| 20:0x | The v5-fasttime lane's exception, owned by the node lane: `class_signal.rs decide()` with the v5 object set and both floors at 0 resolves the epochs under the window to class v3 regardless of the v4 floor (e0:v3 e1:v3 e2:v4 on the 60x profile); on Devnet 3 (v4 from genesis) enabling v5 would re-read day one as v3. This lane's harness never saw it (its v4 floor is 120, v3 at 60). Gates the crossing object, not the hash side | ## 1. The claim, in one paragraph @@ -274,8 +277,20 @@ Every item of this page that has no code, no test or no measurement yet, with th | Miner cost rows per card (5090, M5 Max, 9070 XT, 4070) | the 4090 row only (rate equal within 0.01 percent, build +0.24 ms); the four named cards owed, each labelled measured with the date | fleet (5090, 4070), this lane (M5 Max under the lock), PC 1 (9070 XT) | 3 | | The exec snapshot wire carrying the day streams (a proof-synced node's trusted-data path) | DONE by the node lane (19:3x UK): fork branch class-v5-node-wire 7737ebd9 on both box mirrors, igneum/exec only, SNAPSHOT_VERSION kept at 1 with `epoch_streams` as the last field and a legacy fallback (a 0.3.23 snapshot loads on a pre-field node and the other way round through the sweep); the writer carries the two newest captures, each checked to rebuild to its root; the loader rebuilds each, compares its root with the loaded state's record at that block, publishes the final one to `ExecState.stream_cache` at once and holds the next until its cut, refuses a tampered or foreign stream and installs the rest; tests `snapshot::tests::the_legacy_shape_is_the_struct_without_the_streams` and `service::class_v5_tests::a_snapshot_carries_the_epoch_streams_and_a_loader_serves_them`, known-failed first; exec suite 43 of 43 on build-2 at 18:33:09Z | node lane | 0 | | The pool protocol's per-epoch state fetch | NO code | pool lane | 2 | -| The day-state witness in the pruning-proof format | IN PROGRESS (the node lane, from 19:3x UK, on class-v5-node-wire): the reference block's hash and state root per epoch beside the epoch seed witnesses (`ProofSeeds::from_witnesses`), checked against the syncing node's snapshot record once it holds one; nothing in igneum-pow changes | node lane | 4 | +| The day-state witness in the pruning-proof format | DONE by the node lane (19:5x UK): class-v5-node-wire 6d827c5d on both mirrors. The witness is the state root after each class v5 epoch's seed block, `EpochSeedHeader.stateRoot = 3` beside the epoch seed header (empty before class v5, so the old wire parses both ways), `PruningProofStateRoots` on the proof metadata; the prover reads each class v5 seed block's root from the installed provider (`DayStateProvider::state_root(block)`, one addition to the kaspa-pow trait, default decodes the stream) or from a witness it took; the verifier's `ProofSeeds::from_witnesses_with_roots` refuses a class v5 epoch whose seed header has no root (the known-failed shape) and a root for an epoch with no header; `check_header` on DayStateUnavailable for a class v5 header accepts it as trusted data when the proof carries the epoch's witness, else refuses naming the missing witness; the importer installs the (seed block, root) pairs through `install_day_state_witnesses`; the executor's `install_epoch_streams` holds a stream for a block it has no record of to that witness (section 5's rule, both conditions). Suites on build-2 on the commit: kaspa-consensus 122, igneum-exec 44, kaspa-p2p-flows 37, kaspa-p2p-lib 20, kaspa-pow 18 | node lane | 0 | +| The acceptance bound on a program's hot-set share (section 14, main's order 19:5x UK) | (c''') at ab6f980b: the per-site distinct-index floor raised to 0.995 under the state flag, known-failed first on seed 100767; the census (clean rejection rate, attempts histogram) running on box 2; the number goes to the coordinator and here when it lands | this lane | 1 | +| The class walk under the v5 object (`class_signal.rs decide()`: epochs under the window resolve to v3 regardless of the v4 floor once v5 is enabled; the v5-fasttime lane's reading 20:0x UK) | OPEN, the node lane's: a known-failed test with v4 from genesis and the v5 object set must read v4 at epoch 0; this lane's harness gains the same case (v4 floor 0, v3 never) once the fork has the fix | node lane (fork), this lane (harness case) | 2 | | The spec text (01 1.8.5 the leaf line, 1.12 the cut, 10 the witness) | NO text | this lane | 2 | | The litepaper paragraph and ledger M35 | done (the litepaper's measured numbers are the 4090's) | this lane | 0 | Sum of the gap: about 40 agent hours, 17 of them this lane's (the two draw rules, the M5 Max rows, the spec text, the gate's v5 checks), the rest the node, worker, pool, fleet and attack-pass lanes'. + +## 14. The acceptance rule's class v5 part (c'''): the residual hot-set class (main's order, 7 October 2026, 19:5x UK) + +The class the in-house attack pass attributed tonight (adv-accept, `docs/analysis/cryptanalysis/report-acceptance-rule.md` on branch adv-accept): a value-level constant from a lineage-fresh writer that neither (a') nor (c'') reaches. The exemplar is class v4 sub-version 3's seed 100767 of the f8 label space (epoch bytes the words of `igneum-attack-f8/program/100767`, era bytes of `igneum-attack-f8/era/100767`, attempt 2, program id 9d68e6286fc817d4, class mx8-era763e5847+sh256x27). It passes every part of the sub-version 3 rule: its load site 6 (instruction 23, source r6, a quarter window) reads a (c'') ratio of 0.9919 on the closed form and 0.9920 on the live day at the rule's own 2^20 sample, 0.012 above the 0.98 floor; live at 2^24 sequential nonces the site sends 3.35 percent of its reads to the top 0.1 percent of items (the multiples of 2^19: `rotl(x * stride, rot)` of a `mad` value that is near zero in a value class; r6 is zero in 0.0126 percent of evaluations, 1,677 reads of index 0 in 10^6 nonces), the top 0.1 percent of items taking 2.05x the window model's share, where the chip model's gate is 1.2x. + +Two fixes were on the table, with the rule that the one with the lower rejection rate on clean seeds wins if it reaches the class: (i) a per-site hot-item test at live scale (the top items' share per site against the window model, which needs a sample far above 2^20: at 2^20 evaluations the hot set is 272,544 word indices in 17,034 items, 0.13 reads per index, so no index-multiplicity statistic reads it; the item-share statistic at 2^20 reads 2.05x only with the 2^24 run's resolution); (ii) the per-site distinct-index floor of (c'') raised from 0.98 to the bottom of the clean seeds' spread. The distinct count is the same statistic as the collision excess the hot set produces: 3.35 percent of a site's reads landing on 0.1 percent of its window's items is about 0.8 percent of its evaluations repeating an index at 2^20, which is exactly the 0.992 the ratio reads, while the model's spread at that sample is 0.0001 (the collision count is near Poisson with mean n^2 / 2W = 8,192 on the quarter window, so a clean site reads 0.9997 to 1.0001; adv-accept's census of sub-version 3 clean seeds reads 0.9960 at the minimum site, 0.9990 at p1, 1.0000 at the median). + +The rule taken: `MIN_DISTINCT_RATIO_V5 = 0.995` (igneum-pow `accept.rs`, ab6f980b), applied by `check_indices_v5` on the ratio pass's own run when the candidate's class has the state flag: `site_index_stats` keeps every site's sorted word indices once (the distinct count, the colliding pairs, the most read index with its count), `distinct_ratio_on` names a site under 0.98 as (c'') first, then `hot_item_pass` names a site under 0.995 as (c''') `Reject::HotItemSite` with the ratio and the most read index. No class v4 verdict moves (the key is the state flag), no new sample is drawn, and the per-candidate cost is the sort (c'') already paid plus one linear pass. The exemplar is refused at attempt 2 under class v5; the class v5 draw moves to the next attempt; the class v4 draw still lands on it. The known-failed test `class_v5_hot_set_rule_known_failed_seed_100767` holds all of that and the genesis draws of both classes clearing the floor. The census test `v5_hot_census` (ignored; run by hand on a box) reads over 4,600 seeds of the f8 label space the class v4 accepted programs' minimum-site spread, the count under floors 0.98, 0.99, 0.995, 0.998 and 0.999, the attempts histogram of the class v4 and class v5 draws and the seeds whose v5 draw lands on another attempt: the clean rejection rate and the retried-draw cost of the number. Its reading goes in this section when it lands. + +What the floor does not reach, named: a hot set whose excess collisions at 2^20 stay under 0.5 percent of a site's evaluations (a top-0.1-percent item share under about 1.3x); adv-accept's lowest-ratio list between 0.995 and 0.998 reads 1.29x to 1.63x live on the f8 gate but no hot set by X_f >= f, and the floor rejects those too (cheap: one more attempt). A chip that holds 0.1 percent of a window's items serves at most the excess the floor allows, 0.5 percent of a site's reads, 1/16 of that over the program: under the 1.067x bound of AP-F8-1 by a wide margin. The per-site hot-item test at live scale stays owed only if the census reads the floor's clean rejection rate over 1 percent.