adv-accept-2: rotclass census command; queued 1e8 tail and prevalence pass noted in the report

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 19:34:27 +00:00
parent 93a789f651
commit 34da77c99a
2 changed files with 48 additions and 0 deletions

View file

@ -117,6 +117,10 @@ Result: PASS with one FINDING of 0.1 percent (a data-dependent rotate by a possi
| Home miner (one card, any size, any vendor, any OS), rig, pool user | Nobody gains from grinding headers for locality: the best group a search can find runs 0.09 percent faster on a card and costs 15 hashes to find. Mining stays one header, every nonce. The drawn:0 repeat is 0.1 percent for every miner alike, no tier favoured | Nothing to change in the miner. The repeat class is handed to the rule's owners as a one-line note (a rotate by a register amount as the sole write between two loads from one register) |
| A chip that stores the dataset (chip-model-v3 5) | Header choice gives it nothing either; its per-hash read count stays 128 (Q2) and the items per unit stay at 4,095 of 4,096 | The analytic bound of Q3 and the card point stand on their own |
## Queued (lease pool, build-1)
At 20:32 UK one `lease pool 32 --min 8` job (owner adv-accept-2, label "adv-accept-2 1e8 locality tail + rotclass census", pid file /srv/builds/_adv-adv-accept-2/lease-1e8.pid, log lease-1e8.log) was queued behind the attack-pass F9 class v5 exhaustion leases (0 of 88 pool cores free at 20:33 UK). It runs 33 single-thread jobs at the leased width: 16 shards of 1e8 hashes on each real program (per-shard logs rows-devnet-1e8-s<k>.log, rows-devnet3-1e8-s<k>.log: the 1e-6 tail reading on the same windowed baseline) and the rotate-identity prevalence census over 300 drawn accepted programs (rotclass-300.log). Rows are appended here when they land. Binary sha256 003e540eee23c8621eb1282c2c9a1b4aca981e81337daa20f63e8469d01b1e7e.
## What a longer pass would add
A 1e8-hash sweep per program moves the 1e-5 tail to a 1e-6 reading on the same baseline; a sweep over 1,000 drawn programs counts how often the rotate-identity repeat class appears and at what strength (the two real programs show none; one of eight drawn does); a card point on an RTX 5090 instead of the A6000 reproduces the 0.09 percent at the production read rate. None of these changes the bound.

View file

@ -461,6 +461,49 @@ fn cmd_export(prog: &str, hashes: u64, units: usize, plant: Plant, best: bool, o
log!("export: wrote {} units ({} bytes) to {out}, prog={prog} plant={} best={best}, mean distinct 8 KiB rows {:.2}", units, bytes.len(), plant.name(), r8sum as f64 / units as f64);
}
/// Prevalence of the rotate-identity repeat class (the drawn:0 finding): over `count` drawn class v4 programs through
/// the chain draw path (accepted programs), count ordered pairs of load sites (i before j, cyclic over the 64 base
/// instructions plus the shadow block in execution order) that read the SAME source register where every write to that
/// register between them is a `rotr` (identity when its amount register AND 31 is 0: probability 1/32 each). Any other
/// op between them (add, sub, xor, or, mul, mulhi, mad, shfl, load, rotl by 1..31) changes the value except with
/// probability about 2^-32 and ends the chain. Reports pairs, the identity probability per pair, and programs affected.
fn cmd_rotclass(count: u64) {
let (mut affected, mut pairs_total) = (0u64, 0u64);
let mut by_k: std::collections::BTreeMap<usize, u64> = std::collections::BTreeMap::new();
for k in 0..count {
let (p, _) = program_of(&format!("drawn:{k}"));
// one iteration in execution order: base then shadow x reps; a second pass closes the wrap
let order: Vec<&igneum_pow::generator::Instr> = p.instrs.iter().chain((0..p.shadow_reps()).flat_map(|_| p.shadow.iter())).collect();
let mut found = Vec::new();
for (ai, a) in order.iter().enumerate() {
if !a.op.is_load() { continue; }
let reg = a.src;
let mut rotrs = 0usize;
let n = order.len();
for step in 1..n {
let b = order[(ai + step) % n];
if b.op.is_load() && b.src == reg {
found.push((ai, (ai + step) % n, rotrs));
break;
}
if b.dst == reg {
if b.op == Op::Rotr { rotrs += 1; continue; }
break;
}
}
}
// a pair with zero rotr writes cannot exist (rule (a) forces a write between two loads from one register)
let real: Vec<_> = found.iter().filter(|(_, _, r)| *r > 0).collect();
if !real.is_empty() { affected += 1; }
for (a, b, r) in &real {
pairs_total += 1;
*by_k.entry(*r).or_insert(0) += 1;
if real.len() <= 4 { println!(" drawn:{k} id={:#018x} attempt={}: loads at order {a} and {b} read one register with {r} rotr write(s) between: identity probability 32^-{r}", p.program_id(), p.attempt); }
}
}
log!("rotclass: {count} drawn accepted class v4 programs: {affected} affected ({:.2} percent), {pairs_total} load-site pairs whose only intervening writes are rotr; by rotr count: {:?}", 100.0 * affected as f64 / count as f64, by_k);
}
/// Q1b: the predictable prefix. In iteration 0, a load site whose source register has no dataflow path from an earlier
/// load's result can be addressed from (I, n) with ALU work alone. Taint: a load taints its dst; add/sub/xor/mad/shfl/
/// or/mul/mulhi/rotr propagate taint from any operand (shfl from the lane group); rotl keeps the dst's taint. Reported
@ -544,6 +587,7 @@ fn main() {
"prefix" => cmd_prefix(&args[2..]),
"repeats" => cmd_repeats(&prog, hashes),
"dump" => cmd_dump(&prog, 64),
"rotclass" => cmd_rotclass(hashes),
"export" => cmd_export(&prog, hashes, units_arg, plant, best, &out_arg),
"idcheck" => cmd_idcheck(hashes, pairs),
_ => usage(),