66 KiB
Report: exhaustion and steering of the program draw (lane adv-accept-3)
internal adversarial pass, not an independent review
- Target commit:
017e703764(class v4 sub-version 3, object byte 7). Worktree HEAD45845b09at the start (build/master, 19:51 BST);git diff --quiet 017e7037 HEAD -- igneum-powprints IDENTICAL. - Harness: tools/attack/adv-accept-3 (crate adv-accept-3, bin
adv3), igneum-pow by path, nothing in the library modified. Binaries built on build-2 through tools/build-remote.sh: 35336b84039b76538916c5c2a715aefd06ecc5b215d100bedfa9215b750eaa5b (first build, 20:05 BST: idcheck, margins, exhaust-mirror, ids, the first static sweep, the plant run), 928272e0a4f426a33380612bbcf5cde52017765f5185fc73a4396d5581105785 (20:09 BST, generator 4 stamped on every candidate: every queued sweep), 60613d0c4412401d1bed6dd55a831eb42ffc6b1524c36d79500766b7515ef212 (20:13 BST, the multiply columns: the static sweep v2), 7e96fcca98c9cdb256b2dcb4095c84c6ded785911be32856b1c4560d375d794f (21:44 BST, theratioscommand: sweep 982). The f8 copy (census code untouched, a seed mode added): attack-f8 918f80a5fee5d2c349c90794b825a731ff16084c833c2f76f810a0e9ff5b0d80 (built on build-1, 21:28 BST). Pinned copies under /srv/builds/_adv-accept-3/bin/ on both boxes. - Boxes: build-1 and build-2, nice 10, cores 8 to 95. Until 20:22 BST one sweep per box under a flock (the coordinator's rule of 19:55 BST); from 20:22 BST every run through
/srv/builds/_bin/lease pool(main's rule), from 20:43 BST at 32 threads with--min 16in class adv (release > v5 > measure > adv), in chunks of 100 to 1,000 seeds with a mechanical yield to class-v5 and attack-pass v5 waiters (section 8b). Load 430 to 560 on 96 threads before the lease tool, 80 to 120 after it. No GPU: the live-hash-rate confirmation of any gain is BLOCKED and the gain is priced from read counts, as chip-model-v3.md section 5 prices it. - Logs: /srv/builds/_adv-accept-3/ on each box; copies in docs/analysis/cryptanalysis/logs/adv-accept-3/ on this branch.
- Seed space:
seed_words_from_bytes("igneum-adv-accept-3/steer/<i>")as 32 little-endian bytes (chain-shaped epoch seeds), era fixed to the Devnet 3 era bytes 4020cb43...b925 (the era is a 180-day constant the epoch attacker does not re-roll). Every program is drawn through the library's own attempt loop and rule. - Queue: 90 (exhaustion census) was claimed by lane adv-accept before this lane started; its row is cited, not repeated. 91 and 92 claimed at 19:5x BST; 93 to 99 written, claimed and queued (section 9).
- Box-hours spent: about 3.3 in all, every sweep ended by 23:04 BST (section 8). Earlier readings: 22:45 BST about 3.0; 22:10 BST about 2.4 (steer s00 to 10,000 seeds and s01 to about 12,500 at 23 to 32 cores, the last-resort and verdict sweeps, four f8 runs at 2^24 of about 15 min each at 24 to 32 cores). Earlier reading, 21:36 BST: about 1.6 (the 0.5 of 20:45 plus one verdicts chunk at 17 cores, five last-resort chunks at 32 cores for 2,500 x 3.6 s, three f8 runs at 32 cores, and 2^24 f8 runs in progress). Earlier text: about 0.5 (four builds under a minute each; idcheck, margins, exhaust-mirror, ids, two static sweeps and the plant run under 5 min together; steer part 1 at 88 threads 9 min and verdicts part 1 at 88 threads 8 min before the 20:22 kill; three 1,000-seed steer chunks at 23 cores, about 4 min each; the f8 live census 4 min at 32 cores). 8 box-hours is the reading line, 16 the ask line.
Draw-path validation (passed before any claim)
adv3 idcheck (box 2, 20:06 BST, log logs/adv-accept-3/idcheck.log): the Devnet 3 epoch-0 seed drawn through Epoch::chain_program(ProgramClass::V4) gives attempt 0, id fce15bf61030be57, class mx8-eraaf3a9139+sh256x27 (MATCH with the pack, sha256 e025750f... verified on build-1); the kit's shared devnet epoch-0 seed gives attempt 1, id a785001687d8688a (MATCH). So the harness draws what the chain draws.
Status board
| Q | Question | Method | Known-failed shape (must fire) | Gate | Result | Status |
|---|---|---|---|---|---|---|
| Q1 | Exhaustion: P(a seed exhausts 256 attempts), per-part rejection rates, attempt distribution | Cite row 90 (lane adv-accept, 16,337 seeds); add this lane's static per-part rates over 3,009,928 candidates of 10^6 seeds and the geometric fit | the reject-everything mirror of Q1b | P(exhaust) bounded with its count | per attempt: accept 0.323, reject 0.677 ((a') 0.568, (a) 0.079, (b) 0.022, dynamic parts about 0.009); histogram geometric (bin ratios 0.65 to 0.71); P(exhaust) = 0.677^256 = 4.6 x 10^-44; 0 of 16,337 seeds reached the cap | BOUND |
| Q1b | The last-resort program: weak, constant, predictable | last_resort_v4(candidate(b, 256)) through the library on 3,000 seeds, the REAL rule run on each |
adv3 exhaust-mirror: a reject-everything loop must hit the cap and print the last resort |
last resort characterised; no predictable or weak accepted program | mirror fired (cap 256, byte-identical to the library's). 3,000 seeds DONE: the real rule REJECTS 271 of 3,000 last-resort programs (9.0 percent): 251 by (a) cyclic stale load, 14 by (b), 6 by (c) distinct sum; 3,000 distinct programs; 0 lossy ops; chi-square against the weights 39 (mean), largest op share 37 percent | FINDING (correctness, unreachable at 4.6 x 10^-44): the "accepted as drawn" last resort fails the rule's own (a) in 1 of 12 seeds |
| Q2 | Steering with the full rule: tries per quantile of each property | adv3 steer, the real draw per seed, min per-site ratio at 256 units plus static properties; 2 x 10^4 seeds queued |
--plant hot: two or writers before a load's source must put the planted set in the lowest quantile |
tabulated; no property reaches a 1.1x chip gain under 10^5 tries | plant: 45 of 48 planted rows at ratio 0.207 to 0.993 against a population minimum of 0.999 at 256 units (3 did not fire: the scan found no ALU writer to change); the real rule rejects every effective plant by (a'). First 975 seeds (part 1): min ratio 0.998, median 0.999; 18 of 18 chain re-draws equal; 0 last-resort programs; per-attempt rejects (a') 1,820, (a) 257, (b) 87, (c'') 26, (c) 19 of 3,184 candidates | BOUND: 19,975 full-rule seeds DONE at 22:43 BST (section 4 table); every property's tail is the honest card's as much as the chip's except the live hot set, and the lowest stand-in ratio of 20,000 (0.995, 5 seeds, 1 in 4,000) buys 1.0002x live; the (c) tails stop at 151 saturated of 163, bias 122 of 136, distinct 123.75 of the 120 floor; 398 of 398 chain re-draws equal; 0 last-resort programs |
| Q2b | Static steering at 10^6 seeds | adv3 static: the first candidate passing (a), (b), (a') per seed, static properties, quantiles to 10^-6 |
the plant of Q2 (the same property code) | the 10^-5 and 10^-6 quantiles of each property, priced | DONE: table in section 3; the best property at 1 in 10^6 tries is a shadow block with 38 multiplies of 256 (mean 74), worth about 2 to 3 percent of the f = 1 chip's energy per hash (approximate); the load critical path at 40 of 128 (median 72) is worth nothing at the memory activate ceiling | BOUND |
| Q2c | Live hot set of the lowest-ratio steered seeds | f8 warps on the lowest-ratio seeds of Q2 |
f8's own const-item plant | the worst steered seed's gain priced | plant FLAGGED (X_0.1% +6.25, 24.9x); control clear; the five lowest of 975 (ratio 0.998): within the 1.2x gate (1.01x to 1.13x), no hot set, 6-sigma flagged (+32 to +164); the three lowest of 4,975 (ratio 0.995: seeds 2653, 3296, 4388): PASS with no test fired (1.0007x to 1.0008x, X_0.1%/f 0.002, 6-sigma +4.3 to +4.8, the control's level); worst case over the eight: 1.0004x | BOUND: the 0.995 seeds PASS at 2^24 (0.9998x to 1.0002x); seed 620 at 2^24 is beyond the 1.2x share gate (1.43x) with no hot set (X_f/f 0.77), 1.0008x: the lane's worst live case |
| Q3 | Program id: collisions and derivation agreement | adv3 ids over 10^7 (seed, attempt) pairs; adv3 idcheck by three derivations |
low-32 collision counts must match the birthday expectation; one attempt bit flipped must change the id | 0 collisions; the derivation that matches the packs named | 0 id collisions, 0 program-stream state collisions, 0 seed-word collisions over 10^7 pairs (control: 11,759 low-32 id collisions against 11,641 expected); FINDING: program.json's program_id_derivation string and spec 1.4.6 state a derivation WITHOUT the `"sub/" |
|
| Q4 | Determinism traps | adv3 margins; a second interpretation of the rule (adv3 verdicts, 2,000 seeds, every attempt); a spec-1.4.6-only interpretation (400 seeds); the read list |
--variant floor97 must change at least one verdict |
0 disagreements between code and the faithful second interpretation; the spec-only disagreement rate measured; every divergence listed | margins: the f64 ratio compare of (c'') never disagrees with the integer rule 50 d >= 49 E on any of the 2^20 + 1 values of d for any window (margins 0.32 to 0.44 counts); second interpretation DONE: 5,748 of 5,748 attempt verdicts of 1,792 seeds agree; its control fired (the single-pass (a) variant changes 4 of 1,008 verdicts and 3 of 300 programs; the floor-0.97 variant changed none, section 6.4) with the code, part by part (the same 1,445 (a'), 204 (a), 70 (b), 20 (c''), 7 (c') saturated-source, 7 constant-bit, 1 distinct-sum rejections and 792 accepts), chosen attempt equal on every seed; read list in section 6 (the spec text is behind the code on six points; the CLI accept command caps at 32 with no last resort) |
DONE. Spec-text implementation at 017e7037 (sweep 97, 400 seeds): 759 of 1,317 verdicts and 264 of 400 epoch programs differ from the code, 1 disagreement the other way; against the rewritten text at 8b834634 (Q4c, section 6.5): 0 of 400 differ on any field, with one named-not-stated function (dataset_elem) still taken from the crate; against 56eebc0d with nothing from the crate (Q4d, section 6.6): 0 of 400 differ, the finding closed in full. Divergence sources: D2 (documentary, FINDING), D3 (the id string, FINDING), D1 (f64 ratio, latent, safe at the shipped constants), D4 (CLI cap 32, tool only); D5 to D10 none |
| Q5 | Era steering | adv3 era-steer over 400 era seeds: stride, interleave, epoch 0's min ratio |
none needed (a listing) | noted | 400 eras: no stride under NAF weight 7 (mean 11.4), all 31 rotations, 354 distinct interleaves; the accepted attempt of epoch 0 is 3 under every era, so the era lever moves the address map and not the program | BOUND |
1. Q1: exhaustion (row 90, owner adv-accept, cited; this lane's static rates added)
Lane adv-accept's census (its report at build/adv-accept d9638927, "The selector's base rate", 16,337 accepted programs of the F8 label space, read 20:12 BST): accepted at attempt 0..11: 5,271, 3,602, 2,434, 1,585, 1,110, 740, 521, 368, 211, 166, 100, 72; max 26; mean 2.097; last-resort programs 0. Its forced-exhaust plant hit the cap and printed the last resort, which the real rule accepted (op mix xor 17 to 19, 0 lossy ops).
This lane's static sweep (adv3 static --from 0 --count 1000000 --threads 8, box 1, 20:14 BST, 61 s, log logs/adv-accept-3/static-1e6-v2.tsv): 10^6 seeds, 3,009,928 candidates drawn until the first that passes the static rule.
| Quantity | Value |
|---|---|
| Candidates per seed to the first static pass | 3.010 |
| Static rejection per candidate | 0.6678 |
| (a') dataflow freshness | 1,708,418 of 3,009,928 = 0.5676 |
| (a) cyclic stale load | 236,693 = 0.0786 |
| (b) no injecting write | 64,817 = 0.0215 |
| Dynamic parts (c), (c'), (c'') on a static pass (from the full-rule accept rate 0.323 against the static pass rate 0.332) | about 0.027 of static passes, 0.009 of candidates |
| Seeds reaching 256 static rejections | 0 of 10^6 (max first-static-pass attempt 33) |
| Static attempt histogram 0..11 | 332,037, 222,134, 148,201, 98,797, 66,094, 44,000, 29,246, 19,867, 13,210, 8,916, 5,790, 3,895 |
The histogram is geometric: successive bin ratios 0.669, 0.667, 0.667, 0.669, 0.666, 0.665, 0.679, 0.665, 0.675 (static), and 0.683, 0.676, 0.651, 0.700, 0.667, 0.704, 0.706, 0.573, 0.787 on the sibling's 16,337 full-rule seeds. Attempt k's words are seed_words_from_bytes(b || k_le32), an FNV-1a prefix state shared across attempts and finalised by the murmur mix; the bins show no correlation between attempts at this sample size. With r = 0.677 per attempt, P(exhaust) = r^256 = exp(256 ln 0.677) = 4.6 x 10^-44 per epoch seed. An attacker who re-rolls the epoch seed to reach the last resort needs about 2 x 10^43 tries. No seed class exhausts: the attempts of one seed are 256 independent draws by construction. Bound: 1.016 x 10^6 full-rule seeds (this lane's plant run plus the sibling's) and 10^6 static seeds, 0 at the cap.
2. Q1b: the last-resort program
adv3 exhaust-mirror --seed 0 (box 1, 20:06 BST, log logs/adv-accept-3/exhaust-mirror-0.log). A mirror of try_generate_class with the rule replaced by reject-everything tried 256 attempts, reached the cap, and produced last_resort_v4(candidate(seed, 256)); the library's own last-resort call on the same seed is byte-identical (== on the Program). The real rule, run on that program, ACCEPTS it: distinct mean 128.000, saturated 0, bias max 55. Its base op mix: xor 18, load 16, add 10, shfl 6, rotl 4, rotr 4, mad 3, sub 3 (0 or, 0 mul, 0 mulhi; the 3 mad multiplies are kept by the rewrite). Its static properties: 88 loads on the critical path, 18,979 ALU ops on it, 2 predictable sites, chi-square 34.5 against the weights (the rewrite moves every or, mul and mulhi to xor, so its op mix is the one structural tell), 0 lossy ops.
Sweep 95 DONE (adv3 lastresort, 3,000 seeds through lease pool 32, 20:43 to 21:51 BST, log logs/adv-accept-3/95-lastresort.tsv): last_resort_v4(candidate(b, 256, class)) built through the library for each seed and the REAL accept::check run on it.
| Quantity | Value |
|---|---|
| Last-resort programs the real rule accepts | 2,729 of 3,000 (91.0 percent) |
| Rejected by (a), a load whose source no instruction wrote since the previous load from it (cyclic) | 251 (8.4 percent) |
| Rejected by (b), a register with no injecting write | 14 (0.5 percent) |
| Rejected by (c), the distinct-address sum at or under 245,760 | 6 (0.2 percent) |
| Rejected by (a'), (c'), (c''), the other (c) parts | 0 |
| Distinct programs (fingerprint over every field) | 3,000 of 3,000 |
| or, mul, mulhi left (base and shadow) | 0 on every program; mad kept: 14 to 57 of 320 (mean 32.7) |
| Chi-square of the base op mix against the weights | 16.0 to 110.6 (mean 39.1; the population's mean is 8.9) |
| Largest single-op share of the 48 non-load ops | 18 to 62 percent (mean 36.8; xor absorbs or, mul and mulhi) |
| Loads on the critical path | 40 to 120 (mean 76.5, the population's) |
FINDING (correctness, not exploitable). try_generate_class returns the last resort "deterministic and accepted as drawn, with no further check" (generator.rs, the comment and the code), and its doc argues only that (a') holds by construction because no lossy op is left. It does not hold the rest of the rule: in 1 of 12 seeds the last-resort program breaks rule (a), the cyclic stale-load test, because the attempt-256 candidate's draw took the empty-eligible-list fallback for some load (spec 1.4.3, "if E is empty ... 1.4.6 (a) rejects the program") and the rewrite of or, mul and mulhi to xor does not touch load sources. Such a program re-reads one dataset address in two loads of the same hash, the exact shape the rule exists to reject (spec 1.4.2: the GPU rate tracked the distinct-address count). The rewrite also leaves 0.4 percent with a register nobody injects and 0.16 percent under the distinct-sum floor. What it costs: nothing today, because the last resort is reached at 4.6 x 10^-44 per epoch seed (section 1) and no seed in 1.02 x 10^6 full-rule draws came within 230 attempts of it. What it says: the draw's totality rests on a program the rule would reject 9 percent of the time, and a change of the rule or the cap that made exhaustion likelier (the first version's 32-attempt cap, where exhaustion is 3.8 x 10^-6 per seed, one epoch in a few decades of hourly epochs) would hand the chain such a program. A fix that keeps the draw total: pick, among the 256 rejected candidates, the first whose rejection was a dynamic part (so (a), (b) and (a') hold) and apply the rewrite to that; or run check on the last resort and continue to attempt 257, 258, ... until one passes (the per-attempt accept rate is 0.32, so this terminates in a handful of draws with probability 1 minus 10^-100 class). Owed to the defender: which of the two the chain adopts, and a test that walks the (a)-failing last resort (seed 2 of this lane's label space, fingerprint 8e0b8894d29778bc, is one).
What the last resort is: a deterministic function of the seed (the attempt-256 draw), different for every seed, computable by anyone, reachable only at 4.6 x 10^-44 per seed. Whether it is weak by the rule's own measures over many seeds, and how often the rule would have rejected it, is sweep 95 (3,000 seeds, the real rule on each, queued). What a chip would gain from it: nothing beyond any accepted program's gain; it has no hot set by construction (no lossy op feeds a load) and its only skew is the missing or, mul and mulhi, which a fixed datapath pays for in the shadow block anyway (the shadow's multiplies become xor too, so a last-resort epoch is the cheapest epoch a multiplier-poor chip could see: a 256-instruction shadow with 0 mul and mulhi and only its mad multiplies, about 27 of 256). At its probability that epoch never comes.
3. Q2b: static steering at 10^6 seeds (DONE)
The property of the first statically accepted candidate of each seed (the dynamic rule then rejects about 2.7 percent of these; the full-rule sweep of section 4 checks that the conditioning does not move the tails). Tries per quantile are 1/q by definition; the table's content is the value a grinder buys at each quantile.
| Property (per hash unless said) | Mean | Min | 10^-1 low / high | 10^-2 | 10^-3 | 10^-4 | 10^-5 | 10^-6 (1 of 10^6) | Max |
|---|---|---|---|---|---|---|---|---|---|
| Loads on the critical path (of 128) | 76.4 | 40 | 64 / 96 | 48 / 104 | 48 / 120 | 40 / 128 | 40 / 128 | 40 / 128 | 128 |
| ALU ops on the critical path (base and shadow, all reps) | 18,326 | 14,008 | 17,001 / 19,808 | 15,937 / 20,952 | 15,273 / 21,992 | 14,664 / 22,835 | 14,251 / 23,506 | 14,033 / 24,410 | 24,593 |
| Load sites of iteration 0 with an init-only address (of 16) | 2.5 | 1 | 1 / 4 | 1 / 6 | 1 / 7 | 1 / 8 | 1 / 9 | 1 / 10 | 10 |
| Chi-square of the 48 non-load ops against the weights | 8.94 | 0.25 | 4.23 / 14.46 | 2.15 / 21.63 | 1.18 / 28.40 | 0.71 / 35.49 | 0.44 / 41.43 | 0.25 / 46.62 | 52.35 |
| Largest single-op share of the 48, percent | 19.0 | 10 | 16 / 25 | 14 / 29 | 12 / 35 | 12 / 37 | 12 / 41 | 10 / 43 | 43 |
| Output registers whose last write is or, mul or mulhi | 1.6 | 0 | 0 / 3 | 0 / 4 | 0 / 5 | 0 / 6 | 0 / 7 | 0 / 7 | 7 |
| or, mul, mulhi in the base program | 11.2 | 0 | 8 / 15 | 5 / 18 | 3 / 21 | 2 / 23 | 1 / 24 | 0 / 25 | 27 |
| Multiplies (mul, mulhi, mad) in the base program (of 48) | 13.9 | 2 | 10 / 18 | 7 / 21 | 5 / 24 | 3 / 26 | 2 / 28 | 2 / 29 | 30 |
| Multiplies in the shadow block (of 256) | 73.9 | 38 | 65 / 83 | 58 / 91 | 52 / 97 | 48 / 101 | 45 / 105 | 40 / 109 | 110 |
| Dependency depth of one shadow pass (of 256) | 84.9 | 64 | 79 / 91 | 74 / 97 | 70 / 102 | 68 / 105 | 67 / 109 | 65 / 112 | 114 |
Priced against chip-model-v3.md section 5:
| Lever a grinder could pull | Best value at 10^-4 (10^4 tries) and 10^-6 | What it buys a chip | Reading |
|---|---|---|---|
| Short load chain (memory-level parallelism) | 40 loads on the critical path against a median of 72: 3.2 independent reads in flight per lane against 1.8 | The f = 1 chip and the card both sit at the memory's activate ceiling (section 5.3: 21.3 G reads/s, the 5090 at 82 percent of it); more reads in flight per lane lowers the lanes needed, not the ceiling; both sides mine the same program | 1.00x |
| Short ALU chain | 14,664 ops at 10^-4, 14,033 at 10^-6, against 18,326: 20 to 23 percent shorter | The ALU chain is 7.6 microseconds per hash at one op per cycle at 2.4 GHz against 128 dependent reads at about 400 ns (51 microseconds): the shadow sits inside the read latency on both sides (section 5.7, the program-length lever); shorter helps the honest card and the chip alike | 1.00x |
| Init-only addresses in iteration 0 | 8 of 16 sites at 10^-4, 10 at 10^-6 (median 2) | Addresses a lane can issue before its first read returns: more parallelism at the start of the hash, the same ceiling; the addresses are nonce-dependent (init words are a hash of header and nonce), not a hot set | 1.00x |
| Fewer multiplies for a multiplier-poor datapath | shadow 48 of 256 at 10^-4, 38 at 10^-6 (mean 74); base 3 at 10^-4 | The shadow is 55,296 ops per hash. At N5 datapath figures (section 5.1: multiply 0.52 pJ, add 0.06, 2x pipeline) the mean shadow pass costs 74 x 0.52 + 182 x 0.06 = 49 pJ, the 10^-6 one 38 x 0.52 + 218 x 0.06 = 33 pJ: 34 percent less shadow ALU energy. The shadow is about 11 to 22 nJ of the f = 1 chip's 470 nJ per hash (approximate), so the saving is 2 to 3 percent of its energy for one epoch in a million tries. The chip still needs multipliers for every other epoch | about 1.02x to 1.03x per joule at 10^-6, approximate; under the 1.1x gate at every quantile |
| Lossy last writes (output bias shape) | 6 of 8 registers at 10^-4, 7 at 10^-6 | A lossy last write narrows an output register's value set; the rule's bias test bounds every output bit to 6 sigma and the final values to under 1 percent saturated; nothing a chip reads faster | 1.00x |
Static steering buys no asymmetric gain: every property a seed grinder can move in the static program helps the honest card by the same amount or not at all, because the rate on both sides is reads per second at the memory ceiling. The one asymmetric lever is a hot set on the live dataset, which is the full-rule sweep's ratio column (section 4) and lane adv-accept's live measurement (its five lowest-ratio seeds of 4,600 all beyond the f8 1.2x gate live, the worst a 1 MB copy serving 0.31 percent of loads, 1.002x; its selector table: ratio under 0.999 at 256 units in 85 of 16,337, one try in 192).
4. Q2: steering with the full rule (RUNNING)
Sweeps 93 and 96 (adv3 steer --from 0 --count 10000 --threads 88 --check-every 50 and --from 10000), box 2, queued 20:11 BST behind lane adv-cache-2's census under the sweep lock. Per seed: the real attempt loop and rule, the accepted attempt, the chain id, the minimum per-site distinct-index ratio at 256 units and its site, the (c) report, every rejection reason of the seed's earlier attempts, the static properties of section 3, and every 50th seed re-drawn through Epoch::chain_program for equality. Rows land in the log as they finish; the quantile table lands here from the first 10^4 seeds.
Part 1 (seeds 0 to 974, binary 928272e0, 88 threads, 20:13 to 20:22 BST before the kill, log logs/adv-accept-3/93-steer-s00-part1.tsv):
| Quantity | Value |
|---|---|
| Accepted at attempt 0..11 | 307, 186, 160, 107, 52, 44, 43, 22, 15, 13, 13, 5 (max 11); last resort 0 |
| Rejections of the 3,184 candidates | (a') 1,820, (a) 257, (b) 87, (c'') 26, (c) constant bit 9, (c) saturated 9, (c) distinct sum 1 |
| Min per-site ratio at 256 units | min 0.998 (seeds 514, 522, 620, 703, 959), 1 percent quantile 0.999, median 0.999 |
Chain re-draw equality (Epoch::chain_program, every 50th seed) |
18 of 18 equal |
| Saturated final values, max over the 975 | 76 (limit 163) |
| Seconds per seed at 88 threads under load 400 | median 4.9 |
The dynamic parts reject 45 of 1,017 candidates that passed the static rule (4.4 percent), (c'') 26 of them: the 2^20 ratio pass is live in the population and is the one dynamic test that fires more than once in a hundred static passes.
Shard s00 DONE (seeds 0 to 974 before the kill, 1,000 to 9,999 through the lease in 1,000-seed chunks, the last at 21:41 BST; log 93-steer-s00-part2.tsv): 9,975 seeds, 31,285 candidates. Accepted at attempt 0..13: 3,197, 2,112, 1,471, 1,031, 704, 479, 318, 203, 140, 116, 70, 43, 26, 18; max 25; last resort 0. Rejections: (a') 17,735, (a) 2,473, (b) 671, (c'') 279, (c) saturated 69, (c) constant bit 58, (c) distinct sum 18, (c') 6, (c) bias 1 (the first bias rejection seen). Chain re-draw equality 198 of 198. The dynamic parts reject 431 of 10,406 static passes (4.1 percent), (c'') 279 of them. Min ratio at 256 units: 0.995 on four seeds (2653, 3296, 4388, 6453: 1 in 2,500), 0.997 on three more; the (c) tails stand where the 4,975-seed table below put them (saturated 151, bias 122, distinct 123.87). The 4,975-seed table (seeds 0 to 974 and 1,000 to 3,999): Accepted at attempt 0..13: 1,594, 1,057, 742, 511, 327, 241, 157, 102, 69, 63, 39, 22, 14, 8; max 25; last resort 0. Rejections: (a') 8,973, (a) 1,207, (b) 344, (c'') 142, (c) constant bit 32, (c) saturated 31, (c) distinct sum 8, (c') 2. The dynamic parts reject 215 of 5,190 static passes (4.1 percent), (c'') 142 of them. Chain re-draw equality 98 of 98.
| Property of the accepted program | Mean | Min | 10^-1 low / high | 10^-2 | 10^-3 | 2 x 10^-4 (1 of 4,975) | Max |
|---|---|---|---|---|---|---|---|
| Min per-site distinct ratio at 256 units (milli) | 999 | 995 | 999 / 999 | 999 / 999 | 997 / 999 | 995 / 999 | 999 |
| (c) distinct mean (milli, of 128,000) | 127,957 | 123,872 | 127,874 / 128,000 | 127,356 / 128,000 | 124,037 / 128,000 | 123,872 / 128,000 | 128,000 |
| (c) saturated finals (limit 163) | 1.1 | 0 | 0 / 0 | 0 / 30 | 0 / 51 | 0 / 151 | 151 |
| (c) bias max (limit 136) | 58.6 | 33 | 48 / 71 | 41 / 85 | 38 / 100 | 33 / 122 | 122 |
| Loads on the critical path | 76.5 | 40 | 64 / 96 | 48 / 104 | 48 / 120 | 40 / 120 | 120 |
| ALU ops on the critical path | 18,334 | 14,448 | 16,849 / 19,820 | 15,920 / 21,107 | 15,297 / 21,984 | 14,448 / 22,233 | 22,233 |
| Init-only sites, iteration 0 | 2.5 | 1 | 1 / 4 | 1 / 6 | 1 / 7 | 1 / 8 | 8 |
| Chi-square against the weights | 8.95 | 0.60 | 4.17 / 14.40 | 2.20 / 21.54 | 1.04 / 30.70 | 0.60 / 37.68 | 37.68 |
| Lossy last writes | 1.6 | 0 | 0 / 3 | 0 / 4 | 0 / 5 | 0 / 6 | 6 |
| or, mul, mulhi in the base | 11.1 | 1 | 7 / 15 | 5 / 18 | 3 / 21 | 1 / 22 | 22 |
Both shards DONE (s00 seeds 0 to 9,999, s01 seeds 10,000 to 19,999, the last chunk at 22:43 BST; logs 93-steer-s00-part1.tsv, 93-steer-s00-part2.tsv, 96-steer-s01.tsv): 19,975 seeds (25 of the kill window uncovered, no pre-emption gap), 62,240 candidates. Accepted at attempt 0..13: 6,386, 4,306, 2,945, 2,033, 1,398, 986, 616, 421, 282, 216, 133, 75, 60, 39; max 25; last resort 0. Rejections: (a') 35,119, (a) 4,957, (b) 1,320, (c'') 552, (c) constant bit 142, (c) saturated 132, (c) distinct sum 32, (c') 10, (c) bias 1. Chain re-draw equality 398 of 398. The per-attempt accept rate is 0.321 (19,975 of 62,240), so P(exhaust) = 0.679^256 = 1.0 x 10^-43 on this sample (section 1's 4.6 x 10^-44 used the static rate alone).
The steering table over the 19,975 accepted programs (tries per quantile = 1/q; the 5 x 10^-5 column is the single worst seed of 20,000):
| Property of the accepted program | Mean | 10^-1 low / high | 10^-2 | 10^-3 | 10^-4 | 5 x 10^-5 (1 of 20,000) |
|---|---|---|---|---|---|---|
| Min per-site distinct ratio at 256 units (milli) | 999 | 999 / 999 | 999 / 999 | 998 / 999 | 995 / 1000 | 995 |
| (c) distinct mean (milli, of 128,000; floor 120,000) | 127,951 | 127,872 / 128,000 | 127,274 / 128,000 | 123,992 / 128,000 | 123,866 / 128,000 | 123,748 |
| (c) saturated finals (limit 163) | 1.1 | 0 / 1 | 0 / 30 | 0 / 60 | 0 / 95 | 151 |
| (c) bias max (limit 136) | 58.7 | 48 / 71 | 41 / 86 | 37 / 99 | 33 / 111 | 122 |
| Loads on the critical path (of 128) | 76.5 | 64 / 96 | 48 / 104 | 48 / 120 | 40 / 128 | 40 / 128 |
| ALU ops on the critical path | 18,317 | 16,992 / 19,809 | 15,920 / 20,946 | 15,129 / 21,977 | 14,448 / 22,385 | 14,251 / 22,896 |
| Init-only sites, iteration 0 (of 16) | 2.5 | 1 / 4 | 1 / 6 | 1 / 7 | 1 / 9 | 9 |
| Chi-square against the weights | 8.91 | 4.22 / 14.40 | 2.15 / 21.59 | 1.05 / 28.25 | 0.60 / 37.68 | 0.45 / 38.02 |
| Lossy last writes (of 8) | 1.6 | 0 / 3 | 0 / 4 | 0 / 5 | 0 / 6 | 6 |
| or, mul, mulhi in the base (of 48) | 11.2 | 8 / 15 | 5 / 18 | 3 / 21 | 2 / 23 | 1 / 25 |
Reading, as the attacker who pays per try: 10^4 tries buy a program with 40 loads on its critical path instead of 72 (worth nothing at the memory ceiling, section 3), an ALU chain 21 percent shorter (the same on both sides), 9 of 16 first-iteration addresses computable from the init words (more parallelism at the start of a hash, the same ceiling), or a stand-in ratio of 0.995 whose live hot set is nil (section 4b). What 10^4 tries do not buy: a program under any (c) limit, a last resort, a program whose chain re-draw differs from the harness's, or a live hot set worth more than 0.1 percent of reads (the eight live measurements of this lane, worst 1.0008x; lane adv-accept's worst of 16,337 seeds, 1.002x). The steering cost table is therefore flat: no property reaches a 1.1x chip gain at any quantile down to 5 x 10^-5, and the gate of queue file 91 holds with a margin of about 100x on the gain.
The accepted population's static tails are the static sweep's tails of section 3 at the same quantiles (critical path 48 / 104 at 10^-2 in both; ALU 15,920 against 15,937), so the dynamic rule does not condition the static shape and the 10^6-seed static table stands for the accepted programs too. The dynamic tails: the worst accepted program sits at 151 saturated finals of the 163 limit and bias 122 of 136, and the worst distinct mean at 123.9 of the 120 floor; the (c'') ratio reaches 0.995 at 256 units (seeds 2653, 3296, 4388; three of 4,975, 1 in 1,660), below anything the first 975 showed (0.998) and below lane adv-accept's minimum over 16,337 (0.9945 at the same 256 units, so the two label spaces agree). Those three go to the live census (section 4b, second table).
Known-failed shape (adv3 steer --from 900000 --count 48 --threads 48 --plant hot --check-every 1, box 1, 20:07 to 20:08 BST, binary 35336b84, log logs/adv-accept-3/steer-plant.log): on each accepted program the two ALU instructions that last wrote the source register of one load were rewritten to or (the source then saturates toward all ones) and the ratio re-read.
| Rows | Planted ratio at 256 units | Population ratio (the same 48 unplanted) | The real rule on the planted program |
|---|---|---|---|
| 45 of 48 | 0.207 to 0.993 (median about 0.98) | 0.999 on every row | rejected, (a') on every one |
| 3 of 48 | 0.999 (the plant did not fire: the backward scan found loads only before the site and changed nothing) | 0.999 | accepted (unchanged program) |
Every effective plant sits below the population minimum, so the property column separates a forced hot source at once. Rejection reasons over the 48 seeds' 106 rejected attempts: (a') 89, (a) 12, (b) 3, (c'') 2; mean accepted attempt 2.21. Per seed under the box's load: 15 to 57 s at 48 threads (the 2^20 pass of (c'') on every static-passing candidate is the cost), so the two 10^4-seed shards are about 2 box-hours each at 88 threads once they hold the lock. The chain_eq column of that run read DIFF on every row because the first binary left generator 2 on the candidates (the id then took the class path); the comparison is meaningful from binary 928272e0 on, which every queued sweep runs.
4b. Q2c: the live hot set of the lowest-ratio steered seeds (box 1, 20:33 to 20:37 BST)
991-adv-accept-3-f8-live.sh through lease pool 32 --min 16: the f8 warps census (census code untouched; seed mode added for this lane's label space, binary attack-f8 918f80a5) on day 20733, 10^6 nonces each, the library's hash_warp agreement sampled (0 mismatches on every clean run), log logs/adv-accept-3/991-f8-live.log.
| Run | Top 0.1% share over the window-model control (gate 1.2x) | Hot-set test f = 0.1%: X_f, X_f/f | Windowed 6-sigma, 64-item buckets | Verdict |
|---|---|---|---|---|
seed 0 with --plant const-item (known-failed shape) |
24.87x | +6.25%, 62.5 | +387,032 sigma | FLAGGED, HOT SET: the tool fires |
| seed 0 clean (control; attempt 3, id 6eb252f3a3102cdb) | 1.0007x | +0.0002%, 0.002 | +5.46 within | PASS |
| 514 (ratio 0.998 at 256 units, site 9) | 1.088x | +0.025%, 0.25 | +32.6 flagged | within the gate, no hot set |
| 522 (site 4) | 1.045x | +0.012%, 0.12 | +44.3 flagged | within, no hot set |
| 620 (site 0) | 1.128x | +0.037%, 0.37 | +164.5 flagged | within, no hot set |
| 703 (site 12) | 1.108x | +0.030%, 0.30 | +34.5 flagged | within, no hot set |
| 959 (site 12) | 1.011x | +0.003%, 0.03 | +45.7 flagged | within, no hot set |
Part 2 (992-adv-accept-3-f8-live-2.sh, 21:33 BST, the three lowest-ratio seeds of 4,975 at 10^6 nonces, log logs/adv-accept-3/992-f8-live-2.log; the 2^24 passes on these three and on 620 follow in the same log):
| Seed (ratio 0.995 at 256 units) | Top 0.1% over the window model | X_0.1%, X_f/f | Windowed 6-sigma | Verdict |
|---|---|---|---|---|
| 2653 (id d56075ecc196a9ec, attempt 3, site 10) | 1.0008x | +0.0002%, 0.002 | +4.33 within | PASS, no test fired |
| 3296 (eb47ecbc0e86d847, attempt 2, site 1) | 1.0007x | +0.0002%, 0.002 | +4.77 within | PASS |
| 4388 (5cd329c25bc6986b, attempt 0, site 6) | 1.0007x | +0.0002%, 0.002 | +4.78 within | PASS |
At 2^24 nonces (21:34 to 21:37 BST, same log), where the gate is sharper and adv-accept read a base rate near 1 in 10 beyond 1.2x:
| Seed | Top 0.1% over the window model | X_0.1%, X_f/f | Windowed 6-sigma | Verdict |
|---|---|---|---|---|
| 2653 | 0.9998x | -0.00003%, -0.0003 | +4.55 within | PASS |
| 3296 | 1.0001x | +0.00001%, +0.0001 | +4.88 within | PASS |
| 4388 | 1.0002x | +0.00003%, +0.0003 | +5.16 within | PASS |
| 620 (ratio 0.998, the worst of the first five at 10^6: 1.13x) | 1.435x BEYOND the gate | +0.077%, 0.77 (f = 1%: +0.152%, 0.15) | +669 flagged | within the hot-set test, beyond the share gate: the lane's worst live case |
Seed 620 at 2^24 (21:38 to 21:53 BST, 24 cores): the top 0.1 percent of items (16,777 items, 1 MB) take 0.253 percent of its 2.1 x 10^9 reads against 0.176 percent on the window model; site 0 (instruction 2, source r4, written by an xor at instruction 1, one bit off a saturated value in a slice of its evaluations) puts 1.94 percent of its reads into those items against 0.10 expected; the hottest item takes 5,671 reads, 0.0003 percent. Priced as chip-model-v3 section 5.7 prices it: a 1 MB on-die copy removes 0.077 percent of DRAM reads, 1.0008x on the f = 1 chip. This is the worst live case of this lane's 9,975 seeds (eight measured live, chosen by the stand-in ratio), against lane adv-accept's worst of 16,337 (seed 100767, 2.05x, +0.155 percent, 1.002x).
So the three programs with the LOWEST stand-in ratio of 4,975 are indistinguishable from the clean control live, while the five at 0.998 all flagged the 6-sigma test. At 256 units the ratio's own sampling noise (65,536 evaluations per site against a window of 2^26 to 2^28 words; the expected distinct count is within 0.1 percent of N, so a 0.5 percent shortfall is about 300 missing indices, 1.2 sigma of a Poisson count of that size) is the same order as the signal. The 256-unit proxy is a weak selector: it enriches the live-lumpy tail (adv-accept's 6 of 8 against a base rate near 1 in 10 at 2^24, its own correction) but its floor is noise. A grinder's real selector is the live census itself at about 3 s per seed on 32 cores, which this lane did not run at scale; its cost per seed is the number that prices the steering (10^4 seeds = 8 box-hours at this rate), and what it buys is bounded by adv-accept's worst live case, 1.002x.
Reading. The selector (the stand-in ratio at 256 units) picks programs whose live item histogram is measurably lumpy (every one flags the windowed 6-sigma where the clean control sits at +5.5), and none of the five has a hot set: the best, seed 620, puts 0.326 percent of its reads into the top 0.1 percent of items against 0.289 percent expected, so a 1 MB on-die copy of those items removes 0.037 percent of DRAM reads, a gain of 1.0004x on the f = 1 chip of chip-model-v3 section 5. Steering cost for this property at this sample: 5 of 975 seeds (1 in 195) reach ratio 0.998; none of the 5 reaches the f8 gate at 10^6 nonces. Lane adv-accept's wider search (16,337 seeds, 2^24 nonces) found its worst at 2.05x over the window model and X_0.1% +0.155 percent, 1.002x for the chip; the two lanes' numbers agree on the order: a seed grinder with 10^4 to 10^5 tries buys a hot set worth under 0.2 percent of reads. At 2^24 nonces the gate is sharper (adv-accept: base rate near 1 in 10 beyond 1.2x among random accepted programs), so these five at 2^24 are owed and queued behind the sweeps.
5. Q3: the program id
adv3 ids --seeds 1000000 --attempts 10 --threads 16 (box 1, 20:09 BST, 3.0 s, log logs/adv-accept-3/ids-1e7.log): 10^7 (seed, attempt) pairs of this lane's seed space.
| Quantity | Count | Expected at random |
|---|---|---|
| Equal program ids (FNV-1a-64 with the generator-4 suffix) | 0 | 2.7 x 10^-6 |
Equal program-stream states (the 64-bit lo ^ hi * golden that fixes the base program and shadow: two seeds with one state draw one program) |
0 | 2.7 x 10^-6 |
| Equal seed-word octets | 0 | about 0 |
| Control: equal low 32 bits of the id / of the state | 11,759 / 11,781 | 11,641 |
The control is the known-failed shape: at 32 bits the counter sees the birthday collisions the model predicts, so a 64-bit collision would have been counted. Bound: 10^7 pairs; the 64-bit birthday bound for the chain's whole life (about 10^5 epochs a decade) is 2.7 x 10^-10 per decade. FNV-1a-64 is not collision-resistant against a chosen input, but the id's preimage is the seed words, themselves the murmur-finalised FNV of the epoch seed, so a chosen-id attack needs a preimage through that step; this lane did not attempt one, and the id is a label, not a commitment.
FINDING (interoperability): adv3 idcheck recomputed the two public ids from the seed words three ways. The code's derivation ("igneum-program/" || 4_le32 || words || attempt_le32 || "sub/" || 3_le16) reproduces both pack ids. The derivation the pack STATES in its own program_id_derivation field ("FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32") and spec 1.4.6's text at this commit give 30956569d8f3d8d7 for Devnet 3 (pack: fce15bf61030be57) and 8aa9f185d63f269e for the kit's shared devnet pack (pack: a785001687d8688a). A worker or verifier written from the pack's own description computes a different id from the node for every generator-4 program and is refused at the id check (packcheck.rs, "a worker MUST refuse a pack whose class or era seed does not match"), or, if it trusts its own id, splits from the node's view of which pack is which. Not a consensus fault (the id does not enter the hash) and not an attack gain; it is a divergence source two conforming-by-the-text implementations hit at once. Fix: the program_id_derivation string in program.json and spec 1.4.6 should carry the suffix. Where it lives: generator.rs program_id (the suffix), emit.rs (the string, not read by this lane; the string is quoted from the pack).
6. Q4: determinism traps
6.1 The f64 in (c'') (adv3 margins, box 2, 20:06 BST, log logs/adv-accept-3/margins.log)
distinct_ratio_pass computes ratio = d / (n - n^2 / 2W) in f64 and compares ratio < 0.98, inside the consensus rule, while spec 1.14 item 1 says "no floating point anywhere". At n = 2^20 evaluations and the three windows the expectation E is an integer and 0.98 E is never within 0.32 of an integer:
| Window | E = n - n^2 / 2W | 0.98 E | Nearest integer | Margin (counts) | f64 compare against 50 d >= 49 E, all 2^20 + 1 values of d |
f32 compare |
|---|---|---|---|---|---|---|
| 2^28 words | 1,046,528 | 1,025,597.44 | 1,025,597 | 0.44 | 0 disagreements | 0 |
| 2^27 | 1,044,480 | 1,023,590.40 | 1,023,590 | 0.40 | 0 | 0 |
| 2^26 | 1,040,384 | 1,019,576.32 | 1,019,576 | 0.32 | 0 | 0 |
So no conforming implementation, in f64 or f32 or integers, can flip a (c'') verdict at these constants. The trap is latent: it depends on ACCEPT_UNITS_DISTINCT_V4 and the floor staying at values where the threshold is not near an integer. A table of nearby constants (units 256 to 8,192, floors 0.97 to 0.995): at floor 0.975 the threshold IS an integer for five of the pairs, among them the shipped 4,096 units on the 2^27 window (0.975 x 1,044,480 = 1,018,368). There f64 (d / E >= 0.975, or d < 0.975 * E) and the exact integer rule still agree, because both round the same rational to the same double, but an f32 implementation reads 0.975f32 = 0.97500002 and rejects d = 1,018,368 where the integer rule accepts it: one verdict flipped for every program that lands exactly on the threshold. At floors 0.97, 0.985 and 0.995 other pairs sit within 0.04 counts. The exact form 50 d >= 49 E (or 1000 d >= 975 E) costs nothing and removes the dependence on the arithmetic. BOUND at the shipped constants; a rule for any later change of the constants.
6.2 The spec text against the code (measured by sweep 97)
Spec 01 section 1.4.6 at 017e7037 describes (a), (b), (c), a 5.14 percent rejection rate, a 32-attempt consensus fault and an id without a suffix. The code adds (a') with the shared-operand idiom, (c'), (c'') at 2^20 evaluations, a 256-attempt cap keyed on the class v4 shape, a total draw with the last resort, generator 4, the suffix, and executes the 256-instruction shadow block 27 times per iteration inside the acceptance interpreter (which the spec's section 1.7 does not have). MEASURED (sweep 97, adv3 verdicts --variant spec, 400 seeds, 1,317 attempt verdicts, 22:48 to 23:00 BST on build-2, log 97-verdicts-spec.tsv; the variant: (a), (b), (c) only, no (a'), (c') or (c''), no shadow block in the acceptance execution, cap 32):
| Quantity | Value |
|---|---|
| Attempt verdicts that differ from the code | 759 of 1,317 (57.6 percent) |
| Attempt-0 verdicts that differ | 227 of 400 (56.8 percent) |
| Seeds whose epoch PROGRAM differs (another attempt chosen) | 264 of 400 (66 percent) |
| Direction: code rejects, spec-text accepts | 758: (a') 733, (c'') 15, (c) constant bit 5, (c) saturated 5 |
| Direction: code accepts, spec-text rejects | 1 ((c) saturated finals: the spec-text execution has no shadow block, so its register statistics are another program's) |
| Spec-text candidates reaching the 32 cap | 0 |
| The parts the spec text does share, (a) and (b) | 115 and 34 rejections, identical on every row |
So an implementation that follows docs/spec/01-lottery-hash.md section 1.4.6 at this commit mines a different program from the node on two thirds of epochs, and on 1 in 400 would reject a program the node accepts (the shadow block changes the (c) statistics). The static parts the text does carry, (a) and (b), agree exactly. This is the largest divergence source found and it is documentary: the public spec at the frozen commit does not describe the frozen rule ((a'), (c'), (c''), the shadow block in the acceptance execution, the 256 cap, the last resort, generator 4 and the id suffix are all absent from it).
6.3 Read list
| # | Where | What two implementations could do differently | Verdict |
|---|---|---|---|
| D1 | accept.rs distinct_ratio_pass, f64 ratio |
section 6.1 | safe at these constants, latent |
| D2 | spec 1.4.6 against accept.rs and generator.rs | section 6.2 | FINDING (documentary), rate from sweep 97 |
| D3 | program.json program_id_derivation, spec 1.4.6 id |
section 5 | FINDING (interoperability) |
| D4 | generator.rs attempts_class (the CLI igneum-pow accept command) |
loops 0..MAX_ATTEMPTS (32) and has no last resort, while the chain's try_generate_class loops to 256 and falls back; the diagnostic tool and the node disagree on a seed needing more than 32 attempts (0.677^32 = 3.8 x 10^-6 per seed; the code comment names one seen seed) |
tool-only, not consensus |
| D5 | accept.rs run_unit early return on a lane-constant site; check_dynamic order of tests |
the verdict is accept iff every test passes, so the order and the early return change only the reported reason | no divergence |
| D6 | distinct_indices_v4 allocates 16 x 2^20 u32 (64 MiB) and sorts per site |
a memory-poor verifier is slower, not different | no divergence |
| D7 | is_class_v4_shape keys (a'), (c'), (c''), the cap and the last resort on shadow.instrs == 256 with the era and the rep count set aside |
a rung change (other reps) keeps the rules; a class with a different shadow size silently loses them | by design, noted |
| D8 | load_index under an era at the rule's constant D = 28, window with k = min(win, 2) |
matches spec 1.13.1's formula at D = 28 | no divergence |
| D9 | below(n) as next() % n |
defined by spec 1.3.2 | no divergence |
| D10 | panics: check_static on a wrong instruction count; generate_from_seed_bytes_class on exhaustion for v2 and v3 |
unreachable from the generator; class v4's draw is total | none on the chain path |
6.4 The second interpretation (sweeps 94, 98)
adv3 verdicts --variant faithful re-implements (a) cyclic, (b), (a') to its fixpoint with the shared-operand idiom, (c), (c') and (c'') as a per-lane scalar interpreter with its own accumulators and an integer ratio compare, from the module table of accept.rs and the spec's instruction semantics (reusing the library's splitmix32, dataset_elem and load_index as primitives), and compares verdict by verdict with accept::check on every attempt of 2,000 seeds. --variant floor97 (300 seeds) was the planned known-failed shape: the floor at 0.97 must change at least one verdict against the code, or the comparison is not reading the ratio. IT DID NOT FIRE: sweep 98 (300 seeds, 1,008 attempt verdicts, 20:4x to 21:42 BST, log 98-verdicts-floor97.tsv) agrees with the code on every row, its 12 (c'') rejections included. The reason is itself a result: every (c'') rejection in the population sits under 0.97 (the attack-pass finding's failing bands read 0.84 to 0.97; the accepted population's minimum at 2^20 is about 0.983 by lane adv-accept's gap rows), so a floor anywhere in 0.97 to 0.98 draws the same line and the comparison had no candidate in the band it moved. The shape is replaced by two runs: (i) --variant noncyclic (sweep 981, 300 seeds, started 21:45 BST), the (a) test in a single pass, which must ACCEPT the wrap-case candidates the code rejects by (a) (459 of 5,748 verdicts in sweep 94 were (a) rejections, and under the draw's fresh-source rule every one of them is a wrap case, so this variant must disagree on about 8 percent of rows or the comparison is not reading (a)); (ii) adv3 ratios (sweep 982, binary 7e96fcca98c9cdb256b2dcb4095c84c6ded785911be32856b1c4560d375d794f, 300 seeds, started 21:47 BST), the 2^20 ratio of every candidate that reached the dynamic rule, which gives the distribution the floor cuts and so the headroom on each side as a number in place of the assumption above. (i) LANDED (sweep 981, 300 seeds, 1,008 attempt verdicts, 22:23 to 22:30 BST on build-2, log 981-verdicts-noncyclic.tsv): the single-pass (a) variant disagrees with the code on 4 verdicts (code reject by (a), variant accept) and changes the chosen program of 3 of the 300 seeds; 0 disagreements the other way. The control fires, so the comparison reads (a), and the 5,748-row agreement of the faithful variant stands with its control. The variant also exposes how the parts overlap: of the code's 81 (a) rejections in this sample, 25 are re-caught by (a') and 1 by (c'') once (a) lets them through, and 51 were also single-pass (a) failures (the empty-eligible fallback inside one pass), so only 4 verdicts in 1,008 (0.4 percent) rest on the cyclic form of (a) alone; the dataflow rule (a') covers most of what (a) was written for. The faithful variant's total is unchanged at 5,748 of 5,748.
(ii) LANDED (sweep 982, adv3 ratios, 300 seeds, 1,008 candidates of which 320 reached the dynamic rule, 22:4x BST on build-2, log 982-ratios.tsv): the minimum per-site ratio at 2^20 evaluations of every candidate that passed the static rule.
| Candidates that reached the dynamic rule | n | Min ratio at 2^20: min | 1 percent | 10 percent | median | max |
|---|---|---|---|---|---|---|
| Accepted | 300 | 0.989 | 0.992 | 0.997 | 0.999 | 0.999 |
| Rejected by (c'') | 12 (3.75 percent of dynamic candidates) | 0.814 | 0.899 | 0.966 | ||
| Rejected by another (c) part (constant bit 4, saturated 3, distinct sum 1) | 8 | 0.958 | 0.999 | 0.999 |
The twelve (c'') ratios: 0.814, 0.815, 0.815, 0.856, 0.856, 0.885, 0.913, 0.933, 0.955, 0.956, 0.965, 0.966. So on this sample the 0.98 floor sits in a gap of 0.023: the accepted population's minimum is 0.009 above it and the rejected population's maximum 0.014 below it, with nothing in between. That is why the floor-0.97 variant changed no verdict, and it is the floor's headroom as a number: a floor anywhere in 0.967 to 0.988 gives the same verdicts on these 320 candidates. The accepted minimum of 0.989 (one program in 300) is also the sharpest accepted ratio seen by either lane (adv-accept's gap rows read 0.983 on seed 100064 of its 16,337); the two readings together put the accepted tail at about 0.983 to 0.989 and the rejected head at about 0.966, so the shipped floor is near the middle of the gap. A grinder cannot steer into the gap: no program of 20,275 draws between the lanes has landed in it. Sweep 94 DONE (seeds 0 to 791 at 88 threads before the kill, then 1,000 to 1,999 through the lease in 200-seed chunks, 20:43 to 21:40 BST; logs 94-verdicts-faithful-part1.tsv and part2.tsv): 5,748 attempt verdicts over 1,792 seeds, 5,748 agree; the first failing part named by the second interpretation equals the code's on every row ((a') 3,281, (a) 459, (b) 135, (c'') 50, (c) constant bit 16, (c) saturated 12, (c) distinct sum 3, accept 1,792); the chosen attempt is the same on all 1,792 seeds. Every part of the rule fired at least once in the sample except (c'), the saturated-source count (its rate is 2 in 15,714 candidates in the steer sweep, so 0 in 5,748 is expected), and the lane-constant-site test (never seen in any sweep of this lane; the draw's fresh-source rule makes a 32-lane-constant address need a constant register). (Part 1 alone: 2,546 attempt verdicts, 2,546 agree; the first failing part named by the second interpretation equals the code's on every row ((a') 1,445, (a) 204, (b) 70, (c'') 20, (c') 7, constant bit 7, distinct sum 1, accept 792); the chosen attempt is the same on all 792 seeds. The known-failed variant (floor 0.97) runs as sweep 98.
6.5 Q4c: the same 400 programs from the rewritten spec text (read against build master 8b834634)
Main's order, 8 October 2026, 01:0x BST: the audit lane rewrote spec sections 1.4.3 and 1.4.6 to the shipped rule (build master 8b834634; read here: docs/spec/01-lottery-hash.md at that commit only, sections 1.3, 1.4.2, 1.4.3, 1.4.6 with its constants and pinned-ids tables, 1.6, 1.7, 1.13.1, and the shape of igneum-pow/tests/spec_readback.rs). A fresh text-only implementation (tools/attack/adv-accept-3/src/text.rs: seed words, SplitMix64, the program stream, the 1.4.3 draw with the two eligible states and the shared-operand rule, the 256-instruction shadow block, the 1.13.1 stride and rotation, the 1.6 init, the 1.7 execution with the shadow 27 times per iteration, parts (a), (b), (a'), (c), (c'), (c'') in the stated order with the integer ratio compare, the 256 cap, the last resort, the id with the sub-version suffix) derived the same 400 epoch seeds of sweep 97 under the Devnet 3 era and was compared with the crate's Epoch::chain_program field for field.
| Compared over 400 seeds | Equal |
|---|---|
| The 64 base instructions (op, dst, src, src2, imm, imm2, rot, bit, mask, window) | 400 of 400 |
| The 256 shadow instructions | 400 of 400 |
| The chosen attempt | 400 of 400 (0 last resorts) |
| The program id | 400 of 400 |
| The sequence of rejection parts of the earlier attempts | 400 of 400 |
Command: adv3 textderive --from 0 --count 400 --threads 16 (binary 144b81a9ec9d01f52ed81c728288fa37bc58dd522446004a65e47b74d2443814), lease pool 16 --min 8 class adv on build-2, 01:11 to 01:15 BST, 8.8 s per seed at 16 threads (the text implementation runs the 2^20 pass on every candidate that passes the static parts, as the text says the code does), log logs/adv-accept-3/983-textderive-8b834634.tsv; queue file 983 claimed.
Result: 0 of 400 differ. The documentary finding of section 6.2 (264 of 400 against the text at 017e7037) is CLOSED against 8b834634. One sentence of 1.4.6 still cannot be followed from the text alone: 1.4.6.4's Dataset row names dataset_elem(idx, S[0], S[1]) "of verify.rs (the closed form of the version 0.1 packs)" and does not state its six operations, so a reader of the text cannot compute part (c), (c') or (c'') without the crate; text.rs takes that one function from igneum_pow::verify and nothing else. Stating the closed form in the text (x = idx XOR S[0]; x *= 0x9E3779B1; x ^= x >> 15; x += S[1]; x *= 0x85EBCA77; x ^= x >> 13; x *= 0xC2B2AE3D; x ^= x >> 16, read from verify.rs for this note) would remove the last dependence. Box-hours for the row: 0.02.
6.6 Q4d: the same 400 programs from the spec text at 56eebc0d, text.rs taking nothing from the crate
The audit lane closed the gap of section 6.5 on build master 56eebc0d (01:49 BST, 8 October): 1.4.6.4 now states the closed form in full (x = i XOR S0; x = x * 0x9E3779B1; x = x XOR (x >> 15); x = x + S1; x = x * 0x85EBCA77; x = x XOR (x >> 13); x = x * 0xC2B2AE3D; x = x XOR (x >> 16), 32-bit wrapping) with two pinned vectors (dataset_elem(0x00000fed, 0x9E3779B9, 0x7F4A7C15) = 0x5c7dabd2, dataset_elem(0x0fffffff, 0, 0) = 0x7662c1ec). A diff of the spec between 8b834634 and 56eebc0d shows that block and one clause of the "what the parts catch" paragraph as the only changes. text.rs now carries dataset_elem written from that text and imports nothing from igneum_pow (0 use igneum_pow lines); the binary checks the two pinned vectors before the first seed and exits 4 on a mismatch (binary 47b72eadc5466e8054f5701cbb53881dec6afef9705300ac7be2a30defa84ae4). The run: the same 400 seeds, adv3 textderive --from 0 --count 400 --threads 16 under lease pool 16 --min 8 class adv on build-2, queue file 984, queued 01:51 BST with the pool at 88 of 88 leased, granted 01:52 BST, ended 01:57 BST (log logs/adv-accept-3/984-textderive-56eebc0d.tsv).
Compared over 400 seeds, text at 56eebc0d, nothing from the crate |
Equal |
|---|---|
| The 64 base instructions with every operand and window | 400 of 400 |
| The 256 shadow instructions | 400 of 400 |
| The chosen attempt (0 last resorts) | 400 of 400 |
| The program id | 400 of 400 |
| The rejection-part sequence of the earlier attempts | 400 of 400 |
| Every row identical to the Q4c row for the same seed on every field but the time | yes (cmp of the two logs' first eleven columns) |
Result: 0 of 400 differ, and no sentence of sections 1.3, 1.4.2, 1.4.3, 1.4.6, 1.6, 1.7 or 1.13.1 still needs the crate: text.rs has no use igneum_pow line (file tools/attack/adv-accept-3/src/text.rs, 0 crate imports; the comparison target alone, in main.rs, calls the crate). The pinned vectors held at start. The documentary finding of section 6.2 is closed in full against 56eebc0d: the public spec text now reproduces the shipped class v4 program, attempt and id on every seed tried. Box-hours for the row: 0.02.
7. Q5: era steering (sweep 99)
adv3 era-steer --from 0 --count 400 (DONE, 23:00 to 23:04 BST on build-2, log 99-era-steer.tsv) lists, per era seed of this lane's label space, the stride multiplier (its popcount and NAF weight), the rotation, the interleave, and epoch 0's accepted attempt and minimum ratio under that era.
| Quantity over 400 era seeds | Value |
|---|---|
| Stride multiplier popcount | 9 to 24, mean 16.3; none at or under 4 |
| Stride multiplier NAF weight (adders a fixed datapath pays for a constant multiply) | 7 to 16, mean 11.4; none at or under 6; the weakest 0x080bfbb9 (NAF 7) and 0xf87fa03f (NAF 7) |
| Stride rotation | all 31 values 1 to 31 drawn |
| Interleave (four positions of 16) | 354 distinct of 400 (1,820 possible); the most common three times |
| Epoch 0's accepted attempt under each era | 3 on all 400 eras |
| Epoch 0's minimum stand-in ratio at 256 units under each era | 0.999 on all 400 |
Reading. The era attacker re-rolls one 32-byte value per 180 days and gets an odd 32-bit stride, a rotation and four interleave positions. A weak stride (popcount 1, or NAF weight 1 or 2) sits at 2^-27 to about 2^-22 per try, so at one try per era it never comes; the 400 drawn give nothing under NAF 7, where a datapath multiply by the era constant costs 6 adders against 11 on average, a saving of a few adders in a 55,000-op hash: nothing. What the sweep settles beyond the stride: the era does not steer the PROGRAM. The accepted attempt of the same epoch seed is 3 under every one of the 400 eras, because the static parts (a), (b) and (a') read the instruction list alone, and the dynamic parts' verdict did not move with the address map on this seed; the era changes where the loads go, not which program runs. So the two attacker levers are separable: the epoch seed picks the program (sections 3 and 4) and the era seed picks the address map (this section), and neither buys a chip gain above 1.03x at any quantile measured. BOUND.
8. Box-hours and what a longer pass would add
Spent at 01:58 BST on 8 October, every sweep of this lane ended, nothing holding or waiting on either box: about 3.35 box-hours (the Q4c and Q4d rows of sections 6.5 and 6.6 added 0.02 each). By step: builds 5 x under 1 min; idcheck, margins, exhaust-mirror, ids and two static 10^6 sweeps about 5 min together; the plant run 1 min at 48 threads; steer 19,975 seeds at 88 then 23 to 32 cores, about 1.6 box-hours; verdicts faithful 1,792 seeds, noncyclic 300, floor97 300, spec 400, about 0.7 together; last resort 3,000 seeds 0.3; f8 live census 8 runs at 10^6 and 4 at 2^24 nonces, about 0.5; ratios 300 seeds 0.1. The 8-hour reading line was not reached.
Bound reached, honestly: exhaustion is bounded at 1.0 x 10^-43 per epoch seed from 62,240 candidates of 19,975 full-rule seeds plus 3 x 10^6 static candidates of 10^6 seeds (0 at the cap in either); the last resort is characterised on 3,000 seeds and found to fail the rule itself in 9 percent of them (FINDING, unreachable); steering is bounded over 19,975 full-rule seeds and 10^6 static seeds with the worst live hot set of eight measured at 1.0008x and no static property buying over about 1.03x at one seed in a million; the id is bounded at 0 collisions in 10^7 pairs with the stated derivation wrong (FINDING); determinism is bounded by a second interpretation agreeing on 5,748 of 5,748 verdicts with a fired control, the f64 ratio safe by 0.32 counts at the shipped constants, and the spec text diverging on 264 of 400 programs (FINDING, documentary).
What a longer pass would add, not a reason to wait: the full-rule steering sweep to 10^5 seeds (about 8 box-hours at 32 cores) to read the stand-in ratio and the (c) tails at 10^-5, with the live census on the lowest twenty at 2^24 nonces; a live census at scale (3 s per seed on 32 cores) as the grinder's true selector, since the stand-in ratio's floor is noise; the ratios sweep to 3,000 seeds to put an error bar on the 0.967 to 0.988 gap; a chosen-id preimage attempt through seed_words_from_bytes (a 2^32-class meet in the middle on FNV-1a-64 is the published shape, the murmur finaliser the obstacle); and the era-steer listing to 10^4 eras for the stride's weak classes at 2^-22.
8b. Ledger: the 20:22 BST kill
Main's rule (relayed 20:2x BST): no sweep starts except through /srv/builds/_bin/lease pool <threads> -- <cmd>; hand-started binaries are killed by their pid files now. Killed at 20:22 BST by pid file (process groups): sweep 94 on build-1 (verdicts faithful, 88 threads, running 8 min, pid group 1148339) and its four flock waiters (95, 97, 98, 99); sweep 93 on build-2 (steer, 88 threads, running 1 min, pid group 2114475) and waiter 96. Nothing lost: both logs were written row by row and are kept as data (logs/adv-accept-3/94-verdicts-faithful-part1.tsv, 93-steer-s00-part1.tsv); the sweeps resume through the lease on the seed ranges not yet covered. The sweep.lock flock is superseded. Pool rule of 20:2x BST (release builds and class v5 suites outrank every sweep; no adversarial lease competes with a waiter labelled v5 gate or v5 kit): at 20:26 BST the seven leases became two chunked drivers per box (bin/chunks.sh, bin/seq.sh under /srv/builds/_adv-accept-3/): every chunk (1,000 steer seeds, 200 verdict seeds, 500 last-resort seeds) is its own lease pool 40 --min 16 --owner adv-accept-3 call with --threads {cores}, and the driver sleeps while any waiter in lease status carries one of those two labels. Box 2: steer s00 then s01. Box 1: verdicts faithful, floor97, spec, era-steer in sequence, and lastresort beside them.
20:41 BST: main's order through the coordinator: box 2 must show no adv- holder or waiter so the class v5 (c''') census can start. The steer chunk 3000 to 3999 had ended and released; the box-2 driver was stopped by pid and resumes from seed 4000 on the coordinator's word. 20:40 BST: the lease tool gained priority classes (release > v5 > measure > adv; an adv holder above 32 threads is pre-empted after a higher class has waited 120 s), so every chunk of this lane asks for 32 with --min 16, and the two build-1 waiters started under the old tool were killed and re-submitted once at 20:43 BST.
22:47 BST: sweeps 97 (spec variant) and 99 (era-steer) moved from build-1, where the one remaining adv waiter had sat since 22:23 BST at 0 free cores, to build-2, which had served and finished the two sweeps moved there at 22:08 BST (build-1's waiter and driver killed by pid first). Build-1 now holds nothing of this lane; every remaining run is on build-2.
22:21 BST: main's pre-emption rule (a release or v5 waiter that has waited 120 s pre-empts adv holders at any size with SIGTERM, oldest first). The harness writes one row per seed as it lands, so a pre-empted chunk keeps its partial; the driver became chunks5.sh (in the tree), which re-queues the same lease line when the lease returns non-zero (up to 6 tries per chunk; the analysis deduplicates rows by seed). Restarted under it at 22:23 BST: 981 and 982 on build-2 (still holding in their yield loop for v5 waiters), 97 then 99 on build-1. Steer s01 keeps its running chunks3.sh driver (a pre-empted chunk of it would be lost, not re-queued; the seeds it covered are read from the log and any gap is re-run at the end). Operations note: the restart on build-1 first left the old 97 waiter in the pool beside the new one (a pattern that missed its pid); the old one was killed by pid 20 s later, so no chunk ran twice.
22:08 BST: sweeps 981 (noncyclic) and 982 (ratios) moved from build-1, where every adv waiter had sat 25 minutes at 0 free cores, to build-2 (the same chunk drivers through lease pool 32 --min 16, build-1's waiters killed by pid first); on arrival they held in their yield loop for a class v5 waiter ("v5 gate: F8 64 x 2^24", owner attack-pass) as the rule requires. Sweep 97 (spec variant) and 99 (era) stay queued on build-1 in order; steer s01 holds 24 cores on build-2 (chunk 12,000 at 21:57 BST).
20:30 to 20:33 BST: the yield test was refined twice by the coordinator (any waiter with "v5" in its label; then by owner: class-v5, or attack-pass with "v5" in the label, never an adv- owner) and the drivers restarted each time as chunks3.sh; steer s00 resumes at seed 4000 (seeds 1000 to 3999 were covered by the lease chunks before the restarts, one 1,000-seed chunk at 23 cores left to finish and release). Operations note for the class record: the second restart's kill loop on build-1 used pgrep -f with a literal pattern that sat in the calling shell's own command line, ended that shell, and left three orphan waiting leases; they were killed by explicit pid at 20:33 BST and the drivers started again. Nothing ran twice on the pool and no row was lost.
9. Queue files (all on build-2 under /srv/builds/_adv/accept/queue/, claimed under claims/)
| File | Box | Command | State at 20:20 BST |
|---|---|---|---|
| 91-adv-accept-3-seed-steering.sh (definition) | implemented as 93 and 96 | claimed | |
| 92-adv-accept-3-program-id-determinism.sh (definition) | implemented as ids, idcheck, margins, 94, 97, 98 |
claimed; ids, idcheck, margins done | |
| 93-adv-accept-3-steer-s00.sh | 2 | steer 0..10000 | DONE 21:41 BST (9,975 seeds; 975 to 999 fell between the kill and the first chunk) |
| 94-adv-accept-3-verdicts-faithful.sh | 1 | verdicts faithful 0..2000 | DONE 21:40 BST (1,792 seeds; 792 to 999 fell between the kill and the first chunk) |
| 95-adv-accept-3-lastresort.sh | 1 | lastresort 0..3000 | DONE 21:51 BST |
| 96-adv-accept-3-steer-s01.sh | 2 | steer 10000..20000 | DONE 22:43 BST |
| 97-adv-accept-3-verdicts-spec.sh | 1 then 2 | verdicts spec 0..400 | DONE 23:00 BST on build-2 |
| 98-adv-accept-3-verdicts-knownfailed.sh | 1 | verdicts floor97 0..300 | DONE 21:42 BST: did not fire (section 6.4) |
| 981-adv-accept-3-verdicts-noncyclic.sh | 1 then 2 | verdicts noncyclic 0..300 (the replacement known-failed shape) | DONE 22:30 BST on build-2: fired, 4 verdicts and 3 programs changed |
| 982-adv-accept-3-ratios.sh | 1 then 2 | ratios 0..300 (the (c'') ratio at 2^20 of every dynamic candidate) | DONE 22:45 BST on build-2: the floor sits in a 0.023 gap |
| 983-adv-accept-3-textderive.sh | 2 | textderive 0..400 against the spec text at 8b834634 (Q4c) |
DONE 01:15 BST, 8 October: 0 of 400 differ |
| 984-adv-accept-3-textderive-56eebc0d.sh | 2 | textderive 0..400 against the spec text at 56eebc0d, nothing from the crate (Q4d) |
DONE 01:57 BST, 8 October: 0 of 400 differ, 0 crate imports |
| 99-adv-accept-3-era-steer.sh | 1 then 2 | era-steer 0..400 | DONE 23:04 BST on build-2: the lane's last run |
| 991-adv-accept-3-f8-live.sh (DONE 20:37 BST) and 992-adv-accept-3-f8-live-2.sh (DONE 21:53 BST) | 1 | f8 warps (seed mode added, census code untouched; binary attack-f8 918f80a5fee5d2c349c90794b825a731ff16084c833c2f76f810a0e9ff5b0d80) on the const-item plant, seed 0 as control, then steer seeds 514, 522, 620, 703, 959 at 10^6 nonces, day 20733 |
claimed 20:30 BST, waiting |