class v5 design page, section 2a proof of following: the leaves refresh every window (pow_state_window_daa, 3,600 on the devnet = the epoch, so the reference block is the epoch's seed block and the refresh rides the existing program swap with no dip), the state after it, uncertified by rule so a finality pause never stops mining, how a miner learns it (stateBlock in the template, the leaves from its node), what a pool can and cannot centralise, the farm attack bounded honestly (anything derivable is shippable: the floor is the state size, 6 KB today; the WAN line is about 700,000 state records for 10,000 members at 1 Gbit/s; a LAN farm is never bounded below the 2 GiB cap), the cost per refresh (32 ms per hour on a 4090), the hostile review
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
c389f7fccc
commit
3a675ae62e
1 changed files with 66 additions and 2 deletions
|
|
@ -9,7 +9,8 @@
|
|||
| 08:05 | Lane started. Read: CLAUDE.md, new-pow.md whole, `proto-newpow/state-dataset` (README, sd.h), the latency ladder page, igneum-pow (generator, memhard, verify, emit, main), the fork's class signal (`consensus/core/src/igneum.rs`, `processes/class_signal.rs`, the header processor's seed path), the engine (`consensus/pow/src/igneum.rs`), the exec layer (state, snapshot, service, records), the miner's day handling, the class v4 signal harness, the ladder harness and bench script, spec 01 1.8.5 and 1.12, spec 04 4.3 |
|
||||
| 08:20 | Fleet agent asked for one quiet 4090; answered: a fresh RunPod 4090 pod from 10:00 UK to 12:00 UK |
|
||||
| 08:33 | The canonical day stream written in the fork (`igneum/exec/src/day_stream.rs`) and run against node 1's exec snapshot on igneum-build-1: 93 records, 8,619 bytes, root recomputed and equal (section 7) |
|
||||
| 08:5x | This page |
|
||||
| 08:36 | This page committed (ad25d2c3); the fork's day stream (f32641d1) |
|
||||
| 08:4x | The coordinator relays the project lead's widening to proof of following; section 2a written (the refresh per window from the epoch's seed block, the pool and farm bound with the numbers: the floor is the state size, 6 KB today, 45 MB per member per hour is the WAN line at 10,000 members, a LAN farm is never bounded) |
|
||||
|
||||
## 1. The claim, in one paragraph
|
||||
|
||||
|
|
@ -32,7 +33,70 @@ Under class v5 the day's dataset is built from the execution state at a referenc
|
|||
| The program | class v4's draw with generator 5 stamped (`GENERATOR_VERSION_V5`, `V5_CLASS = { state: true, ..V4_CLASS }`, `program_id(5, seed, attempt)`), the ladder's rung riding as it does for v4 (`v5_class_at(reps)`) | a v5 pack and a v4 pack of one seed never share an id, so a worker on the wrong class refuses the pair (the id check of M28) |
|
||||
| The kernel text | unchanged for the hash; the build kernel gains a leaf buffer: `igneum_build(ds, cache, leaves, nLeaves, nItems)` with `mh_item_sd(cache, leaves + 16 (t mod nLeaves), t, s)` in the three dialects | the prototype measured +1.43 ms for the one coalesced 64-byte read per item with a 1 GiB leaf array; at today's 6 KB of leaves the read is cache-resident and cheaper |
|
||||
|
||||
What moves with the day: at `cut(d)` every node's executor captures the day's state; from then on `D` for day `d` is computable, and the miner's existing one-lead-ahead prepare of the next day's pair (`igneum-miner`, `day_eta_s <= epoch_lead`: 10 minutes before the day on the devnet) builds the next dataset with it. The switch at the boundary is the day switch the miner already makes (a `prepare`d pair swaps with no pause); the build itself is 32 ms on a 4090 (31.98 ms measured, new-pow 5.2) against the 1.8 ms cache fill, so even an unprepared worker loses under 50 ms of hashing at the boundary. No hash-rate dip by construction: the state is known 60 minutes before the day, the pair 10 minutes before.
|
||||
Under proof of following (section 2a) the day keys the cache alone and the leaves refresh per window from `C_w`; the paragraph below describes the daily rule as first designed, which 2a replaces for the leaves. What moves with the day: at `cut(d)` every node's executor captures the day's state; from then on `D` for day `d` is computable, and the miner's existing one-lead-ahead prepare of the next day's pair (`igneum-miner`, `day_eta_s <= epoch_lead`: 10 minutes before the day on the devnet) builds the next dataset with it. The switch at the boundary is the day switch the miner already makes (a `prepare`d pair swaps with no pause); the build itself is 32 ms on a 4090 (31.98 ms measured, new-pow 5.2) against the 1.8 ms cache fill, so even an unprepared worker loses under 50 ms of hashing at the boundary. No hash-rate dip by construction: the state is known 60 minutes before the day, the pair 10 minutes before.
|
||||
|
||||
## 2a. Proof of following (the project lead's widening, 7 October 2026, 08:4x UK, through the coordinator)
|
||||
|
||||
The daily dataset of section 2 proves the miner holds the chain once a day. Proof of following makes the leaves refresh every WINDOW from the chain's latest reference block, so a miner must keep executing and following the chain to keep mining: a machine that stops following builds the previous window's dataset at the refresh and is wrong on every hash from then on.
|
||||
|
||||
### 2a.1 The refresh rule
|
||||
|
||||
| Item | Rule | Why |
|
||||
|---|---|---|
|
||||
| The window | `Params::pow_state_window_daa`, a genesis parameter in DAA seconds; 3,600 on the devnet (one hour, which at the base epoch length is one epoch; the two candidates coincide there and the parameter keeps the refresh where it is when the epoch ladder of spec 01 1.12 moves `epoch_len`); 60 on the fast-time profile; in the digest once `program_class_v5_activation_daa` is set (the 0.3.15 rule) | a refresh per hour is 32 ms of rebuild per hour on a 4090 (section 2a.3), so the cadence costs nothing; one epoch is the natural unit because the miner already swaps programs there |
|
||||
| The window index | `w = daa_score / W` (the block's own DAA score, as the epoch index is) | a block's window is a function of its header |
|
||||
| The reference block `C_w` | the last selected-chain block below DAA score `W x w - W / 6`, walking down from the header's selected parent; genesis when none. With `W = epoch_blocks` and the devnet lead of 600 this IS the epoch's seed block (`HeaderProcessor::epoch_seed`), so the node pays no second walk and the state's block is the block the epoch seed is already taken from | a function of the header's own past; the lead (`W / 6`, 10 minutes at the devnet window) is the epoch seed's lead and the miner's existing prepare horizon |
|
||||
| The state | the execution state after `C_w`: `R_w = records[C_w].state_root`, the stream `DayStream` of section 2 taken at `C_w` | the executor's ring already clones the state per chain block; the capture keeps the one at `C_w` for the window and the next |
|
||||
| Finalised or not | the selected-chain block at the cut, certified or not; in practice the lock lands within a minute of `C_w`, ten minutes before the window starts, so the block is finalised when mined on; by rule mining never waits on finality (spec 04 4.3, section 3.7 item 2) | "the latest finalised checkpoint" as a RULE couples mining to finality liveness: a pause longer than the lead would stop every miner, which the 6 October pause (18:42Z, 20 minutes) would have done; the cut rule gives the same block in every normal hour and keeps mining through a pause |
|
||||
| The leaves | `D_w[i] = Blake2b-512("igneum-sd1/" || R_w || i_le32 || record_i)` over the stream at `C_w`; `leaf(t) = D_w[t mod n]`; the sample rule of section 2 above the dataset size | as section 2, keyed by the window's root |
|
||||
| The dataset of `(day d, window w)` | the day's 256 MiB cache (the day key, unchanged) with `D_w` folded into every item: a rebuild of the dataset per window, never of the cache | the cache is a function of the day key alone and stays; the build is the measured 32 ms on a 4090 (section 2a.3) |
|
||||
| How a miner learns it | the template's `powEpoch` carries `stateBlock` (the hash of `C_w`) and `nextStateBlock` once the next window's cut has passed (one lead before the boundary, with `nextEpochSeed`); the miner fetches the stream for that block from its node (`igneum_getPowStateLeaves [block hash]`), checks nothing when the node is its own (its node executed it), and prepares the next pair; the pack carries `IGNEUM_STATE_BLOCK_HEX`, `IGNEUM_STATE_ROOT_HEX` and `leaves.bin` | the existing next-epoch prepare: with `W = epoch_blocks` the refresh and the program swap are one swap |
|
||||
| The grace at the boundary | none in validation (a block's window is its DAA score's; a block mined with the previous window's leaves after the boundary is invalid); the grace is the lead: the next window's leaves are knowable ten minutes before it, the miner prepares the pair then, and a prepared worker swaps with no pause (the 2.0 hot-swap path). An unprepared worker rebuilds at the boundary: 32 ms of hashing lost on a 4090, under 0.001 percent of the window | no hash-rate dip by construction, as the epoch swap has none today |
|
||||
| A miner whose node is behind | the node serves no template for a window whose `C_w` it has not executed (section 5's refusal), so the miner's hash stops at the boundary rather than mining invalid blocks | a node ten minutes behind the chain is already a node without a useful template |
|
||||
|
||||
### 2a.2 What a pool can and cannot centralise, and the farm attack, with the numbers
|
||||
|
||||
The attack: a farm rents one node for ten thousand cards, rebuilds the dataset once per window on that node and ships it. The bound the design can give, honestly:
|
||||
|
||||
| Fact | Number | Source |
|
||||
|---|---|---|
|
||||
| The dataset the cards hash over | 1 GiB (2^24 items; 2 GiB designed) | spec 01 1.13.3 |
|
||||
| What a card needs to rebuild it itself | the day key (32 B, public) and the window's leaves `D_w` (64 B x min(n, items)) | section 2 |
|
||||
| `D_w` at today's devnet state | 5,952 B (93 records) | igneum-build-1, 08:33 UK |
|
||||
| `D_w` at a mainnet state ten times larger | 60 KB | arithmetic |
|
||||
| `D_w` at the sample cap | 1 GiB at the 1 GiB dataset, 2 GiB at 2 GiB | section 2 |
|
||||
| Shipping the built DATASET to 10,000 cards per hour | 1 GiB x 10,000 / 3,600 s = 2.98 GB/s (24 Gbit/s); 2 GiB: 5.96 GB/s (48 Gbit/s) | arithmetic |
|
||||
| Shipping the LEAVES instead, today | 5,952 B x 10,000 / 3,600 s = 16.5 KB/s | arithmetic |
|
||||
| Where the leaves become too large to ship inside the window over a WAN pool link of 1 Gbit/s to 10,000 members | 1 Gbit/s x 3,600 s / 10,000 = 45 MB per member per window, which is `n` above about 700,000 records (7,500x today's devnet state; approximate) | arithmetic |
|
||||
| Over a farm's LAN at 100 Gbit/s | 4.5 GB per member per window: never, under the 2 GiB cap | arithmetic |
|
||||
|
||||
So the design target "a dataset too large to ship inside the window" is NOT reachable by construction while the state is small, and this page says so rather than claiming it: everything the lottery derives is derived from a seed and the state, so the only bytes a central node cannot compress away are the state's, and the state is the floor. A farm with one node ships `D_w` (6 KB today) to its cards and the chain cannot tell a card that derived the leaves from a card that received them, exactly as it cannot tell a pool member from a solo miner today. What proof of following does force, per machine and per window: holding the current window's state (or its leaves), a rebuild from it, and knowledge of the chain's reference block inside the lead; a machine cut off from the chain for one window stops producing valid blocks at the next refresh, where under the daily rule it kept mining until midnight. The bound bites on the WAN above about 700,000 state records (the leaves pass 45 MB per member per hour at 1 Gbit/s for 10,000 members) and on a LAN never; a used chain crosses the WAN line early (a chain with a million accounts does), a young chain does not.
|
||||
|
||||
What a pool can centralise: templates (as today), the day key (public), the stream or the leaves (a 6 KB to 2 GiB delivery per member per window), the built dataset (1 to 2 GiB per member per window). What it cannot: the rebuild (per machine, 32 ms on a 4090 per window) and the clock (a member that misses a window's delivery is wrong on every hash until it gets it). What the rule "members must hold and refresh the state themselves; the pool serves templates only" means in practice: it is a rule a pool can state and a member can follow, and nothing on the chain enforces it below the WAN line; above the line the link enforces it. The pool protocol (spec 09) gains the per-window `state-leaves` message and the page records that a pool serving leaves is serving the state, which is the design's "every mining operation holds the chain", not every card.
|
||||
|
||||
### 2a.3 The cost per refresh
|
||||
|
||||
| Machine | The rebuild (the dataset from the cache and the leaves) | The leaf pass (`n` Blake2b-512) | The stream check (`to_db` and the root, only when the leaves are not from the machine's own node) | Per window at `W` = one hour |
|
||||
|---|---|---|---|---|
|
||||
| RTX 4090 | 31.98 ms resident (the prototype's second-pass build, new-pow 5.2), about 76 ms chunked over PCIe at the 1 GiB leaf array; at today's 6 KB of leaves the resident figure, cache-resident reads | 93 hashes, under 0.1 ms on the host | 0.1 ms at today's state | 0.0009 percent of the hour (32 ms) |
|
||||
| RTX 5090 | 13.4 ms for 1 GiB (the memory-hard build, `docs/bench-log.md`) plus the leaf read, approximate 15 ms | the same | the same | 0.0004 percent |
|
||||
| A laptop (M5 Max, Metal) | the 1 GiB build on the M5 Max is the GPU item build of spec 01 1.12, 13 to 30 ms class, approximate until measured under the measure lock | the same | the same | under 0.001 percent |
|
||||
| A CPU miner (the harness's `--engine igneum-pow`) | nothing: the CPU verifier derives items lazily; the refresh is the leaves in RAM (6 KB) | the same | the same | nothing |
|
||||
| A node (the verifier) | nothing: lazy derivation; the leaf array swaps per window | one pass per window | its own execution | one capture per window from the ring |
|
||||
|
||||
### 2a.4 Hostile review of the refresh
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| A miner that stops following | the known-failed case (section 4): it builds the window's dataset from the previous window's leaves, every item is wrong, every block is rejected from the first block of the new window; the harness runs a miner frozen on its first leaves and counts its accepted blocks after the first refresh (0) |
|
||||
| A miner that follows with a one-window delay | the same: a window's blocks need that window's leaves; there is no grace in validation |
|
||||
| The lead is too short for a slow node | the lead is ten minutes at the devnet window, the epoch seed's lead; a node that executes ten minutes behind the chain cannot serve a template today either |
|
||||
| A reorg across `C_w` | 600 DAA deep, the epoch seed's own exposure (spec 04 4.3 item 4, O-4.3), and with `W = epoch_blocks` the same block: a reorg that moves the epoch seed moves the leaves with it; every miner prepares again |
|
||||
| A finality pause | no coupling (2a.1); the reference block is the selected-chain block at the cut |
|
||||
| The window parameter moved by a file | a genesis parameter in the digest once v5 is set; a later move is a class change under the 95 percent rule |
|
||||
| The farm with one node | bounded by the state size on the WAN and unbounded on the LAN, with the numbers of 2a.2; stated, not hidden |
|
||||
| Timestamp or DAA grinding at the cut | the cut is a DAA score, as the epoch seed's; a producer chooses at most whether its own block is `C_w` (one bit between two honest states) |
|
||||
| Two windows of leaves in RAM on the node and the worker | the current and the next: 12 KB today, at most 4 GiB at the sample cap; the worker frees the leaves after the build (1,803 MiB resident while hashing, measured) |
|
||||
|
||||
## 3. A miner with a pruned node, and what a chip must hold
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue