igneum/tools/bench-5060ti/pc2-5060ti-bench.ps1
igneum-labs 7eed16a29a Pre-public scrub, the text pass (7 October 2026, 19:5x UK): no founder name, personal login, earlier business or personal address in any tracked text file, and a gate check that keeps it so
The sweep (main's item 1): 199 tracked text files, 783 lines. The founder's full name, first name and possessive become "the founder" (sentence starts capitalised); the lowercase operating-system user name in WSL paths and commands becomes <user>; the second owner login becomes "the second owner login"; the three earlier businesses and the two other brands become "the other business", "the earlier entity", "the earlier business" and "another brand"; the Chrome profile rule names the igneum.network profile, not the profile's label. The standing commit login igneum-labs is not a founder term here: the fresh-repository step renames it in the history (docs/plans/history-rewrite.md, tools/repo/fresh-repo.sh).

The patterns never appear in plain text in the tree (a plaintext list would be the hit): tools/ci/founder-strings.b64 (perl regex, tab, a sample per row) is read by tools/ci/founder-strings-check.sh (every tracked text file, perl, known-failed first: the self-test plants each row's sample in a fixture and the hit must name the file), by tools/community/discord-hooks.mjs (the guard's founder and business rows; the test takes its fixtures from the samples) and by tools/repo/fresh-repo.sh (the business names of the rewrite rules). site/forbidden-strings.txt carries the same patterns as b64: lines, decoded case-insensitive by site/scrub.mjs and tools/ci/launch-gates-check.mjs (whose fixture now plants an encoded made-up name). The check runs in the gate's tree checks on every merge.

Not in this commit, by main's word: the 105 commit messages and 40 personal-identity commits that need the history rewrite (listed, not run), and the secrets found by gitleaks over the history (reported with owners).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 18:39:50 +00:00

289 lines
29 KiB
PowerShell

# Igneum run job: the ASUS Dual GeForce RTX 5060 Ti in a Razer Core X V2 (Thunderbolt) on PC 2 (machine 1ccfe586),
# 7 October 2026 (branch bench-5060ti). Published as a signed `run` job with the RUNNER's --cards-off <the 5060 Ti's key>
# (NOT --stop-miners): the runner switches the 5060 Ti off through the app's own card path before this script and puts
# it back on ANY exit; the RTX 5090 keeps mining. This script never switches a card, never stops, holds or ends the
# installed app (the playbook-quit rule, tools/ci/playbook-quit-check.sh); it reads api/state once, read only.
# POWER RULE (the founder, 7 October 2026: PC 2 lost power twice today): nothing here raises any card's power limit. There is
# no nvidia-smi -pl, -lgc or -lmc anywhere in this file; the limits are READ (power.limit, power.default_limit) and the
# row says whether they were equal. The efficient point is the app's own Ember Tune's to find (its ladder only steps
# DOWN from the stock limit); this job sets nothing and reads the app's TUNE lines for the card, if any.
# After the fetch job that places the two packs (tools/bench-5060ti/make-kit.sh: v4-devnet-epoch0 at sub-version 1,
# the stream 0.3.20 ships, fingerprint 867dbc45cfb36b4d; mx8-devnet-epoch0 the class v3 control, 90f794dd556f7a3b).
# Steps: detect (nvidia-smi: name, VRAM, driver, the PCIe link gen and width = the Thunderbolt link; the Windows
# adapter rows; the app's own card row, read only), the card quiet (no compute process on its UUID), G1 (self-test and
# the 2^24 fingerprint on both packs, bit-exact against the Mac), the 10-minute window at the stock limit (the worker's
# --bench with enough dispatches for 600 s, nvidia-smi sampled every 2 s on the card alone: MH/s and mean watts at
# utilisation 90 percent and over), the app's tune lines (read only), then, on a 16 GB card, the prove-beside-miner
# read: the app's own WSL2 prover host aimed at the 5060 Ti (IGNEUM_CUDA_DEVICE, the prover-floor host's selector)
# on one live shard while a bench loop hashes on the card, VRAM peak from the sampler; when the shipped host carries
# no selector the read is BLOCKED and the line says so (aiming the stock host would prove on the 5090 instead).
# Lines start with RESULT; a SUMMARY {json} line ends the job. Read back with `node tools/jobs.mjs <job id>`.
$ErrorActionPreference = 'Continue'
$jobName = 'pc2-5060ti-bench'
$kitId = '__KIT_ID__' # the publisher substitutes the fetch job id that placed the packs
$started = Get-Date
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
function Summary([string] $status, [hashtable] $extra) {
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
}
$expected = @{ 'v4-devnet-epoch0' = '867dbc45cfb36b4d'; 'mx8-devnet-epoch0' = '90f794dd556f7a3b' } # sub-version 1: Metal = Apple OpenCL = the RTX 5090 (job run-ca3-v4-amend-g1-pc2-20261007)
$networkMhs = [double] '__NETWORK_MHS__' # the publisher substitutes live_state.hashes_per_second_estimate / 1e6; 0 = unknown, the line says so
if ($networkMhs -isnot [double] -or [double]::IsNaN($networkMhs)) { $networkMhs = 0 }
$subsidyIgn = 31.69 # IGN a block, spec 02 section 2.5 (1 block a second)
$ref5090Mhs = 122.0; $ref5090W = 308.0 # the 5090's row on PC 2 (main's figures for the per-tier sentence)
$windowS = 600
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION network_mhs=$networkMhs"
$jobs = Split-Path $env:IGNEUM_JOB_DIR
$kit = Join-Path $jobs $kitId
if (-not (Test-Path (Join-Path $kit 'packs\v4-devnet-epoch0\program.h'))) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; an app update wipes the jobs folder: republish it under a new id after one)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-5060ti' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
if (-not $inst) { "RESULT error no installed igneum-worker-cuda.exe"; Summary 'failed' @{ error = 'no worker' }; exit 2 }
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
"RESULT app install=$inst app_dir=$appDir worker=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())"
foreach ($f in (Get-ChildItem (Join-Path $kit 'packs') -Recurse -File | Sort-Object FullName)) { "RESULT kitfile $($f.FullName.Substring($kit.Length + 1).Replace('\', '/')) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower()) bytes $($f.Length)" }
# ---- detect: nvidia-smi's rows (the Thunderbolt link is the PCIe gen and width the card reports), Windows' adapter rows, the app's row ----
function Smi([string[]] $argv) { try { @(& nvidia-smi @argv 2>&1 | ForEach-Object { "$_" }) } catch { @("nvidia-smi failed: $($_.Exception.Message)") } }
$gpuFields = 'index,uuid,name,driver_version,memory.total,memory.used,pcie.link.gen.current,pcie.link.gen.max,pcie.link.width.current,pcie.link.width.max,power.draw,power.limit,power.default_limit,power.min_limit,power.max_limit,clocks.max.sm,clocks.max.mem,temperature.gpu'
# the query is ONE string built first: inside @( ... ) the comma binds before +, so '--query-gpu=' + $f, '--format=...'
# became a single argument carrying the format words (run a, 14:43:29Z: "temperature.gpu --format=csv is not a valid field")
$gpuQuery = "--query-gpu=$gpuFields"
$rows = @(Smi @($gpuQuery, '--format=csv,noheader,nounits'))
$rows | ForEach-Object { "RESULT gpu $_" }
$card = $null
foreach ($r in $rows) {
$c = @($r -split ',\s*')
if ($c.Count -ge 18 -and $c[2] -match '5060 Ti') { $card = [pscustomobject]@{ index = [int]$c[0]; uuid = $c[1]; name = $c[2]; driver = $c[3]; mem_total = [int]$c[4]; gen = $c[6]; gen_max = $c[7]; width = $c[8]; width_max = $c[9]; plimit = [double]$c[11]; pdefault = [double]$c[12]; pmin = $c[13]; pmax = $c[14]; clk_sm_max = $c[15]; clk_mem_max = $c[16] }; break }
}
try {
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) { "RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)" }
} catch { "RESULT adapter error=$($_.Exception.Message)" }
if ($null -eq $card) { "RESULT error no RTX 5060 Ti in nvidia-smi's list (the rows above are what the driver sees; is the enclosure's Thunderbolt link up and the card seated?)"; Summary 'failed' @{ error = 'no 5060 Ti in nvidia-smi' }; exit 2 }
$tier = if ($card.mem_total -ge 15000) { 16 } elseif ($card.mem_total -ge 7000) { 8 } else { [math]::Round($card.mem_total / 1024) }
"RESULT DETECT name=`"$($card.name)`" vendor=nvidia vram_mib=$($card.mem_total) tier_gb=$tier driver=$($card.driver) pcie_link=gen$($card.gen)x$($card.width) pcie_max=gen$($card.gen_max)x$($card.width_max) power_limit_w=$($card.plimit) power_default_w=$($card.pdefault) power_range_w=$($card.pmin)-$($card.pmax) limit_is_stock=$(if ($card.plimit -eq $card.pdefault) { 'yes' } else { 'NO' }) smi_index=$($card.index) uuid=$($card.uuid)"
# READ ONLY: the app's view of the card (its key is what the publisher's --cards-off named; the runner, not this script, switched it)
$cardKey = ''; $appDevice = ''
try {
$urlFile = Join-Path $appDir 'app.url'
if (Test-Path $urlFile) {
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
foreach ($c in @($all | Where-Object { $_.name -match '5060' })) { $cardKey = $c.key; $appDevice = "$($c.device)"; "RESULT app_card key=$($c.key) vendor=$($c.vendor) kind=$($c.kind) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) device=$($c.device) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) reason=`"$($c.reason)`" message=`"$($c.message)`"" }
if (-not $cardKey) { "RESULT app_card none: the app's state lists no card named 5060 (cards: $(@($all | ForEach-Object { $_.name }) -join ' | '))" }
} else { "RESULT app_card no app.url under $appDir" }
} catch { "RESULT app_card error=$($_.Exception.Message)" }
# the card quiet? read only, by the driver's compute list on the card's UUID: the runner's --cards-off took, or not
function CardApps { @(Smi @('--query-compute-apps=pid,process_name,used_memory,gpu_uuid', '--format=csv,noheader,nounits') | Where-Object { $_ -match [regex]::Escape($card.uuid) }) }
$cardAlone = $false; $t = 0
while ($t -lt 150) { if ((@(CardApps)).Count -eq 0) { $cardAlone = $true; break }; Start-Sleep -Seconds 5; $t += 5 }
if ($cardAlone) { "RESULT card $(Stamp) quiet after $t s: no compute process on $($card.uuid) (the card to itself)" } else { "RESULT card $(Stamp) UNCONFIRMED after $t s: a compute process still runs on the card ($((CardApps) -join '; ')); was the job published with --cards-off $cardKey ? the rows below are LOADED-card figures" }
# The worker sees ONLY the 5060 Ti (CUDA_VISIBLE_DEVICES on its UUID) and takes --device 0; every RESULT line names
# the device it ran on, and a line naming another card voids the row. The 5090's process is never touched.
$env:CUDA_VISIBLE_DEVICES = $card.uuid
# Runs the worker as a child under a wall-clock cap; a child past its cap is ended by ITS OWN pid (taskkill /PID, never
# a name). Output comes back through a file; a trailing "timeout <cap>s" line says when the cap fired.
function Capped([string] $tag, [string[]] $argv, [int] $capS) {
$out = Join-Path $job ("$tag.out"); $errf = Join-Path $job "$tag.err"
Remove-Item -LiteralPath $out, $errf -Force -ErrorAction SilentlyContinue
$p = Start-Process -FilePath $exe -ArgumentList $argv -NoNewWindow -PassThru -RedirectStandardOutput $out -RedirectStandardError $errf
$done = $p.WaitForExit($capS * 1000)
if (-not $done) { try { & taskkill /T /F /PID $p.Id 2>&1 | Out-Null } catch { }; Start-Sleep -Seconds 2 }
$lines = @(); if (Test-Path -LiteralPath $out) { $lines = @(Get-Content -LiteralPath $out -ErrorAction SilentlyContinue) }
if (Test-Path -LiteralPath $errf) { $lines += @(Get-Content -LiteralPath $errf -ErrorAction SilentlyContinue) }
if (-not $done) { $lines += "timeout ${capS}s: the worker was ended by its pid $($p.Id)" }
if ($done) { try { $p.Refresh() } catch { } }
$script:lastExit = if ($done) { $p.ExitCode } else { 124 }
return $lines
}
$script:lastExit = 0
# the nvidia-smi sampler on the card's index, 2 s, to a file; ended by its pid
function SamplerStart([string] $tag) {
$f = Join-Path $job "$tag.csv"; Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue
$p = Start-Process -FilePath 'nvidia-smi' -ArgumentList @('-i', "$($card.index)", '--query-gpu=timestamp,power.draw,power.limit,clocks.sm,clocks.mem,temperature.gpu,utilization.gpu,memory.used', '--format=csv,noheader,nounits', '-lms', '2000') -NoNewWindow -PassThru -RedirectStandardOutput $f -RedirectStandardError (Join-Path $job "$tag.smi.err")
return [pscustomobject]@{ pid = $p.Id; file = $f }
}
function SamplerStop($s) { try { Stop-Process -Id $s.pid -Force -ErrorAction SilentlyContinue } catch { }; Start-Sleep -Seconds 1 }
# the window's numbers from a sampler file: samples at utilisation 90 and over are the loaded ones
function SamplerRead($s, [string] $tag) {
$all = @(); if (Test-Path -LiteralPath $s.file) { $all = @(Get-Content -LiteralPath $s.file -ErrorAction SilentlyContinue | Where-Object { $_ -match ',' }) }
$pw = @(); $pl = @(); $sm = @(); $mem = @(); $tmp = @(); $used = @(); $n = 0
foreach ($l in $all) { $c = @($l -split ',\s*'); if ($c.Count -lt 8) { continue }; $n++; $u = [double]$c[6]; $used += [double]$c[7]; if ($u -ge 90) { $pw += [double]$c[1]; $pl += [double]$c[2]; $sm += [double]$c[3]; $mem += [double]$c[4]; $tmp += [double]$c[5] } }
$o = [ordered]@{ samples = $n; loaded = $pw.Count; w_mean = 0; w_p50 = 0; w_max = 0; limit_max = 0; sm_mhz = 0; mem_mhz = 0; temp_max = 0; mem_used_peak_mib = 0 }
if ($pw.Count -gt 0) { $sorted = $pw | Sort-Object; $o.w_mean = [math]::Round(($pw | Measure-Object -Average).Average, 1); $o.w_p50 = [math]::Round($sorted[[int][math]::Floor($sorted.Count / 2)], 1); $o.w_max = [math]::Round(($pw | Measure-Object -Maximum).Maximum, 1); $o.limit_max = ($pl | Measure-Object -Maximum).Maximum; $o.sm_mhz = [math]::Round(($sm | Measure-Object -Average).Average); $o.mem_mhz = [math]::Round(($mem | Measure-Object -Average).Average); $o.temp_max = ($tmp | Measure-Object -Maximum).Maximum }
if ($used.Count -gt 0) { $o.mem_used_peak_mib = ($used | Measure-Object -Maximum).Maximum }
# the line goes to the host, not the pipeline: a bare string here was swallowed into the caller's variable (run b, 14:55Z: the ROW carried the watts, the sampler line never reached the report)
[Console]::Out.WriteLine("RESULT sampler $tag samples=$($o.samples) loaded=$($o.loaded) watts_mean=$($o.w_mean) watts_p50=$($o.w_p50) watts_max=$($o.w_max) limit_seen_w=$($o.limit_max) sm_mhz=$($o.sm_mhz) mem_mhz=$($o.mem_mhz) temp_max=$($o.temp_max) mem_used_peak_mib=$($o.mem_used_peak_mib) file=$($s.file)")
return $o
}
# ---- the benches ----
# A function's uncaptured output is its return value: Bench writes its lines into $script:benchOut; Flush prints them.
$script:benchOut = @()
function Say([string] $l) { $script:benchOut += $l }
function Flush { $script:benchOut | ForEach-Object { $_ }; $script:benchOut = @() }
function Bench([string] $label, [string] $packDir, [int] $batches, [int] $capS) {
if (-not (Test-Path $packDir)) { Say "RESULT bench $label error=pack missing at $packDir"; return $null }
$t0 = (Get-Date).ToUniversalTime()
Say "RESULT bench $label start $(Stamp) pack=$packDir batches=$batches"
$lines = @(Capped "bench-$label" @('--bench', '--pack', $packDir, '--batches', "$batches", '--batch-log2', '24', '--block-warps', '1', '--device', '0') $capS)
$rc = $script:lastExit
$wall = [int]((Get-Date).ToUniversalTime() - $t0).TotalSeconds
$lines | Where-Object { $_ -match '^RESULT|^warm-up|^info|^pack |^nvrtc|^self-test|FAIL|error|MISMATCH|^timeout' } | Select-Object -First 30 | ForEach-Object { Say "RESULT bench $label $_" }
Say "RESULT bench $label end $(Stamp) exit=$rc wall_s=$wall"
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
if ($null -eq $rc -or "$rc" -eq '') { $rc = 0 } # a null exit after WaitForExit on a -PassThru process: the RESULT line is the verdict
if (-not $res -or $rc -ne 0) {
$err = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); if (-not $err) { $err = "exit $rc, no RESULT line" }
Say "RESULT G1 pack=$label error=$($err -replace '\s+', ' ')"
return $null
}
$fp = ''; $mhs = ''; $check = ''; $dev = ''
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
if ($res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
if ($res -match 'check=(\S+)') { $check = $Matches[1] }
if ($res -match 'device=(\S+)') { $dev = $Matches[1] }
if ($dev -notmatch '5060') { Say "RESULT G1 pack=$label error=ran on device `"$dev`", not the 5060 Ti: the row is void"; return $null }
$want = $expected[$label]
$match = if ($want) { if ($fp -eq $want) { 'yes' } else { 'no' } } else { 'n/a' }
Say "RESULT G1 pack=$label fingerprint=$fp match=$match expected=$want check=$check device=$dev batches=$batches wall_s=$wall harness=cuda-installed"
return [pscustomobject]@{ label = $label; mhs = [double]$mhs; fp = $fp; match = $match; check = $check; wall_s = $wall; device = $dev }
}
# G1: five dispatches each, the fingerprints
$v4 = Bench 'v4-devnet-epoch0' (Join-Path $kit 'packs\v4-devnet-epoch0') 5 300; Flush
$v3 = Bench 'mx8-devnet-epoch0' (Join-Path $kit 'packs\mx8-devnet-epoch0') 5 300; Flush
# the 10-minute window at the stock limit: as many 2^24 dispatches as the five-batch rate says fit in 600 s
$stock = $null; $stockSmi = $null
if ($v4 -and $v4.mhs -gt 0) {
$n = [int][math]::Ceiling($windowS * $v4.mhs * 1e6 / 16777216); if ($n -lt 50) { $n = 50 }; if ($n -gt 6000) { $n = 6000 }
$s = SamplerStart 'stock'
Start-Sleep -Seconds 4
$stock = Bench 'v4-stock-window' (Join-Path $kit 'packs\v4-devnet-epoch0') $n ($windowS + 300); Flush
SamplerStop $s
$stockSmi = SamplerRead $s 'stock'
} else { "RESULT window skipped: no class v4 rate from G1" }
# ---- the efficient point: the app's Ember Tune's, read only (this job sets nothing; the ladder only ever steps DOWN) ----
try {
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$tuneLines = @()
if ($newest) { $tuneLines = @(Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match '^\d+(\.\d+)? (TUNE|tune) ' -and $_ -match '5060' } | Select-Object -Last 8) }
if ($tuneLines.Count -gt 0) { $tuneLines | ForEach-Object { "RESULT tune $_" } } else { "RESULT tune none: the app has no TUNE line for the 5060 Ti yet (the card was off for this job; the app's tune runs when the card mines and the Power Helper answers)" }
} catch { "RESULT tune error=$($_.Exception.Message)" }
# ---- prove beside the miner, 16 GB only: the app's own WSL2 host aimed at the 5060 Ti while a bench loop hashes on it ----
$prove = [ordered]@{ ran = $false; verdict = 'not attempted'; peak_mib = 0; base_mib = 0; prove_s = 0; why = '' }
if ($tier -ge 16) {
$hostWin = Join-Path $inst 'wsl2\bin\igneum-prove-host'
if (-not (Test-Path -LiteralPath $hostWin)) { $prove.why = "no WSL2 host at $hostWin"; "RESULT prove blocked why=$($prove.why)" }
else {
function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } }
$hostW = WslPath $hostWin; $jobW = WslPath $job
# the fixture: one live block from PC 2's own node (read only, the exec RPC on loopback), cut with the app's exporter
$evmPort = $null
foreach ($p in 26790, 26800, 26810) { try { $r = Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:$p" -ContentType 'application/json' -Body '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' -TimeoutSec 20; if ($r.result) { $evmPort = $p; $tipHex = $r.result; break } } catch { } }
if (-not $evmPort) { $prove.why = 'no exec RPC on 26790/26800/26810'; "RESULT prove blocked why=$($prove.why)" }
else {
$tip = [Convert]::ToInt64($tipHex, 16); $blk = $tip - 30
$body = (@{jsonrpc='2.0'; id=1; method='igneum_exportSegments'; params=@('0x0', ('0x{0:x}' -f $blk))} | ConvertTo-Json -Compress)
$bodyFile = Join-Path $job 'export-request.json'; $seqFile = Join-Path $job 'seq.json'
[IO.File]::WriteAllText($bodyFile, $body, (New-Object System.Text.UTF8Encoding $false))
& curl.exe -s -S -m 600 -X POST "http://127.0.0.1:$evmPort" -H 'Content-Type: application/json' --data-binary "@$bodyFile" -o $seqFile 2>&1 | ForEach-Object { "RESULT prove curl $_" }
$seqLen = if (Test-Path $seqFile) { (Get-Item $seqFile).Length } else { 0 }
"RESULT prove export port=$evmPort tip=$tip block=$blk bytes=$seqLen"
# the bash side (LF, no BOM, run as the WSL default user = the app's prover user, never as the root user: the
# socket rule of tools/ci/prover-socket-check.sh; the server this run starts is ended by the pid on ITS socket).
# Inventory first; the prove only when the host carries the selector and the card is not WSL device 0 (device 0's
# socket /tmp/sp1-cuda-0.sock is the app's own server on the 5090 and is never touched).
$bashBody = @'
set -uo pipefail
H="$1"; JOB="$2"; UUID="$3"; BLK="$4"
X="$(dirname "$H")/igneum-prove-export"
CUDA_DIR="$(ls -d /usr/local/cuda-12.* 2>/dev/null | sort -V | tail -1 || true)"; [ -n "$CUDA_DIR" ] && export PATH="$CUDA_DIR/bin:$PATH" && export LD_LIBRARY_PATH="$CUDA_DIR/lib64:/usr/lib/wsl/lib:${LD_LIBRARY_PATH:-}"
export PATH="$HOME/.sp1/bin:$PATH"
stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; }
echo "RESULT prove host=$H exists=$([ -x "$H" ] && echo yes || echo no) sha=$(sha256sum "$H" 2>/dev/null | cut -c1-16) export_exists=$([ -x "$X" ] && echo yes || echo no) user=$(id -un) home=$HOME"
SEL=$(grep -c IGNEUM_CUDA_DEVICE "$H" 2>/dev/null || true); SEL=${SEL:-0}
echo "RESULT prove selector IGNEUM_CUDA_DEVICE strings_in_host=$SEL"
for s in "$HOME/.sp1/bin/sp1-gpu-server" /opt/igneum-floor/bin/sp1-gpu-server; do if [ -x "$s" ]; then echo "RESULT prove server $s bytes=$(stat -c %s "$s") sha=$(sha256sum "$s" | cut -c1-16) version=$("$s" --version 2>&1 | head -1)"; else echo "RESULT prove server $s absent"; fi; done
echo "RESULT prove sockets_before $(ls /tmp/sp1-cuda-*.sock 2>/dev/null | tr '\n' ' ')"
nvidia-smi --query-gpu=index,uuid,name,memory.total --format=csv,noheader 2>/dev/null | sed 's/^/RESULT prove wslgpu /'
IDX="$(nvidia-smi --query-gpu=index,uuid --format=csv,noheader 2>/dev/null | awk -F', ' -v u="$UUID" '$2==u {print $1}' | head -1)"
echo "RESULT prove wsl_index_of_5060ti=${IDX:-none}"
if [ "$SEL" = 0 ]; then echo "RESULT prove blocked why=the shipped host has no IGNEUM_CUDA_DEVICE selector: aimed at nothing it proves on CUDA device 0 (the 5090) through the app's own socket; the prover-floor host carries the selector (proof_system.rs) and is the owed cut"; exit 0; fi
if [ -z "$IDX" ] || [ "$IDX" = 0 ]; then echo "RESULT prove blocked why=the 5060 Ti is WSL CUDA device '${IDX:-none}': device 0 is the app's own prover socket and is never touched by a job"; exit 0; fi
SOCK="/tmp/sp1-cuda-$IDX.sock"
rm -f "$SOCK"
python3 -c "import json; d=json.load(open('$JOB/seq.json')); json.dump(d['result'], open('$JOB/export.json','w'))" || { echo "RESULT prove blocked why=the export reply did not unwrap"; exit 0; }
FIX="$JOB/block-$BLK.json"
if ! "$X" "$JOB/export.json" "$BLK" "$FIX" --source "PC 2 live devnet export, the 5060 Ti prove-beside read" 2>&1 | tail -1 | sed 's/^/RESULT prove cut /'; then echo "RESULT prove blocked why=the cut failed"; exit 0; fi
echo "RESULT prove start $(stamp) fixture=$(basename "$FIX") device=$IDX socket=$SOCK"
t0=$(date +%s)
IGNEUM_CUDA_DEVICE="$IDX" SP1_PROVER=cuda RUST_LOG=off timeout 900 "$H" "$FIX" --mode compressed --shard 0 --prover 0xCAfc6e74000000000000000000000000000000c2 --out "$JOB/prove-5060ti.json" > "$JOB/prove-5060ti.log" 2>&1; rc=$?
echo "RESULT prove end $(stamp) rc=$rc wall_s=$(( $(date +%s) - t0 ))"
grep -E '^(RESULT|STAGE|segment proof|FLOOR|thread|Error|error)' "$JOB/prove-5060ti.log" | head -20 | sed 's/^/RESULT prove host-out /'
# the server this run started answers on ITS socket: ended by that pid, never by a name; the socket unlinked
PID="$(fuser "$SOCK" 2>/dev/null | tr -d ' ' || true)"
if [ -n "$PID" ]; then kill "$PID" 2>/dev/null; sleep 2; kill -9 "$PID" 2>/dev/null; echo "RESULT prove server_ended pid=$PID"; fi
rm -f "$SOCK"
echo "RESULT prove sockets_after $(ls /tmp/sp1-cuda-*.sock 2>/dev/null | tr '\n' ' ')"
'@
$bashFile = Join-Path $job 'prove-5060ti.sh'
[IO.File]::WriteAllText($bashFile, ("#!/bin/bash`n" + ($bashBody -replace "`r`n", "`n").TrimEnd() + "`n"), (New-Object System.Text.UTF8Encoding $false))
$bashW = WslPath $bashFile
# the miner-shaped load on the 5060 Ti for the read: a bench loop of about six minutes, by pid, file output
$loopN = if ($v4 -and $v4.mhs -gt 0) { [int][math]::Ceiling(360 * $v4.mhs * 1e6 / 16777216) } else { 300 }
$loopOut = Join-Path $job 'beside-loop.out'
$loop = Start-Process -FilePath $exe -ArgumentList @('--bench', '--pack', (Join-Path $kit 'packs\v4-devnet-epoch0'), '--batches', "$loopN", '--batch-log2', '24', '--block-warps', '1', '--device', '0') -NoNewWindow -PassThru -RedirectStandardOutput $loopOut -RedirectStandardError (Join-Path $job 'beside-loop.err')
$s2 = SamplerStart 'beside'
Start-Sleep -Seconds 45 # the loop's build, self-test and warm-up: the base reading is the miner-shaped resident set
$baseRow = @(Get-Content -LiteralPath $s2.file -ErrorAction SilentlyContinue | Where-Object { $_ -match ',' } | Select-Object -Last 1)
if ($baseRow.Count -gt 0) { $c = @($baseRow[0] -split ',\s*'); if ($c.Count -ge 8) { $prove.base_mib = [double]$c[7] } }
"RESULT prove base_mib=$($prove.base_mib) (the bench loop resident on the card before the host starts)"
$wslOut = @(& wsl.exe -d Ubuntu-24.04 -- bash $bashW $hostW $jobW $card.uuid "$blk" 2>&1 | ForEach-Object { ("$_" -replace "`0", '') })
$wslOut | ForEach-Object { $_ }
$prove.ran = ($wslOut | Where-Object { $_ -match '^RESULT prove end ' }).Count -gt 0
$endLine = $wslOut | Where-Object { $_ -match '^RESULT prove end ' } | Select-Object -First 1
if ($endLine -and $endLine -match 'rc=(\d+) wall_s=(\d+)') { $prove.prove_s = [int]$Matches[2]; $prove.verdict = if ($Matches[1] -eq '0') { 'proved' } else { "failed rc=$($Matches[1])" } }
$blocked = $wslOut | Where-Object { $_ -match '^RESULT prove blocked ' } | Select-Object -First 1
if ($blocked) { $prove.verdict = 'blocked'; $prove.why = ($blocked -replace '^RESULT prove blocked why=', '') }
if (-not $loop.HasExited) { $loop.WaitForExit(120000) | Out-Null }
if (-not $loop.HasExited) { try { & taskkill /T /F /PID $loop.Id 2>&1 | Out-Null } catch { } }
SamplerStop $s2
$besideSmi = SamplerRead $s2 'beside'
$prove.peak_mib = $besideSmi.mem_used_peak_mib
$loopRes = @(Get-Content -LiteralPath $loopOut -ErrorAction SilentlyContinue | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1)
$loopMhs = if ($loopRes.Count -gt 0 -and $loopRes[0] -match 'mhs=([\d.]+)') { $Matches[1] } else { 'none' }
"RESULT prove loop_mhs=$loopMhs (the bench loop's mean over its dispatches, the prover beside it when it ran)"
$own = if ($prove.ran -and $prove.verdict -eq 'proved') { [math]::Round(($prove.peak_mib - $prove.base_mib) / 1024, 1) } else { 0 }
"RESULT PROVE tier_gb=$tier verdict=$($prove.verdict) prove_s=$($prove.prove_s) peak_mib=$($prove.peak_mib) base_mib=$($prove.base_mib) own_gb=$own vram_mib=$($card.mem_total) spare_gb=$(if ($prove.peak_mib -gt 0) { [math]::Round(($card.mem_total - $prove.peak_mib) / 1024, 1) } else { 'n/a' }) why=`"$($prove.why)`""
}
}
} else { "RESULT PROVE tier_gb=$tier verdict=not-a-16gb-card: the prove-beside read is for the 16 GB tier; an 8 GB card mines only (the 12 GB floor is 13.9 GB for an empty shard)" }
# ---- the row, the picker entry and the consequences ----
$rate = if ($stock) { $stock.mhs } elseif ($v4) { $v4.mhs } else { 0 }
$watts = if ($stockSmi -and $stockSmi.loaded -gt 0) { $stockSmi.w_mean } else { 0 }
$mhw = if ($watts -gt 0 -and $rate -gt 0) { [math]::Round($rate / $watts, 3) } else { 0 }
$blocksDay = if ($networkMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $networkMhs * 86400, 2) } else { 0 }
$hoursBlock = if ($blocksDay -gt 0) { [math]::Round(24 / $blocksDay, 1) } else { 0 }
$ignDay = [math]::Round($blocksDay * $subsidyIgn, 1)
$shadowCost = if ($v4 -and $v3 -and $v3.mhs -gt 0) { [math]::Round((1 - $v4.mhs / $v3.mhs) * 100, 1) } else { 'owed' }
$of5090 = if ($rate -gt 0) { [math]::Round($rate / $ref5090Mhs * 100, 1) } else { 0 }
$ref5090Mhw = [math]::Round($ref5090Mhs / $ref5090W, 3)
$mhwVs5090 = if ($mhw -gt 0) { [math]::Round($mhw / $ref5090Mhw * 100) } else { 0 }
"RESULT ROW card=`"$($card.name)`" vram_gb=$tier driver=$($card.driver) pcie=gen$($card.gen)x$($card.width) g1_v4_mhs=$(if ($v4) { $v4.mhs } else { 'failed' }) g1_v3_mhs=$(if ($v3) { $v3.mhs } else { 'failed' }) stock_window_mhs=$(if ($stock) { $stock.mhs } else { 'failed' }) stock_window_s=$(if ($stock) { $stock.wall_s } else { 0 }) watts_mean=$watts watts_p50=$(if ($stockSmi) { $stockSmi.w_p50 } else { 0 }) limit_w=$($card.plimit) default_w=$($card.pdefault) mhw=$mhw sm_mhz=$(if ($stockSmi) { $stockSmi.sm_mhz } else { 0 }) mem_mhz=$(if ($stockSmi) { $stockSmi.mem_mhz } else { 0 }) temp_max=$(if ($stockSmi) { $stockSmi.temp_max } else { 0 }) shadow_cost_pct=$shadowCost efficient_point=app-tune-owed network_mhs=$networkMhs blocks_per_day=$blocksDay hours_per_block=$hoursBlock ign_per_day=$ignDay card_alone=$cardAlone"
"RESULT PICKER ['NVIDIA RTX 5060 Ti', $rate] (site/yourcard.js CARDS entry; measured on the card alone, so it may go on the site)"
"RESULT CONSEQUENCES a 5060 Ti owner ($tier GB, Windows, in a Thunderbolt enclosure at gen$($card.gen)x$($card.width)): $rate MH/s at $watts W ($mhw MH/W), $of5090 percent of the 5090's $ref5090Mhs MH/s at $ref5090W W ($ref5090Mhw MH/W: $mhwVs5090 percent of its hash per watt); $blocksDay blocks a day at today's $networkMhs MH/s network (one every $hoursBlock h, about $ignDay IGN a day at $subsidyIgn IGN a block, approximate: the network rate moves); prove-beside verdict $($prove.verdict); the efficient point is the app's tune to find and is owed (nothing set by this job)"
$status = if ($v4 -and $v4.match -eq 'yes' -and $v3 -and $v3.match -eq 'yes' -and $stock) { 'done' } elseif ($v4 -or $stock) { 'partial' } else { 'failed' }
"RESULT end $(Stamp)"
Summary $status @{ name = $card.name; tier_gb = $tier; driver = $card.driver; pcie = "gen$($card.gen)x$($card.width)"; rate_mhs = $rate; watts = $watts; mhw = $mhw; limit_w = $card.plimit; default_w = $card.pdefault; v4_fp = $(if ($v4) { $v4.fp } else { '' }); v4_match = $(if ($v4) { $v4.match } else { 'failed' }); v3_match = $(if ($v3) { $v3.match } else { 'failed' }); prove = $prove.verdict; prove_peak_mib = $prove.peak_mib; blocks_per_day = $blocksDay; card_alone = $cardAlone }
if ($status -eq 'failed') { exit 1 }
exit 0