igneum/docs/analysis/cryptanalysis/report-acceptance-rule.md
2026-10-07 18:56:01 +00:00

21 KiB

Report: adversarial pass on the program acceptance rule (class v4 sub-version 3)

internal adversarial pass, not an independent review

  • Target commit: 017e703764 (class v4 sub-version 3, object byte 7).
  • Crate built: igneum-pow at the frozen commit (this worktree's igneum-pow/ is reset to it; build/master diverged by 635 deletions and is not used). Harness: tools/attack/adv-accept (depends on igneum-pow by path).
  • Re-base (19:3x BST): build/master moved to 7a7caa34 whose igneum-pow IS the frozen object; merged at 8748e955ceb48be5a6cbf7f8718a4884d3de9828; git diff --quiet 017e7037 HEAD -- igneum-pow prints IDENTICAL. Nothing measured here was on a stale tree: igneum-pow/ had been reset to the frozen object before the first build.
  • Binary sha256 (first build, box 2): e3d35f4464937f91aac0648e2ee7134f33c85c7aa314b87b33f91059dedc6682 (adv-accept single-binary build); the two-binary build (adv-accept, adv-live = the unmodified f8 harness) sha256 is logged per box in the run sections below.
  • Boxes: igneum-build-1 (CPU-bound sweeps, by the coordinator's order) and igneum-build-2 (shard 0, already there). nice 10 on every idle core, the capacity layer's yield (run-box.sh). Logs under /srv/builds/igneum-wt-adv-accept/adv/ on each box (spared from the checkout clean by .igneum-scratch-spare; the first 10k log on box 2 was wiped by a rebuild before the spare existed and is void anyway: old labels, old binary).
  • Lane scope since 19:2x BST: THE BYPASS (Q1, Q2, Q5). Q3 is lane adv-accept-3's, Q4 lane adv-accept-2's.
  • Seed space: the attack-pass F8 label space (program k = seed_words("igneum-attack-f8/program/k"), era ".../era/k"), so adv-live warps --program k measures the live hot set of exactly the program the sweep reports.
  • rustc 1.99.0 both sides. First results by 8 October 2026 18:00 UTC. GPU: not available, so any per-card hash-rate confirmation of a gain is BLOCKED and said so.
  • Box-hours spent so far: about 0.1 (one 7 s build, three short runs). Budget 8.

Draw-path validation (must pass before any claim)

The shared devnet epoch-0 class v4 program drawn through Epoch::chain_program(ProgramClass::V4) has program id a785001687d8688a at attempt 1 (class mx8-erad810f22d+sh256x27), which matches the frozen pack proto-cuda/packs-ca3-v4/v4-devnet-epoch0/program.json and the brief. So my draw path is the chain's. Command: adv-accept derive-check on box 2. Devnet 3 epoch-0 (id fce15bf61030be57) derive check is owed once I copy its program.json read-only from build-1.

Status board

Q Method Known-failed shape (must fire) Gate Result Status
Q1 Steering/hot set: the chain's class v4 draw over the F8 label space (shards of 100k seeds; 19:27 BST to 19:4x BST under the SIGSTOP yield, since 19:4x BST nice 10 on cores 8-95, no yield), the stand-in per-site ratio at 256 units as the proxy, the live hot set by the unmodified f8 harness (adv-live) on the lowest-ratio seeds and on a consecutive-seed census adv-live const-item plant FLAGGED (21.8x, X_1% +6.49); clean control 0.9996x PASS X_f >= f flags a hot set; gain = implied on-die SRAM copy size seed 100767 (of 4,600 accepted) flags the hot-set test at 2^24 nonces: X_0.1% +0.155%, 2.05x; four lowest-ratio seeds all BEYOND the f8 1.2x gate live; 27 random accepted programs: 0 beyond, 0 hot sets; gain 1.002x (1 MB copy serves 0.31% of loads) FINDING, bounded: a distinguisher and a seed selector, not an exploitable bypass; sweep RUNNING
Q2 Stand-in gap: a validated mirror runs the rule's own units on the closed form and on the live dataset; per-site ratio at 2^20 and the (c) metrics compared zero-dataset plant: live 0.9733 REJECT against closed accept (fired) any false accept that reads a hot set on the live set 4 + 6 programs so far: max site gap 0.0003, 0 verdict disagreements BOUND (widening to 50 RUNNING)
Q3 Handed to lane adv-accept-3 (unspawned); row 90 (exhaustion census: per-part rejections, cap, last resort) claimed by this lane, owner adv-accept-3 forced-exhaust plant must hit the cap and print the last-resort program P(exhaust) bounded; last resort characterised attempts census over 20k seeds RUNNING (box 2); plant RUNNING RUNNING (owner adv-accept-3, unspawned)
Q4 Handed to lane adv-accept-2 HANDED OVER
Q5 Generator distinguishers over accepted programs: lossy last write, register-set collapse, the per-site ratio tail against the 0.98 floor the five lowest-ratio seeds reproduce as live tail programs (done); F8 p15/p18/p19/p56 reproduction owed a structural shape that lowers the live distinct-item count first 864: lossy last write in 83%, 0 register collapse, min ratio 0.9986 at 256 units; at 2^20 the tail reaches 0.9832 (floor 0.98) RUNNING
Q6 Fixed (c) sample grindability; live-size invariance of the (c) verdict BOUND unless something fires not started PENDING

The plant (known-failed shape for the harness itself) fired: planting an or write of a load's source register immediately before the load makes the rule reject with "(a') load at 1 reads r4, not fresh by dataflow in the loop's steady state". So the harness reads the real rule, not a copy. Command: adv-accept plant --seed 0 on box 2.

Q1 and Q5: the accepted-program sweep (RUNNING, sharded)

Ten shards of 100,000 seeds (k = 2..1,000,001 of the F8 label space), each adv-accept sweep --seed-start S --seeds 100000 --threads 96 --ratio-units 256 --out /srv/builds/igneum-wt-adv-accept/adv/sweep-sNN.txt, started through run-box.sh (nice 10, yield to builds, pid/pgid/yield pid files beside the log). Shard 00 (S = 2) on box 2, started 19:27 BST. Shard 01 (S = 100,002) on box 1, started 19:30 BST. Shards 02..09 sit as self-contained scripts in /srv/builds/_adv/accept/queue/ on box 2 (claim by mkdir under claims/), to be run back to back on whichever box has idle cores. Every row is written as it lands, so a partial shard is data.

Live hot set (the measure the chip model prices): adv-live warps --program k --nonces N (the unmodified f8 harness: the day's items derived into a table, the cross-hash item histogram, the hot-set test X_f >= f against the window-model control, the 6-sigma test, library agreement checks). Known-failed shape run first: --plant const-item on program 2 at 10^6 nonces (box 1, 19:30 BST, log adv/live-plant-p2.log) beside its clean control (adv/live-control-p2.log). The plant must be FLAGGED and the control clear before any live number below is trusted.

Seeds: epoch bytes = LE words of seed_words_from_bytes("igneum-attack-f8/program/k"), era bytes of ".../era/k"; each drawn through the chain path generate_era(V4_CLASS, V3_ALLOWED), which runs the full attempt loop and the real acceptance rule.

Per accepted program the sweep records: attempt, program id, the closed-form distinct-item mean per hash (the rule's own (c) metric; 128 is ideal, the rule floor is a mean above 120), the minimum per-site distinct-index ratio at 256 units (the (c'') metric sampled cheaply; the enforced floor is 0.98 at 2^20 units), the saturated-final count and the output-bias max.

This is the closed-form proxy for Q1 and the headroom characterization for Q5. The live-dataset hot set (the memory-hard cache, items derived into a table, the cross-hash histogram with the hot-set test) is the next run and is what the chip model prices; the closed-form distinct mean is a cheap upper bound on how concentrated an accepted program can be on the stand-in.

Numbers land here when the run finishes (first results by 8 October 18:00 UTC).

Results as they land

Tool validation (box 1, 19:30 BST, 10^6 nonces each, about 30 s per run on 32 threads)

Run Hot-set test (window-model control) Top 0.1% share ratio over the window model 6-sigma Verdict
adv-live warps --program 2 --plant const-item (known-failed shape) f 1%: S_f 8.58%, E_f 2.09%, X_f +6.49% (X_f/f 6.49) HOT SET 21.80x (flat control 26.85x) FLAGGED FLAGGED
adv-live warps --program 2 (clean control) f 1%: S_f 2.56%, E_f 2.09%, X_f +0.47% (X_f/f 0.47) no hot set 0.9996x (flat 1.2312x, the window layer) clear PASS

The plant fires and the control is clean, so the live hot-set numbers below are trusted. Logs adv/live-plant-p2.log and adv/live-control-p2.log on box 1.

Sweep shard 01, first 864 accepted programs (box 1, seeds 100,002..., read at 19:33 BST)

Metric Value
Attempt histogram 0..9 273, 191, 134, 90, 59, 38, 34, 12, 7, 11; max 26
Min per-site distinct-index ratio at 256 units 0.9986 (floor 0.98 at 2^20; every row above 0.998)
Programs with a lossy (or/mul/mulhi) last write to an output register 715 of 864 (83%)
Programs writing fewer than 8 registers 0
Last-resort programs 0
Lowest-ratio seeds (live hot set queued) 100211 (0.9986), 100629 (0.9987), 100064, 100767 (0.9988), 100159

Reading: on the stand-in the accepted population sits 0.02 above the 0.98 floor at this sample size; the floor leaves headroom (Q5), and no accepted program in this sample comes near it. Whether any of the lossy-last-write programs concentrate reads on the LIVE set is what the live census answers.

Rule change in the method column (box-hours honesty)

19:27 BST to 19:4x BST: shards ran under the capacity layer's SIGSTOP yield and were paused almost the whole time (a build slot held nearly continuously on both boxes). Ruling at 19:4x BST: yield dropped, every sweep at nice 10 on cores 8-95 (cores 0-7 reserved for release builds, the seed and the observer). Shards 00 and 01 SIGCONTed and re-pinned at 19:4x BST. Box-hours before the ruling: about 0.1 of running time.

Live hot set of the five lowest-ratio shard-01 programs (box 1, 19:37 BST, 10^6 nonces each)

All five pass the rule (they are the chain's accepted programs). Log logs/adv-accept/live-lowratio-s01.log. Columns: the f8 gate (top 0.1% share over the window-model control, gate 1.2x), the hot-set excess X_f at f = 0.1% and 1% (a hot set needs X_f >= f), the windowed 6-sigma test, the hottest item and its traced source.

Seed id attempt closed ratio (256 u) top 0.1% over window model X_0.1% / X_1% 6-sigma (buckets64) hottest item, reads, share, source
100211 d07885a437e237c7 0 0.9986 1.08x +0.026% / +0.049% +42.4 sigma FLAGGED 0x454585, 1238 (0.0010%), site instr 4 reads r0 one-zero-bit, writer load@3
100629 37c849d9741c5994 0 0.9987 1.01x +0.003% / +0.015% +5.6 within 30 reads, none
100064 2442abf9d56f6611 2 0.9988 1.17x +0.044% / +0.189% +77.3 sigma FLAGGED 54 reads; the flag is a 256-item bucket at p43 (iteration 2, site 11, instr 33) holding 0.048% of that position's reads against 0.0061% expected (8x)
100767 9d68e6286fc817d4 2 0.9988 1.34x BEYOND the 1.2x gate +0.087% / +0.138% +68.0 sigma FLAGGED 0x000000, 1677 (0.0013%), site instr 6 reads r6 = zero, writer mad@4; saturated-source share 0.013% at that position (the (c') limit is 1%)
100159 573d650159a8b04e 3 0.9988 1.07x +0.020% / +0.124% +37.5 sigma FLAGGED 37 reads, none
p2 control (19:30 BST) f8 seed 2 0.9996x +0.00% / +0.47% (flat) clear 32 reads

Reading, as an attacker. (1) The stand-in ratio does correlate with live concentration: four of the five lowest-ratio seeds flag the windowed 6-sigma test at 10^6 nonces where the random control is clear, and one sits beyond the f8 gate. (2) The concentration is NOT a hot set a chip can use: the top 0.1% of items (16.7k items, 1 MB) take at most 0.34% of reads against 0.26% expected; the hottest single item takes 0.0013% of reads. An on-die copy of these items saves under 0.1% of DRAM reads: no row of chip-model-v3.md moves. (3) The mechanism is the known one, a near-saturated source (zero, or one bit off all ones) at one site in one iteration, below the (c') 1% limit (0.013% here), so (c') never fires, and the per-site ratio at 0.98 is loose enough (these sit at 0.9986) that (c'') never fires either.

FINDING (bounded): seed 100767 at 2^24 nonces passes the rule and flags the f8 hot-set test (box 1, 19:4x BST)

Reproduce in one command, from the frozen crate (017e7037) with the unmodified f8-uniform harness built as adv-live (tools/attack/adv-accept, cargo build --release):

adv-live warps --program 100767 --nonces 16777216 --threads 32 --diag 1 --validate sample --out <dir>

The program is the chain's class v4 draw for epoch seed bytes = the little-endian words of seed_words_from_bytes("igneum-attack-f8/program/100767") = 74484b391756fc49568cdd716bcfd839a55d31a45633c223c65c7f2ab4617fd4 and era seed bytes = the same of "igneum-attack-f8/era/100767" = 7a65a05391dcd87b8fdaf7f74813aa17c6e13f7c56a03da2aaa6176987ef038b (f8's program_spec(k) for k >= 2; adv-accept gap --seeds 100767 prints the same id), through generate_era(V4_CLASS, V3_ALLOWED): attempt 2, program id 9d68e6286fc817d4, class mx8-era763e5847+sh256x27, day bytes "igneum-day/" || 20730_le64 (f8's default day). The run's log is logs/adv-accept/live-confirm-16m.log in this branch (copied from box 1, /srv/builds/_adv-adv-accept/live-confirm-16m.log); the harness checks its mirror against Epoch::hash_warp on 64 warps plus every 997th (0 mismatches in the log). Accepted by every part of the rule: it is the program try_generate_class returns for the seed (attempt 0 and 1 rejected).

Measure Value Uniform / control
Hot-set test f = 0.1% S_f 0.303%, E_f 0.148%, X_f +0.155%, X_f/f 1.55: HOT SET X_f >= f fires
Hot-set test f = 0.5%, 1% X_f +0.266% (X/f 0.53), +0.325% (0.33): no hot set
Top 0.1% share over the window-model control 2.05x (0.5%: 1.37x, 1%: 1.23x); flat control 2.31x f8 gate 1.2x; population 0.998x to 1.043x (14 random accepted programs, census, 10^6 nonces)
Windowed 6-sigma, 64-item buckets largest bucket +294.8 sigma population +4.4 to +30.6 at 10^6 nonces
Hot items (top 0.1% = 17,034 items, 1.0 MB) 6,579,906 of 2,147,483,648 reads (0.306%) 0.148% expected
Top 8 items 0x000000 (29,355 reads), 0x200000, 0x300000, 0x180000, 0x100000, 0x080000, 0x380000, 0x0c0000: multiples of 2^19, read almost only from site 6 in every iteration
Site attribution site 6 (instr 23, src r6, quarter window) puts 3.35% of its reads into the hot items; site 7 (instr 32, src r0) 0.41%; every other site 0.00 to 0.21% (expected 0.10%)
Site 6 source r6, last written by mad at instr 4; saturated (zero) in 0.0126% of evaluations at that position (the (c') limit is 1%); the hot items are the images of small source values under the era stride

Gain, priced against chip-model-v3.md section 5.7: an on-die copy of the top 0.1% of items (1 MB of SRAM) serves 0.31% of this program's loads instead of 0.15%, so it removes 0.16% of DRAM reads. The f = 1 chip's rate is lanes over latency per read; 0.16% fewer reads is a gain of 1.002x. No row moves. The rule has let through a program with a measurable, attributable hot set, and the hot set is worthless to a chip. The distinguisher is real; the bypass is not exploitable at this size.

The other three lowest-ratio seeds at 2^24 nonces (same log): all BEYOND the f8 1.2x gate on the live set, none a hot set by X_f >= f.

Seed top 0.1% over window model X_0.1% (X/f) 6-sigma hot-set verdict
100211 1.33x +0.066% (0.66) FLAGGED clear
100064 1.63x +0.094% (0.94) FLAGGED clear
100159 1.29x +0.048% (0.48) FLAGGED clear
23 random accepted programs (census, 10^6 nonces) 0.998x to 1.043x, 0 over 1.2x 5 of 14 flagged weakly (+6 to +31)

So the stand-in's per-site ratio, read at 256 units in the sweep, is a working proxy for live concentration: the five lowest of 4,600 all fail the f8 gate on the live set while the population passes it. A seed-steering attacker (lane adv-accept-3's question) has a cheap selector. What it buys is the number above: a 1 MB hot set holding 0.3% of reads.

What a larger search adds: the sweep ranks seeds by the stand-in ratio, and the worst of 4,600 accepted programs gave X_0.1% = 0.155%. If the tail scales as the extreme of the population, 10^6 seeds reach a few times that, still under 1% of reads. The census over consecutive seeds (random accepted programs) says how often the hot-set test fires in the population; that number lands below.

The selector's base rate (sweep rows read at 19:5x BST: 16,337 accepted programs, shards 00 and 01)

The stand-in per-site ratio at 256 units, over every accepted program so far (one row per seed; every seed of the F8 label space yields an accepted program through the chain draw):

Quantile min 0.1% 1% 5% 25% median 75% max
ratio 0.9945 0.9980 0.9993 0.9997 0.9998 0.9999 0.9999 1.0000
Threshold Programs under it Tries per hit
ratio < 0.9990 85 of 16,337 1 in 192
ratio < 0.9988 54 1 in 303
ratio < 0.9986 (the five confirmed seeds sat here) 34 1 in 480

Cost of the selector: one chain draw per try (about 3 s of one core: the accepted attempt's 2^20 ratio pass dominates) plus a 256-unit ratio read (milliseconds), so a seed under 0.9986 costs about 25 core-minutes of grinding, and a seed-steering attacker who can choose among epoch seeds needs about 500 candidate seeds per hit. The five confirmed hits at that threshold all failed the f8 1.2x gate live; the random sample's live failure rate is 0 of 27 so far (census), so the gate-failure rate conditional on the selector is 5 of 5 against 0 of 27 unconditional. The widened rows (20 lowest, 20 random, at 2^24 nonces) turn this into a correlation with its error when they land (live-low20-16m.log on box 1, live-random20-16m.log on box 2).

Attempt histogram over the 16,337 (accepted 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. Q5 structure: a lossy last write to an output register in 13,662 (83.6%), register-set collapse 0.

Row 90 (owner adv-accept-3, unspawned; claimed): the attempt loop, the cap and the last resort

Known-failed shape fired (box 2, 19:50 BST, adv-accept attempts --seed-start 2 --seeds 3 --force-exhaust, log adv/attempts-plant.log): with every verdict read as a rejection the loop hits its cap and hands the last-resort program, which the tool prints and re-checks. The three last-resort programs (cap 8 under the plant): every or, mul and mulhi rewritten to xor (op mix xor 17 to 19, load 16, 0 lossy ops, 8 registers written), and the real rule accepts each one as drawn (distinct mean 127.86 to 128.00, 0 saturated, bias max 58 to 66). Reading: the last resort is predictable (a deterministic function of attempt 256's draw) and is NOT weak by the rule's own measures; it is a program with no lossy op at all, which the live hot-set measure has not yet been run on (owed). The 20k-seed census of real attempts (per-part rejections, cap reached, P(exhaust)) is running (attempts-20k.log); its numbers land here.

Q2, the stand-in gap: first bound (box 2, 19:46 BST, adv-accept gap)

Tool: a mirror interpreter with per-site index capture, run on the rule's own base nonces (seed-keyed acceptance stream) with init words = seed words, once on the closed-form dataset and once on the LIVE memory-hard dataset (day 20730, the kit's day). Validation on every program: the mirror's 32 hashes equal the library's hash_warp on 8 units on both datasets, and the mirror's closed-form per-site distinct counts equal the library's distinct_indices_v4 on all 4096 units. Known-failed shape --plant zero-dataset (every live word 0): live min ratio 0.9733, verdict live REJECT against closed accept, live distinct mean 115.99 against 128.00: the tool reports a gap when there is one. Logs adv/gap-plant.log, gap-first.log.

Seed id min per-site ratio closed (2^20) min live (2^20) largest site gap (c) on 64 units: distinct mean, saturated, bias max (closed / live) verdict closed / live
100767 (the finding) 9d68e6286fc817d4 0.9919 0.9920 +0.0002 127.714, 0, 82 / 127.730, 1, 53 accept / accept
2 (control) 8a0047b2eb071ade 0.9999 0.9999 -0.0002 128.000, 1, 56 / 128.000, 0, 57 accept / accept
100211 d07885a437e237c7 0.9907 0.9908 +0.0002 128.000, 0, 61 / 128.000, 0, 57 accept / accept
100064 2442abf9d56f6611 0.9832 0.9831 +0.0002 128.000, 11, 64 / 128.000, 6, 51 accept / accept

Reading. The per-site index distribution is set by the program and the register init, and the dataset words it folds in act as fresh randomness on both sides: the closed form and the live set give the same ratio to 2 parts in 10,000 at 2^20 evaluations, and the same (c) verdicts. No stand-in gap to steer into on these four; the 50-program widening is running (gap-50.log). Also: the cheap 256-unit proxy did rank real tail programs, and 100064 sits 0.0032 above the 0.98 floor at 2^20, so the floor is live in the population tail, not idle.

Live hot-set census (RUNNING)

  • box 2, 19:5x BST: adv-live census --programs 2..201 --nonces 1000000 --threads 32 (200 consecutive accepted programs of shard 00's range: the random sample), log adv/live-census-s00-2-201.log.
  • box 1, 19:5x BST: adv-live warps --program k --nonces 1000000 for the five lowest-ratio shard-01 seeds, log adv/live-lowratio-s01.log.

Running notes

  • Box-hours and commands are logged in each section with the seed, so every claim is reproducible by re-running the exact command on box 2.
  • No cargo on the Mac. Pushes to the build mirror only. Commits as igneum-labs.