Prover floor packaging: igneum-ota-sign sign-server (a manifest that does not name the server is never signed), push-server.sh uses it; the PC 2 verification playbook (the app's own install and restore scripts, one shard on the 12 GB profile through the installed server)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
6a940a397b
commit
a01ada8e23
3 changed files with 103 additions and 2 deletions
|
|
@ -162,6 +162,18 @@ fn main() {
|
|||
Err(e) => die(&e),
|
||||
}
|
||||
}
|
||||
// the project's GPU prover server manifest (src/proverserver.rs): parsed first, so a manifest that does not
|
||||
// name the server, its sha256 and its size is never signed
|
||||
Some("sign-server") if args.len() == 3 => {
|
||||
let seed = manifest::hex_decode(&read_key_arg(&args[1])).unwrap_or_else(|| die("private key is not hex"));
|
||||
let seed: [u8; 32] = seed.try_into().unwrap_or_else(|_| die("private key is not 32 bytes"));
|
||||
let sk = SigningKey::from_bytes(&seed);
|
||||
let bytes = std::fs::read(&args[2]).unwrap_or_else(|e| die(&format!("{}: {e}", args[2])));
|
||||
let text = std::str::from_utf8(&bytes).unwrap_or_else(|_| die("prover-server manifest is not UTF-8"));
|
||||
let m = proverserver::parse(text).unwrap_or_else(|e| die(&format!("refusing to sign: {e}")));
|
||||
eprintln!("signing the prover server manifest: sp1-gpu-server {} bytes, sha256 {}, SP1 {} ({}), targets {}, built {} by {}", m.bytes, m.sha256, m.sp1_version, &m.sp1_commit[..12.min(m.sp1_commit.len())], m.cuda_archs, m.built_at, m.built_by);
|
||||
println!("{}", manifest::hex_encode(&sk.sign(&bytes).to_bytes()));
|
||||
}
|
||||
Some("sign-inputs") if args.len() == 3 => {
|
||||
let seed = manifest::hex_decode(&read_key_arg(&args[1])).unwrap_or_else(|| die("private key is not hex"));
|
||||
let seed: [u8; 32] = seed.try_into().unwrap_or_else(|_| die("private key is not 32 bytes"));
|
||||
|
|
@ -209,7 +221,7 @@ fn main() {
|
|||
println!("ok: inputs built {} from node commit {} ({}); checked: {}", m.built_at, m.node_source_commit, m.node_source_branch, checked.join(", "));
|
||||
}
|
||||
_ => {
|
||||
eprintln!("usage: igneum-ota-sign keygen <priv> <pub> | sign <priv> <manifest.json> | verify <pub> <manifest.json> <sig> | embedded | fingerprint <pub> | sha256 <file> | sign-jobs <priv> <jobs.json> | verify-jobs <pub> <jobs.json> <sig> | envelope-jobs <pub> <jobs.json> <sig> | verify-signed-jobs <pub> <jobs.signed.json> | sign-inputs <priv> <payload-inputs.json> | verify-inputs <pub|embedded> <payload-inputs.json> <sig> [--zip z] [--dir d] [--node-commit c] | verify-server <pub|embedded> <prover-server.json> <sig> [--binary f]");
|
||||
eprintln!("usage: igneum-ota-sign keygen <priv> <pub> | sign <priv> <manifest.json> | verify <pub> <manifest.json> <sig> | embedded | fingerprint <pub> | sha256 <file> | sign-jobs <priv> <jobs.json> | verify-jobs <pub> <jobs.json> <sig> | envelope-jobs <pub> <jobs.json> <sig> | verify-signed-jobs <pub> <jobs.signed.json> | sign-inputs <priv> <payload-inputs.json> | verify-inputs <pub|embedded> <payload-inputs.json> <sig> [--zip z] [--dir d] [--node-commit c] | sign-server <priv> <prover-server.json> | verify-server <pub|embedded> <prover-server.json> <sig> [--binary f]");
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ m = {"format": "igneum-prover-server/1", "sp1_version": b["sp1_version"], "sp1_c
|
|||
"files": {"sp1-gpu-server": {"sha256": sys.argv[4], "bytes": int(sys.argv[5])}}}
|
||||
open(sys.argv[2], "w").write(json.dumps(m, sort_keys=True, separators=(",", ":")) + "\n")
|
||||
PY
|
||||
"$SIGNER" sign "$KEY" "$DEST/prover-server.json" > "$DEST/prover-server.json.sig"
|
||||
"$SIGNER" sign-server "$KEY" "$DEST/prover-server.json" > "$DEST/prover-server.json.sig"
|
||||
cp "$BIN" "$DEST/sp1-gpu-server"
|
||||
"$SIGNER" verify-server "$PUB" "$DEST/prover-server.json" "$DEST/prover-server.json.sig" --binary "$DEST/sp1-gpu-server"
|
||||
"$SIGNER" verify-server embedded "$DEST/prover-server.json" "$DEST/prover-server.json.sig" >/dev/null || { echo "the embedded key does not verify this signature: the OTA key on this Mac is not the one the apps carry" >&2; exit 1; }
|
||||
|
|
|
|||
89
tools/prover-floor/pc2-packaged-verify.ps1
Normal file
89
tools/prover-floor/pc2-packaged-verify.ps1
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Prover floor, the packaging row's verification run (6 October 2026): the runtime half of the shipped-server path on
|
||||
# PC 2, with what exists there today (the v4 server /opt/igneum-floor/bin/sp1-gpu-server 68b2f512 built on 6 October,
|
||||
# the pv1 host). The signed manifest (prover-server.json + .sig, signed on the Mac with the OTA key) comes from the
|
||||
# fetch job floor-pc2-manifest (jobs\floor-pc2-manifest\). Steps, each a RESULT line: (1) the manifest's sha256 and size
|
||||
# against the binary (the app's check_binary, done here in bash); (2) the app's install script, byte for byte
|
||||
# (app/igneum-app/src/proverserver.rs install_script), run as the app's WSL user: the server lands at
|
||||
# ~/.sp1/bin/sp1-gpu-server where the SDK looks; (3) one v1 shard through the pv1 host with the 12 GB profile
|
||||
# (SP1_GPU_ELEMENT_THRESHOLD=67108864) and the DEFAULT HOME, so the SDK spawns the installed server (the peak and the
|
||||
# time, every proof verified); (4) the app's restore script, so the live prover is as it was (the stock server comes
|
||||
# back by the SDK's download at its next shard). The app's prover is switched off for the run and on at the end; the
|
||||
# miners are stopped by the job (--stop-miners); the live /opt/igneum host is never touched.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$urlFile = if ($env:IGNEUM_APP_DIR) { Join-Path $env:IGNEUM_APP_DIR 'app.url' } else { Join-Path $env:LOCALAPPDATA 'igneum\app\app.url' }
|
||||
if (-not (Test-Path $urlFile)) { $urlFile = Join-Path $env:LOCALAPPDATA 'igneum\app\app.url' }
|
||||
$base = (Get-Content $urlFile -Raw).Trim().TrimEnd('/')
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Prove($on) { try { (Invoke-RestMethod -Method Post -Uri "$base/api/prove" -ContentType 'application/json' -Body (@{on=$on} | ConvertTo-Json -Compress) -TimeoutSec 10) | ConvertTo-Json -Compress } catch { "error: $_" } }
|
||||
"RESULT start $(Stamp) prover off: $(Prove $false)"
|
||||
Start-Sleep -Seconds 30
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs 'floor-pc2-manifest'
|
||||
$manifest = Join-Path $kit 'prover-server.json'
|
||||
if (-not (Test-Path $manifest)) { "RESULT verify_failed no manifest at $manifest (publish the fetch job floor-pc2-manifest first)"; "RESULT end $(Stamp) prover on: $(Prove $true)"; exit 2 }
|
||||
function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } }
|
||||
$manW = WslPath $manifest
|
||||
$job = $env:IGNEUM_JOB_DIR; New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
$jobW = WslPath $job
|
||||
# the WSL user the app runs the host as: the first non-root user of the distribution (the app's prover runs unelevated)
|
||||
$bash = @'
|
||||
set -uo pipefail
|
||||
stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
||||
MAN='MAN_PLACEHOLDER'; JOB='JOB_PLACEHOLDER'
|
||||
BIN=/opt/igneum-floor/bin/sp1-gpu-server; H=/opt/igneum-pv1/igneum-prove-host; FX=/root/igneum-prove-pv1/proving/fixtures/fees-v1-shards2.json
|
||||
[ -x "$BIN" ] && [ -x "$H" ] && [ -f "$FX" ] || { echo "RESULT verify_failed missing: bin=$([ -x $BIN ] && echo ok || echo no) host=$([ -x $H ] && echo ok || echo no) fixture=$([ -f $FX ] && echo ok || echo no)"; exit 2; }
|
||||
# (1) the manifest against the binary, as the app's check_binary does (size first, then the sha256)
|
||||
WANT=$(python3 -c 'import json,sys; f=json.load(open(sys.argv[1]))["files"]["sp1-gpu-server"]; print(f["sha256"], f["bytes"])' "$MAN")
|
||||
WANT_SHA=${WANT% *}; WANT_BYTES=${WANT#* }
|
||||
GOT_SHA=$(sha256sum "$BIN" | cut -c1-64); GOT_BYTES=$(stat -c %s "$BIN")
|
||||
if [ "$GOT_SHA" = "$WANT_SHA" ] && [ "$GOT_BYTES" = "$WANT_BYTES" ]; then echo "RESULT check_binary ok sha256=$GOT_SHA bytes=$GOT_BYTES"; else echo "RESULT check_binary FAILED got $GOT_SHA/$GOT_BYTES want $WANT_SHA/$WANT_BYTES"; exit 2; fi
|
||||
# a tampered copy is refused by the same check (the known-failed case of the gate)
|
||||
cp "$BIN" "$JOB/tampered"; printf 'x' >> "$JOB/tampered"
|
||||
T_SHA=$(sha256sum "$JOB/tampered" | cut -c1-64); T_BYTES=$(stat -c %s "$JOB/tampered")
|
||||
if [ "$T_SHA" = "$WANT_SHA" ] || [ "$T_BYTES" = "$WANT_BYTES" ]; then echo "RESULT tampered_check FAILED: the tampered copy passed"; exit 2; else echo "RESULT tampered_check ok: refused ($T_BYTES bytes is not the manifest's $WANT_BYTES)"; fi
|
||||
rm -f "$JOB/tampered"
|
||||
# (2) the app's install script, as src/proverserver.rs writes it (sha compared first, the server stopped, the copy into ~/.sp1/bin)
|
||||
pkill -f sp1-gpu-server 2>/dev/null; rm -f /tmp/sp1-cuda-*.sock
|
||||
BEFORE=$(sha256sum "$HOME/.sp1/bin/sp1-gpu-server" 2>/dev/null | cut -c1-64 || true); echo "RESULT before user=$(id -un) home=$HOME installed_sha=${BEFORE:-none}"
|
||||
cat > "$JOB/install.sh" <<EOS
|
||||
set -u
|
||||
D="\$HOME/.sp1/bin"; T="\$D/sp1-gpu-server"
|
||||
mkdir -p "\$D"
|
||||
have="\$(sha256sum "\$T" 2>/dev/null | cut -c1-64)"
|
||||
if [ "\$have" = $WANT_SHA ]; then echo "RESULT server $WANT_SHA kept"; exit 0; fi
|
||||
pkill -f sp1-gpu-server 2>/dev/null; rm -f /tmp/sp1-cuda-*.sock
|
||||
cp '$BIN' "\$T.new" && chmod +x "\$T.new" && mv -f "\$T.new" "\$T" || { echo "RESULT server $WANT_SHA install FAILED"; exit 1; }
|
||||
got="\$(sha256sum "\$T" | cut -c1-64)"
|
||||
if [ "\$got" = $WANT_SHA ]; then echo "RESULT server $WANT_SHA installed"; else echo "RESULT server \$got MISMATCH after the copy"; exit 1; fi
|
||||
EOS
|
||||
bash "$JOB/install.sh"; rc=$?; echo "RESULT install_exit $rc"
|
||||
bash "$JOB/install.sh" | sed 's/^RESULT server/RESULT server_again/' # the second run keeps it (the sha matches)
|
||||
echo "RESULT installed_version $($HOME/.sp1/bin/sp1-gpu-server --version 2>/dev/null)"
|
||||
# (3) one v1 shard, the 12 GB profile, the DEFAULT HOME: the SDK spawns ~/.sp1/bin/sp1-gpu-server, which is now the shipped one
|
||||
nvidia-smi --query-gpu=timestamp,memory.used --format=csv,noheader,nounits -l 1 > "$JOB/smi.csv" 2>/dev/null & SMI=$!
|
||||
t0=$(date +%s)
|
||||
SP1_PROVER=cuda RUST_LOG=off SP1_GPU_FLOOR_LOG=1 SP1_GPU_ELEMENT_THRESHOLD=67108864 $H "$FX" --mode compressed --shard 0 --out "$JOB/res.json" > "$JOB/prove.log" 2>&1; prc=$?
|
||||
wall=$(( $(date +%s) - t0 )); pkill -f sp1-gpu-server 2>/dev/null; kill $SMI 2>/dev/null; sleep 1; rm -f /tmp/sp1-cuda-*.sock
|
||||
peak=$(awk -F', *' '{ if ($2+0 > m) m=$2+0 } END { print m+0 }' "$JOB/smi.csv")
|
||||
line=$(grep -E "^RESULT compressed shard" "$JOB/prove.log" | tail -1 | sed -E 's/.*prove ([0-9.]+) s, proof ([0-9]+) bytes, verify ([0-9.]+) s, ([A-Z ]+);.*/prove_s=\1 bytes=\2 verify_s=\3 \4/')
|
||||
opts=$(grep -E "^FLOOR opts" "$JOB/prove.log" | head -1 | cut -c1-160)
|
||||
echo "RESULT prove profile=12gb threshold=67108864 peak_mib=$peak wall_s=$wall ${line:-no_result} exit=$prc server_kind=patched server_sha256=$WANT_SHA"
|
||||
echo "RESULT prove_opts ${opts:-none}"
|
||||
grep -iE "error|panick|Could not" "$JOB/prove.log" | grep -v "^FLOOR" | head -2 | sed 's/^/RESULT prove_err /'
|
||||
# (4) the app's restore script: the stock server comes back by the SDK's own download at the live prover's next shard
|
||||
cat > "$JOB/restore.sh" <<'EOS'
|
||||
set -u
|
||||
T="$HOME/.sp1/bin/sp1-gpu-server"
|
||||
pkill -f sp1-gpu-server 2>/dev/null; rm -f /tmp/sp1-cuda-*.sock
|
||||
rm -f "$T"
|
||||
echo "RESULT server stock restored"
|
||||
EOS
|
||||
bash "$JOB/restore.sh"
|
||||
echo "RESULT after installed_sha=$(sha256sum "$HOME/.sp1/bin/sp1-gpu-server" 2>/dev/null | cut -c1-64 || echo none) (none = the SDK downloads the stock 6.8.1 at the next proof)"
|
||||
echo "RESULT verify_end $(stamp)"
|
||||
'@
|
||||
$bash = $bash.Replace('MAN_PLACEHOLDER', $manW).Replace('JOB_PLACEHOLDER', $jobW)
|
||||
$bashFile = Join-Path $job 'verify.sh'
|
||||
[IO.File]::WriteAllText($bashFile, ($bash -replace "`r`n", "`n"), (New-Object System.Text.UTF8Encoding $false))
|
||||
& wsl.exe -d Ubuntu-24.04 -u root -- bash (WslPath $bashFile) 2>&1 | ForEach-Object { ($_ -replace "`0", '') }
|
||||
"RESULT end $(Stamp) prover on: $(Prove $true)"
|
||||
Loading…
Reference in a new issue