adv-cache-3: rank sampling fix, run-box.sh under the per-box sweep lock

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 19:16:09 +00:00
parent 42827d16eb
commit 667296f14f
3 changed files with 37 additions and 10 deletions

14
tools/attack/adv-cache-3/Cargo.lock generated Normal file
View file

@ -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"

View file

@ -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 <tag> <args...>
# 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/<tag>.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 <pid>).
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)"

View file

@ -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<usize> = [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::<Vec<_>>()).into_iter().flatten().collect();
for &j in &js_test {
let mut g_x = Gf2::new();
let mut g_p = Gf2::new();