spec 01: class v5 as 1.4.7 (the class, generator 5, (c''') with the census numbers, the verified last resort, the two draw rules, the activation object and signal, the test vectors) and 1.8.6 (the state leaves: stream, derivation, sample, leaf line, refresh, witness, cost)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
6ffb6c7c5e
commit
c17ce3b287
2 changed files with 64 additions and 1 deletions
|
|
@ -290,7 +290,7 @@ Every item of this page that has no code, no test or no measurement yet, with th
|
|||
| The shadow-block-write residual of section 14 (the three milder concentrations at 1.26x to 1.45x, Devnet 3's first program among them) | OPEN: a draw rule on the shadow block's last write to a load's source (mul, mulhi, sub of the same register), known-failed first on fce15bf61030be57's site 0, with its after-fraction census; not in tonight's object (the chip value is under the AP-F8-1 bound by two orders); adv-mixer-2's AP-F4-1 lever (redraw when the FPGA LUT cost is 205 or less, a 2-adder mul, or all rotations equal; about 22 days per 100 years redrawn) is the second independent statement of the same mechanism, its census owed on the class v5 generator | this lane | 3 |
|
||||
| AP-F4-1 in the agreed form (attack-pass lane and adv-mixer-2 reconciled, 22:0x UK) | DONE in the post-freeze commit: the rule is `A = 64 + sum(w32(MUL_i) - 1)` at most 205 rejects (the median 226; w32 over bit positions 0 to 31, the first census's position-32 carry digit dropped, 231 to 226), any MUL with w32 at most 3 rejects (k >= 1), the eight ROT all equal rejects; a rejected block is redrawn whole from the continuing stream. It replaces "NAF sum under 163 (cost under 211), four distinct rotations". Known-failed first on the day both censuses name, chain day 29,337 (2050-04-28, 1.113x): read on box 2 at 22:4x UK, the sub-version 3 block of that day costs 203 (w32 minimum 8, eight distinct rotations), rejected at 205 and redrawn under class v5. Tail: 5.69e-4 of days, 15 days a century; nothing to a chip or a GPU | this lane | 0 |
|
||||
| Class v5's verified last resort (adv-accept-3's finding aa359962: sub-version 3's `last_resort_v4` fails the rule on 223 of 2,500 seeds, 209 by part (a), and is handed to the chain unchecked; unreachable at 4.6e-44 per epoch) | DONE in the post-freeze commit: `last_resort_v5` = the rewrite, then `repair_stale_loads` (a stale load re-sourced to the lowest register written since its last load, walked to a fixpoint), then the whole rule, over a 256-candidate scan from the cap; the unchecked fallback past the scan sits under 1e-300. Known-failed first on adv3/steer/2 (`class_v5_last_resort_is_verified_known_failed_adv3_steer_2`): read on box 2 at 22:4x UK, the sub-version 3 rewrite fails part (a) at instruction 47 reading r3; the repair restores (a) moving only load sources; class v5's last resort passes the whole rule at attempt 256 (the first scanned candidate), id 9b29c9481f6941d4; steer seeds 11, 33, 56, 58, 77 (adv-accept-3's other part-(a) rows) pass too. The record: sub-version 3's last resort is unreachable and unverified (the hash lane's AP-F8-1 entry); class v5's is verified by that test | this lane | 0 |
|
||||
| The spec text (01 1.8.5 the leaf line, 1.12 the cut, 10 the witness) | NO text | this lane | 2 |
|
||||
| The spec text | DONE (23:3x UK): `docs/spec/01-lottery-hash.md` 1.4.7 (class v5: load class, generator 5 and the id without the sub suffix; (c''') with the 0.995 floor and the census numbers; the verified last resort; the two draw rules in their agreed forms; the activation object byte 6, the seven-window 95 percent signal, the floor and the refusal; the test vectors: the three pinned packs, seed 100767, day 29,337, adv3/steer/2) and 1.8.6 (the state stream IGSD1, the leaf derivation, the keyed sample, the leaf line before M_0, the pack's leaves, the per-epoch refresh and the witness, the measured cost). The acceptance-rule spec rewrite lane holds 1.4.3, 1.4.6 and 1.13 (its branch spec-accept-23); 1.8.5's one-line pointer to 1.8.6 is theirs. Section 10's witness paragraph stays owed to the node lane's text | this lane | 0 |
|
||||
| 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'.
|
||||
|
|
|
|||
|
|
@ -194,6 +194,44 @@ Test vectors for the rule (`igneum-pow accept --seed ...`):
|
|||
|
||||
The Rust crate and the Swift prototype derive identical instruction lists and identical 96-vector sets on all five seeds (`docs/bench-log.md`, 4 October 2026).
|
||||
|
||||
### 1.4.7 Program class v5: the dataset as chain state
|
||||
|
||||
Implemented (`igneum-pow/src/state.rs`, `memhard.rs`, `generator.rs`, `accept.rs`, `emit.rs`; the node side in the fork's `consensus/core/src/igneum.rs`, `consensus/pow/src/igneum.rs` and `igneum/exec`; design `docs/design/class-v5-stored-state.md`; frozen object class-v5 1c420786, 7 October 2026, with the post-freeze line 8ca66afa). Class v5 is class v4 (section 1.4.3, the shadow block and the load-source rule; section 1.4.6, the acceptance rule) with one change to the dataset and one change to the acceptance: every dataset item is keyed by a leaf of the chain's execution state (section 1.8.6), and the per-site distinct-index floor of part (c'') is raised (1.4.7.2). The program draw is class v4's draw for draw, with the two class v5 draw rules of 1.4.7.3 applied inside the stream. Nothing of the hash's per-nonce work changes: the kernel text of a class v5 program is the class v4 text over a dataset built from other words; the only new work is one 64-byte read per item at dataset build time.
|
||||
|
||||
#### 1.4.7.1 Load class, generator version and program id
|
||||
|
||||
The load class is `mx8+sh256x27+state` (`V5_CLASS = LoadClass { state: true, ..V4_CLASS }`); with an era seed it reads `mx8-era<8 hex>+sh256x27+state` (section 1.13.1's draw inside it, as class v4's). The generator version is 5. The program id is `FNV-1a-64("igneum-program/" || 5_le32 || seed words as little-endian bytes || attempt_le32)` with NO "sub/" suffix (the suffix of section 1.4.6 is class v4's sub-version mark; class v5 has no sub-versions: a rule change is a new class). The state flag changes the id and the dataset, never the base program or the shadow block: for a seed whose draw the class v5 rules do not move, the class v5 program's instruction list equals the class v4 program's of the same seed and attempt.
|
||||
|
||||
#### 1.4.7.2 Acceptance under class v5: part (c''')
|
||||
|
||||
A class v5 candidate must pass every part of section 1.4.6 and then (c'''): on the same run as part (c'') (4,096 units x 32 lanes x 8 iterations = 2^20 evaluations per load site on the seed's acceptance stream, the closed form at 2^28 words), every load site's distinct-word-index ratio against the window model (`distinct / (N - N^2 / 2W)`, `W` the site's window in words, the form of part (c'')) must be at least `MIN_DISTINCT_RATIO_V5 = 0.995`. A site under 0.98 is named by part (c'') first; a site at or above 0.98 and under 0.995 is refused by (c''') (`Reject::HotItemSite`, naming the site, its distinct count, the ratio and the most-read index with its count). The test is keyed on the state flag: no class v4 verdict moves.
|
||||
|
||||
Why 0.995 (the record is `docs/design/class-v5-stored-state.md` section 14 and ledger AP-F8-1): the in-house pass found class v4 sub-version 3 programs that pass every part of 1.4.6 and read a live hot set (the exemplar, seed 100767 of the f8 label space, program `9d68e6286fc817d4`: 3.35 percent of one site's reads on the top 0.1 percent of items, from a `mad` value that is near zero in a value class, mapped by the era stride onto the multiples of 2^19); at the acceptance's own sample that site reads 0.9919, where the window model's spread is 0.0001 and the clean median 0.9999. The census over 4,600 seeds reads 2.435 percent of class v4 accepted programs under 0.995 (their class v5 draw lands on a later attempt; the attempts mean goes 2.174 to 2.248), none under 0.98, and 0 class v5 accepted programs under the floor; every live hot set the pass measured (eight, 0.9821 to 0.9919) is under the floor, and the floor misses the shadow-block-written concentrations at 0.9992 to 0.9997 (worth about 1.0004x to a chip), which no floor reaches without refusing most clean programs. 0.99 would pass the exemplar.
|
||||
|
||||
Attempts, the cap and the last resort are section 1.4.6's with one difference: class v5's last resort is verified. After `MAX_ATTEMPTS_V4 = 256` refused candidates, from attempt 256 on each candidate is rewritten as class v4's last resort is (every `or`, `mul` and `mulhi` to `xor`), its stale loads re-sourced to the lowest register written since the register's last load, walked to a fixpoint (part (a) by construction; only load sources move), and the result run through the whole rule; the first that passes is the program (`last_resort_v5`, scan of 256 candidates). Past the scan the first repaired candidate stands as drawn (the draw is total); that fallback sits under 1e-300 per epoch seed. Class v4 sub-version 3's last resort is unreachable (4.6e-44 per epoch seed) and unverified against the rule (ledger AP-F8-3); class v5's is verified by `class_v5_last_resort_is_verified_known_failed_adv3_steer_2`.
|
||||
|
||||
#### 1.4.7.3 The two class v5 draw rules
|
||||
|
||||
Both live inside the stream (the next stream values are taken; no value is skipped, so the derivation is consumed exactly once), and both are keyed on the state flag.
|
||||
|
||||
AP-F4-1, the mixer block (section 1.8.4's `ROT[0..7]`, `MUL[0..15]`, `RC[0..15]` of the day): after the forty draws, the block is rejected and the forty drawn again from the continuing stream when its adder cost `A = 64 + sum over the 16 multipliers of (w32(MUL_i) - 1)` is at most 205, or any multiplier has `w32` at most 3, or the eight rotation amounts are all equal; `w32(m)` is the non-adjacent-form weight of `m` over bit positions 0 to 31 (the carry digit at position 32, which a 32-bit multiplier never pays, is not counted). The redraw cap is 64 (never reached; the rule fires on about 5.69e-4 of days, 15 days a century; the worst calendar day of the census, chain day 29,337 = 2050-04-28, reads cost 203 and is redrawn). The median cost of a drawn block is 226, so the rule holds a per-day LUT-recompute FPGA under 1.1x of the median on every day.
|
||||
|
||||
AP-F1-1, the shadow block (section 1.4.3): after the 256 shadow instructions are drawn, the block is redrawn from the continuing stream when the count of instructions an honest compiler removes (an `xor` that cancels the same `xor` on the same source, an idempotent repeated `or`, two rotates of one register by one amount, an `add` followed by the `sub` of the same source and immediate, each counted with no intervening write to the destination or source) exceeds 3.0 percent of the block (`SHADOW_REMOVABLE_MAX_PERMILLE = 30`); the cap is 64 redraws. Over 6,000 first draws the maximum removable share read 11 permille and no block was redrawn.
|
||||
|
||||
#### 1.4.7.4 Activation: the object, the signal and the floor
|
||||
|
||||
Class v5 is a program-class step like class v4's (section 2's class signal): the header version's object byte (bits 8 to 13 of the 16-bit version) carries each miner's signal; class v5's byte is 6 (`CLASS_SIGNAL_V5`), counted EXACTLY (a block signalling 7, class v4 sub-version 3, does not count for v5). The chain steps the program class to v5 for epoch `e + 1` when every one of the newest seven windows of `program_class_v4_signal_window_daa` DAA seconds before epoch `e`'s seed block is full and each reads a v5 share of at least 95.00 percent (9,500 bps) of its blocks, or when the epoch's seed block's DAA score is at or past the floor `program_class_v5_activation_daa`; the class moves one step per epoch (v3 to v4 to v5), never back, and an epoch whose seed block lies under the first window takes the floor's class, never a bare v3 (the fork's `unfull_window_base`, with the known-failed test of the class walk). The floor is a genesis parameter at `never` (`u64::MAX`) until set; the chain digest includes it only when set (the 0.3.15 rule), so a network without the object has class v4's digest byte for byte. The class v5 pack of an epoch is the class v4 pack of the same seeds plus the epoch's state leaves (1.8.6); a node that cannot build the leaves (no executor, or the executor has not passed the epoch's cut) refuses to serve a class v5 template (`PowEngineError::DayStateUnavailable`, retryable) rather than mine a program it cannot verify, and a miner that keeps a stale stream across the refresh has its blocks refused from the next epoch on (the proof-of-following known-failed case, `infra/fast-time/class-v5-signal.mjs --stale`).
|
||||
|
||||
#### 1.4.7.5 Test vectors
|
||||
|
||||
| Pack (`proto-cuda/packs-ca3-v5/`) | Seed | Generator, attempt | Program id | State root, leaves | Fingerprint of the 2^24 outputs at base 0 |
|
||||
|---|---|---|---|---|---|
|
||||
| `v4-genesis` | `igneum-genesis` | 4, 0 | `a217c7f698880830` | none (class v4 control) | |
|
||||
| `v5-genesis` | `igneum-genesis` | 5, 0 | `7c54302b487340a1` | `1c583d352bb9c75a...` (node 1's exec snapshot, block 159,357), 93 leaves, FNV-1a 64 `850ad094a937a5c5` | |
|
||||
| `v5-dn3-epoch0` | `igneum-epoch/4020cb43...b925/day/69676e65756d2d6461792ffd50000000000000` (Devnet 3's genesis as epoch and era seed) | 5, 0 | `e5a4ac5978462156` | `7e37a9fb19b154d3...a311` (Devnet 3's genesis state), 11 leaves, FNV-1a 64 `f141bfee2a8b11b0` | `82b19cbde8557ea5` (Metal, Apple OpenCL, CUDA on an RTX 4090; lane 0 at base 0 `e552166a03298f7f`) |
|
||||
|
||||
The acceptance vectors of (c'''): 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`) is accepted under class v4 at attempt 2 (`9d68e6286fc817d4`, site 6 at 0.9919) and refused under class v5 at attempt 2 by (c''') naming site 6 at 0.991; the class v5 draw lands on attempt 4 (`734fbb8e3e4cd20f`); the genesis draws of both classes read 0.9990 at their minimum site. The day vector of AP-F4-1: chain day 29,337 under class v4 draws a mixer block of cost 203 and class v5 redraws it. The last-resort vector: seed adv3/steer/2 (`igneum-adv-accept-3/steer/2`, Devnet 3's genesis as the era) at attempt 256 fails part (a) at instruction 47 reading r3 under the class v4 rewrite; class v5's last resort passes at attempt 256 (`9b29c9481f6941d4`). The crate's tests hold every row (`tests/packs.rs`, `accept::tests`, `memhard::tests`, `generator::tests`).
|
||||
|
||||
## 1.5 Fixed memory footprint
|
||||
|
||||
Implemented for the prototype size; Designed for genesis.
|
||||
|
|
@ -366,6 +404,31 @@ dataset[3093825] = e3922dca dataset[267580473] = 26b5f1d8
|
|||
|
||||
The cache fingerprint has been reproduced by: the Swift CPU fill, the Rust fill, the Metal GPU fill, the clang emulation of the CUDA fill, the RTX 5090 (CUDA and NVIDIA OpenCL), Apple OpenCL, pocl, and the AMD gfx1036 (`docs/bench-log.md`, the five entries dated 3 October 2026 for memory-hard, igneum-pow, proto-opencl, gfx1036 and NVIDIA OpenCL).
|
||||
|
||||
### 1.8.6 The state leaves (class v5)
|
||||
|
||||
Implemented (`igneum-pow/src/state.rs`, `blake2b.rs`, `memhard.rs`; the fork's `igneum/exec/src/day_stream.rs` and `igneum_getPowStateLeaves`). Under class v5 every dataset item is keyed by a leaf of the chain's execution state, so a dataset can be built only by a party holding that state at the epoch's cut: the "proof of stored state" of the design document's scheme C, widened to proof of following (the leaves refresh every epoch).
|
||||
|
||||
The state stream. At each epoch's seed block the executor emits the canonical day stream of the state after that block: the account, storage and code records in canonical order, which rebuilds to the state root (`DayStream::check`). The file form every side reads without a serialisation library (`StateStream`, magic `IGSD1\0`): the chain block number (le64) and hash (32 bytes), the state root (32 bytes), the record count (le32), then each record as its length (le32) and bytes. The stream of an epoch is served by the node's executor RPC `igneum_getPowStateLeaves [blockHashHex]` and by the miner's `--exec-rpc`; the day-stream tool writes it from a snapshot (`igneum-day-stream --genesis <hash>` for the genesis state).
|
||||
|
||||
The leaves. With `root` the state root and `record_i` the i-th record of the stream (`n` records):
|
||||
|
||||
```
|
||||
D[i] = Blake2b-512("igneum-sd1/" || root || i_le32 || record_i) 16 little-endian words, i in 0..n
|
||||
```
|
||||
|
||||
When the stream holds more records than the dataset has items, the records are ordered by `Blake2b-256("igneum-sd1-sample/" || root || record)` and the first `items` are taken (the keyed sample), so a party holding a strict subset of the state cannot choose which records fall in. Blake2b is RFC 7693's, keyed by nothing; the tag strings are the ASCII bytes shown.
|
||||
|
||||
The leaf line. Item `t` of section 1.8.5 begins, under class v5, with its 16 initial words XORed with the leaf before the first mixer:
|
||||
|
||||
```
|
||||
leaf(t) = D[t mod n]
|
||||
s[i] = s[i] XOR leaf(t)[i] for i in 0..15, before M_0
|
||||
```
|
||||
|
||||
and continues as 1.8.5 writes. Every item carries a leaf (the first form of the design keyed only the first `n` items and let a stateless hasher be right on 92.6 percent of items; the known-failed test `a_stateless_hasher_is_wrong_on_every_item` holds the amended form). The cache (1.8.3), the mixer (1.8.4), the table layout and the working-set window (1.13.1) are class v4's; a kernel takes the leaves as one more buffer (`leaves, nLeaves` on `igneum_build` in the CUDA, OpenCL and Metal texts) and reads one 64-byte leaf per item at build time. The pack carries the leaves as `leaves.bin` with their count, FNV-1a 64, the state root and the block in `program.json`'s `state` object and in the kernel's `IGNEUM_STATE_*` defines (`packcheck`: a generator 5 pack without `IGNEUM_STATE_ROOT_HEX` is refused; another class carrying leaves is refused).
|
||||
|
||||
The refresh (proof of following). The leaves of epoch `e` are derived from the state after epoch `e`'s seed block, so the dataset is rebuilt every epoch from the chain's own state at that cut (the refresh cadence is the epoch; the design names one hour and one epoch as the candidates and the epoch is the one shipped). A miner must therefore hold a following executor, or ask a node's RPC for each epoch's stream; a pool can serve its workers the leaves (64 bytes per item, the dataset's size per refresh) but cannot serve a dataset to a party that does not fetch it each epoch. The verifier on a node without the state (a node synced from a pruning proof) holds the state root per class v5 epoch as a witness beside the epoch seed header in the proof (`EpochSeedHeader.stateRoot`, section 10) and trusts the headers under it as trusted data until its own executor passes the cut. Measured cost (design section 7): dataset build +0.24 ms on an RTX 4090, the verifier +0.20 ms per warp on one core (+0.28 cold), hash rate equal within 0.02 percent, no change in watts.
|
||||
|
||||
## 1.9 The 32-lane unit of work
|
||||
|
||||
Definition. The hash is defined over an aligned group of 32 consecutive nonces `g .. g + 31` with `g AND 31 = 0` (wrapping modulo 2^32 at the top of the range). `shfl` exchanges registers within that group: lane `l` reads from lane `l XOR mask`, `mask < 32`, so the exchange never leaves the group. The verification unit is the group: `hash(n)` is computed by evaluating the group `n AND ~31` and taking lane `n AND 31` (`verify.rs`, `Epoch::hash`).
|
||||
|
|
|
|||
Loading…
Reference in a new issue