From 667296f14f39a2e36cf799b8fc6d534c9edad6df Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:16:09 +0000 Subject: [PATCH] adv-cache-3: rank sampling fix, run-box.sh under the per-box sweep lock Co-Authored-By: Claude Fable 5.1 --- tools/attack/adv-cache-3/Cargo.lock | 14 ++++++++++++++ tools/attack/adv-cache-3/run-box.sh | 17 +++++++++++++++++ tools/attack/adv-cache-3/src/main.rs | 16 ++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 tools/attack/adv-cache-3/Cargo.lock create mode 100755 tools/attack/adv-cache-3/run-box.sh diff --git a/tools/attack/adv-cache-3/Cargo.lock b/tools/attack/adv-cache-3/Cargo.lock new file mode 100644 index 000000000..61e969399 --- /dev/null +++ b/tools/attack/adv-cache-3/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "attack-adv-cache-3" +version = "0.1.0" +dependencies = [ + "igneum-pow", +] + +[[package]] +name = "igneum-pow" +version = "0.2.0" diff --git a/tools/attack/adv-cache-3/run-box.sh b/tools/attack/adv-cache-3/run-box.sh new file mode 100755 index 000000000..d86eab7a6 --- /dev/null +++ b/tools/attack/adv-cache-3/run-box.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Start one adv-cache-3 sweep on a build box (run ON the box). Internal adversarial pass, not an independent review. +# run-box.sh +# The binary is /srv/builds/_adv-cache-3/bin/adv-cache-3 (copied there after tools/build-remote.sh; the worktree mirror's +# target dir is not used at run time). The log and pid file live under /srv/builds/_adv-cache-3/logs/.log{,.pid}, +# outside the worktree mirror, which build-remote.sh's source sync would wipe. The run takes the per-box sweep lock +# (/srv/builds/_adv/locks/sweep.lock, coordinator's rule of 19:55 BST, 7 October 2026: one sweep per box at a time, up to 88 +# threads), runs at nice 10 on cores 8 to 95 (0 to 7 reserved), no SIGSTOP yield. Kill by pid file only: kill $(cat ). +set -euo pipefail +tag="$1"; shift +BIN=/srv/builds/_adv-cache-3/bin/adv-cache-3 +DIR=/srv/builds/_adv-cache-3/logs +mkdir -p "$DIR" +log="$DIR/$tag.log" +nohup flock /srv/builds/_adv/locks/sweep.lock -c "nice -n 10 taskset -c 8-95 $BIN $* >> '$log' 2>&1" > /dev/null 2>&1 & +echo $! > "$log.pid" +echo "started $tag pid $(cat "$log.pid") log $log (waits for the sweep lock if held)" diff --git a/tools/attack/adv-cache-3/src/main.rs b/tools/attack/adv-cache-3/src/main.rs index 38a94248e..c376ae75b 100644 --- a/tools/attack/adv-cache-3/src/main.rs +++ b/tools/attack/adv-cache-3/src/main.rs @@ -281,10 +281,6 @@ fn key_of_day(d: u64) -> [u32; 8] { seed_words_from_bytes(&day_bytes(d)) } -fn real_blk(dr: u32, plant: Plant) -> Blk { - Blk::new(32, dr, plant != Plant::NoFeedforward) -} - /// The real block at w = 32, 6 double rounds, with the feed-forward, must equal the library's `chacha_block`; /// `core_inv` must invert `core` at every width used. fn self_test() { @@ -581,7 +577,10 @@ fn cmd_skip(a: &Args) { let key = key_of_day(day); let nb = blk.bits(); log!("skip: day {day} w {w} double rounds {dr} lines {n} segments {segs} plant {} threads {threads}; rotations {:?}", plant.name(), blk.rot); - let seg_of = |p: usize| -> u32 { if segs >= CACHE_SEGMENTS { p as u32 } else { ((p as u64 * 0x9E37) & 0xffff) as u32 } & blk.mask }; + let seg_of = |p: usize| -> u32 { + let s = if segs >= CACHE_SEGMENTS { p as u32 } else { ((p as u64 * 0x9E37) & 0xffff) as u32 }; + s & blk.mask + }; let t0 = Instant::now(); // (1) templates: match counts per template per j, and the chance expectation @@ -661,11 +660,8 @@ fn cmd_skip(a: &Args) { // (2) the GF(2) rank test on (x_j, line_j) and (line_{j-1}, line_j) let samples = 4096usize; let js_test: Vec = [1usize, 2, n / 2, n - 1].into_iter().filter(|&j| j >= 1 && j < n).collect(); - let mut sample_lines: Vec<(Vec<[u32; 16]>, Vec<[u32; 16]>)> = Vec::new(); - for p in 0..(samples / n.max(1) + 1).max(samples / 63 + 1) { - let s = seg_of(p); - sample_lines.push(chain(&blk, &key, s, n, plant)); - } + // one chain per sample segment, so the per-j test has `samples` rows at every j (the first run had 66) + let sample_lines: Vec<(Vec<[u32; 16]>, Vec<[u32; 16]>)> = par(threads, samples, |_, lo, hi| (lo..hi).map(|p| chain(&blk, &key, seg_of(p), n, plant)).collect::>()).into_iter().flatten().collect(); for &j in &js_test { let mut g_x = Gf2::new(); let mut g_p = Gf2::new();