Commit Graph

398 Commits

Author SHA1 Message Date
2e6fce162e engine-cuda: batches of whole grids, early reject on element 0, nonce direction
All checks were successful
ci / fmt (pull_request) Successful in 19s
bench / build (pull_request) Successful in 1m15s
ci / clippy (pull_request) Successful in 1m45s
ci / doc (pull_request) Successful in 1m58s
bench / measure (pull_request) Successful in 2m51s
ci / test (pull_request) Successful in 6m46s
Three scheduling changes measured against the #23 kernel, three interleaved
rounds each, parity clean on every host:

- Batches are rounded up to whole grids, two per batch by default
  (MINER_CUDA_BATCH_ALIGN, MINER_CUDA_BATCH_WAVES). The CLI's 1M-nonce
  batch left a half-empty tail wave and paid the launch gap every 1M
  hashes. beast +1.6% for one grid, +0.4% more for two; benjy +0.5% for
  the second grid; quadbrat neutral.
- LAIR_EARLY_REJECT: the second permutation stops before its last linear
  layer, computes output 0 alone (the hash's top 64 bits) and rejects on
  it; only the rare survivors pay for the other eleven outputs. +0.6%.
- LAIR_NONCE_DIR: the host precomputes the state after the first linear
  layer for the batch's first nonce (first_layer_after_absorb, unit test
  against the matrix column), and the kernel derives each nonce's state
  as that plus a scalar times column 7 of the external matrix; nonces
  past a carry out of the low limb take the old path. +0.25% on beast,
  +0.6% on the 4090 and 3060.

Also tried and rejected: 512-thread blocks (-0.4%), a smaller grid via
MINER_CUDA_THREADS_PER_SM=16384 (-0.6%), a larger one (65536, within
noise of two waves), and the whole S-box as one PTX block, which
compiles to byte-identical SASS. The threads-per-block knob
(MINER_CUDA_THREADS_PER_BLOCK with LAIR_TPB) stays for experiments.

beast 2x5090: 2146 -> ~2206 MH/s (+2.8%); benjy 4090: 711 -> ~718;
quadbrat 3060: 140.2 -> 141.2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ue5ZZm1Hiv5zPnucykKKuF
2026-09-13 23:54:17 +03:00
dc6c58ceb6 Merge pull request 'perf: fused-PTX field arithmetic, +54..66% on every card' (#23) from perf/fused-field-ptx into main
All checks were successful
ci / fmt (push) Successful in 19s
deploy / build (push) Successful in 1m33s
ci / clippy (push) Successful in 1m46s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 46s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 46s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 47s
ci / doc (push) Successful in 1m58s
ci / test (push) Successful in 7m1s
2026-09-13 19:40:26 +00:00
3763996ae6 engine-cuda: fused-PTX field arithmetic, +54..66% on every card
All checks were successful
ci / fmt (pull_request) Successful in 20s
bench / build (pull_request) Successful in 1m16s
ci / clippy (pull_request) Successful in 1m44s
bench / measure (pull_request) Successful in 2m52s
ci / doc (pull_request) Successful in 2m1s
ci / test (pull_request) Successful in 6m59s
The closed-source qpow-cuda kernel runs 2x our hashrate on the same
silicon with the same 1472 multiplies per hash. Its PTX shows why: every
field operation is one self-contained inline-PTX block, the 128-bit
product is reduced with mad.lo.cc/madc.hi.cc (one IMAD.WIDE with a
carry-out) plus three ALU ops, the internal layer's row sum rides in the
mad.wide partial products for free, and accumulators are 32-bit limb
chains that ptxas turns into IADD/IADD.X with merged carries. Ours paid
a compare-and-select reduction (~14 ops), a 64-bit add-with-carry per
diagonal element, and five instructions per accumulator add on sm_120.

This ports those primitives behind LAIR_FUSED_MUL (default 1): gf_mul,
gf_sqr (three-product squaring), gf_mul_add with the addend folded into
the partial products, acc_add/acc_add2 as limb chains, and a four-op
acc_reduce. The round constant now joins the unreduced sum in the
internal layer.

LAIR_EXACT_REDUCE (default 0) keeps the reduction bit-exact for three
more ops per multiply. With 0 the final borrow is dropped, as in their
kernel: wrong only when the product's bits 64..95 are zero and its low
64 bits are below its top 32 bits, about 2^-64 per multiply (emulated
against a reference: 0/3M random mismatches, 2^48*2^48 fails as
predicted). Exactness measured at -15% on beast, so it is off.

Measured, three interleaved rounds, parity 40/40 on each host:
  beast 2x5090  1356 -> 2146 MH/s  (+58%)
  benjy 4090     460 ->  711 MH/s  (+54%)
  quadbrat 3060   84 ->  140 MH/s  (+66%)
Static sm_120 31,280 -> 20,888 instructions, sm_86/89 39,208 -> 22,576,
no spills. ncu: 22,913 instructions per nonce against their 22,233.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ue5ZZm1Hiv5zPnucykKKuF
2026-09-13 22:29:46 +03:00
78dee04ff6 Merge pull request 'perf: parameter-bank uniforms and per-arch carry path (+20.5% sm_120)' (#22) from perf/sm120-spills into main
All checks were successful
ci / fmt (push) Successful in 20s
ci / doc (push) Successful in 2m34s
ci / clippy (push) Successful in 2m39s
ci / test (push) Successful in 7m8s
deploy / build (push) Successful in 1m46s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 35s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 35s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 35s
2026-09-13 17:20:53 +00:00
575c4d0abd engine-cuda: per-arch carry path, +20.5% on sm_120
All checks were successful
ci / fmt (pull_request) Successful in 19s
bench / build (pull_request) Successful in 1m31s
ci / clippy (pull_request) Successful in 2m21s
ci / doc (pull_request) Successful in 2m24s
bench / measure (pull_request) Successful in 4m15s
ci / test (pull_request) Successful in 7m51s
An `asm` block is opaque to nvcc's optimiser, so carry arithmetic written
as inline PTX blocks common-subexpression elimination and strength
reduction across neighbouring field operations. On Blackwell that costs a
quarter of the kernel. On Ada and Ampere the hand-written sequence still
wins, by a similar margin in the other direction, so the default is now
chosen per device pass.

Whole-kernel instructions (ptxas + cuobjdump, CUDA 13.0) and measured
hashrate, three interleaved rounds each:

                    sm_120                sm_86/89
  PTX_CARRY=1   37,432   1127.1 MH/s   37,027   benjy 461.6  quadbrat 85.5
  PTX_CARRY=0   27,837   1358.0 MH/s   41,196   benjy 401.5  quadbrat 76.3

sm_86/89 keep the exact configuration they had, so those cards are
unchanged by construction -- identical macro values, identical 37,027
instructions. Parity 60/60 against the CPU on beast.

LAIR_PTX_ACC is now 1 everywhere rather than following LAIR_PTX_CARRY. In
the Acc accumulators the add-with-carry pair is the whole operation, so
there is nothing around it for the optimiser to fold; on sm_120 with the
carry path off, Acc PTX on measured 1333.3 against 1285.8 with it off.

PR #17 measured the PTX carry path as a win on every card. That was true
of the kernel it was written for, which spilled 104 bytes per thread;
once the spills went the trade reversed on Blackwell only.

Found by profiling rather than reading: ncu puts us at 43,700 executed
instructions per nonce against the rival binary's 22,229 on the same
card, a 1.97x ratio that matches the 2.05x throughput gap, with 82% of
the excess on the ALU pipe rather than the multiply pipe. This lands
35,785. The remaining gap is still ALU: 26,586 against their 14,413,
while the multiply pipe is now close to parity at 8,405 against 7,030.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ytCkttecH9SNTUEw8VT9D
2026-09-13 19:35:32 +03:00
206aa0d876 engine-cuda: record why gf_sqr uses the general product
The three-multiply squaring is a real algebraic saving and a measured
loss. Implemented, verified bit-exact against __int128 over 40M random
values plus limb and power-of-two boundaries, then benchmarked on beast:
1072.9 vs 1137.7 MH/s over three interleaved rounds, 5.7% slower.

It trades 6.7% of the kernel's widening multiplies for 11% more
instructions -- 5 IMAD.WIDE in 56 instructions against the general
product's 6 in 48 -- and four formulations (condition-code carry, compare
carry, two mad.wide.u32 variants) all compiled to the same 5/56, so that
is the floor rather than a tuning failure.

Taken with the LAIR_INT_UNROLL result, the picture is consistent: this
kernel is bound by instruction issue and by the cards' power envelope,
not by multiply throughput or occupancy. Re-expressing the same work
cannot win; only removing work can. Left as a comment so the next person
does not spend the afternoon rediscovering it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ytCkttecH9SNTUEw8VT9D
2026-09-13 18:42:48 +03:00
7804d9d442 engine-cuda: launch uniforms in the parameter bank, no spills
The kernel copied midstate, target and the nonce base into per-thread
register arrays and held all 56 registers live across the whole nonce
loop, which is exactly what the permutation's own working set needed.
ptxas spilled 104 bytes per thread on sm_120 and 44 on sm_86/89; the SASS
carried 26 LDL + 26 STL inside the hot loop.

They are launch-uniform, so they now travel as a by-value MiningUniforms
parameter. Parameters live in constant memory -- broadcast and cached, no
register residency -- and arrive with the launch, so three memcpy_htod
calls per batch are gone with them. Only the low eight nonce limbs are
materialised now; on a hit the high half is written from the parameter
bank. 128 -> 80/80/106 registers on sm_86/89/120, zero spill everywhere.
Both sides assert the 224-byte layout, so a mismatch is a build error
rather than a silent mining bug.

Measured A/B on the fleet, same session, same silicon, parity verified
against the CPU (25/25 on quadbrat and benjy, 40/40 on beast):

  quadbrat  3060    83.45 -> 84.60 MH/s   +1.38%
  benjy     4090   457.87 -> 461.77 MH/s  +0.85%
  beast   2x5090  1133.97 -> 1138.79 MH/s +0.43%
  fleet           1675.29 -> 1685.16 MH/s +0.59%

Not taken: a per-arch LAIR_INT_UNROLL of 1 on sm_120. It is better by
every static measure -- 78 registers instead of 106, three resident
blocks per SM instead of two, no spill either way -- and measured 0.92%
slower on beast over three interleaved rounds. These cards are
power-bound, not latency-bound: the extra warps buy power draw and the
card clocks down to pay for it. Benjy shows the same thing from the other
side, delivering +0.85% at a lower clock (2085 vs 2160 MHz), +4.4% per
MHz. Occupancy is not the lever here; work per hash is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ytCkttecH9SNTUEw8VT9D
2026-09-13 18:32:20 +03:00
2cfb2aae1d Merge pull request 'deploy: miner credentials from the mainnet chain' (#21) from deploy/mainnet-chain into main
All checks were successful
deploy / build (push) Successful in 1m19s
ci / fmt (push) Successful in 20s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 38s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 42s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 46s
ci / clippy (push) Successful in 2m8s
ci / doc (push) Successful in 1m55s
ci / test (push) Successful in 7m18s
2026-09-09 09:04:30 +00:00
b1c6eb2ba7 deploy: miner credentials from the mainnet chain
All checks were successful
ci / fmt (pull_request) Successful in 22s
ci / clippy (pull_request) Successful in 1m54s
ci / doc (pull_request) Successful in 2m6s
ci / test (pull_request) Successful in 7m39s
Mainnet cut over 2026-09-09 09:01 UTC; the miners already run with the
credentials from chains/mainnet/ (installed by hand). The push default
now matches, so the next deploy copies the same files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-09 12:03:21 +03:00
fd965ea312 Merge pull request 'deploy: beast joins the miner fleet for mainnet' (#20) from deploy/enable-beast into main
All checks were successful
ci / fmt (push) Successful in 20s
deploy / build (push) Successful in 1m37s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 37s
ci / clippy (push) Successful in 2m25s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 33s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 53s
ci / doc (push) Successful in 2m30s
ci / test (push) Successful in 7m32s
2026-09-09 06:15:02 +00:00
7ac9608b3d deploy: beast joins the miner fleet for mainnet
Some checks failed
ci / fmt (pull_request) Successful in 26s
ci / doc (pull_request) Successful in 2m25s
ci / test (pull_request) Successful in 7m33s
ci / clippy (pull_request) Failing after 12m11s
Mainnet call 2026-09-09: beast's two RTX 5090s mine alongside benjy and
quadbrat; neuron (cortex inference) is disabled on all three hosts. The
node's miners list and lair/quantus's SCRAPE_MINERS gain beast in the
lair/quantus counterpart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-09 09:14:32 +03:00
03b9c4239f Merge pull request 'deploy: chain id as a dispatch input for the miner credential path' (#19) from deploy/chain-input into main
All checks were successful
ci / fmt (push) Successful in 39s
ci / doc (push) Successful in 2m5s
ci / clippy (push) Successful in 2m57s
deploy / build (push) Successful in 3m43s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 42s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 45s
ci / test (push) Successful in 8m50s
2026-09-09 05:05:36 +00:00
9f41e0f3b9 deploy: chain id as a dispatch input for the miner credential path
All checks were successful
ci / fmt (pull_request) Successful in 46s
ci / clippy (pull_request) Successful in 2m56s
ci / test (pull_request) Successful in 6m49s
ci / doc (pull_request) Successful in 2m18s
The node generates the miner auth token and TLS pin per chain under
<base-path>/chains/<id>/, so the mainnet cutover needs one miner deploy
pointed at the new id (lair/quantus's node deploy prints it). The binary
is unchanged by a chain switch; only the credentials trigger the restart.
CHAIN stays the push-time default and is to be bumped after the cutover.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-09 08:05:04 +03:00
f061ccfeea Merge pull request 'bench: record the memory clock alongside the SM clock (schema 2)' (#18) from bench/record-memory-clock into main
All checks were successful
ci / fmt (push) Successful in 28s
deploy / build (push) Successful in 1m23s
ci / clippy (push) Successful in 1m38s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 38s
ci / doc (push) Successful in 1m45s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 2m38s
ci / test (push) Successful in 6m38s
2026-09-03 15:54:47 +00:00
a7a8ff0532 bench: record the memory clock alongside the SM clock (schema 2)
All checks were successful
ci / fmt (pull_request) Successful in 36s
bench / build (pull_request) Successful in 1m37s
ci / doc (pull_request) Successful in 1m42s
ci / clippy (pull_request) Successful in 2m5s
bench / measure (pull_request) Successful in 3m48s
ci / test (pull_request) Successful in 7m3s
The memory clock is a confound like the power limit: locking it at 810 MHz
gave +13% on the 4090 at the same watts (lair/quantus#10). Records from
before and after that change must be distinguishable.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 18:47:17 +03:00
ad66f1334f Merge pull request 'engine-cuda: deferred carries, carry-flag arithmetic, one nonce per thread' (#17) from cuda/tuning into main
All checks were successful
ci / fmt (push) Successful in 21s
deploy / build (push) Successful in 1m15s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 56s
ci / clippy (push) Successful in 2m13s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 37s
ci / doc (push) Successful in 2m17s
ci / test (push) Successful in 6m53s
2026-09-03 13:01:46 +00:00
b640d3a711 engine-cuda: fold round constants into the preceding linear layer; bench on cuda paths
All checks were successful
ci / fmt (pull_request) Successful in 23s
bench / build (pull_request) Successful in 1m0s
ci / clippy (pull_request) Successful in 1m57s
bench / measure (pull_request) Successful in 2m51s
ci / doc (pull_request) Successful in 1m52s
ci / test (pull_request) Successful in 7m25s
Each linear layer now folds the constant of the round that follows it into
its final deferred-carry reduction (or, for the internal rounds, into the
element-0 multiply-add), removing the per-element gf_add before every S-box.
Parity verified. On benjy the effect is within run-to-run noise: three
interleaved rounds gave medians of 418.5 vs 409.7 MH/s against the previous
default, with SM clock swinging 1935-2025 MHz across rounds. Kept on as
LAIR_FOLD_RC=1 for the instruction count; not claimed as a measured gain.

bench.yaml: crates/engine-cuda/** now triggers the harness on PRs. The
previous PR's kernel changes did not run it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 15:56:23 +03:00
2bf3246634 engine-cuda: deferred carries, carry-flag arithmetic, one nonce per thread
All checks were successful
ci / fmt (pull_request) Successful in 21s
ci / clippy (pull_request) Successful in 1m43s
ci / doc (pull_request) Successful in 1m41s
ci / test (pull_request) Successful in 6m52s
quantus/miner#3 step 5, measured on benjy (RTX 4090, 250 W, miner paused,
interleaved rounds, parity verified on every variant):

  variant                                  MH/s
  straight port (PR #16)                   297-305
  + grid cap 32768 threads/SM              304   (1 nonce per thread up to 4M)
  + deferred-carry linear layers           344   (+13%)
  + carry-flag gf_add / gf_reduce (PTX)    385   (+12%)
  + carry-flag accumulators                414   (+7%)

414 MH/s is 2.86x origin's wgpu build (144.4) on the same card and limit.
Fewer instructions per hash also let the power-capped card clock higher
(1920 -> 1995 MHz), which is part of the gain.

Rejected by measurement: __noinline__ permute (-13%: code size was not the
limit, the call convention was), -maxrregcount 80/64 (flat), internal-round
unroll 1 or 22 (flat, within noise of 2).

The batch sweep found batch size irrelevant once the grid keeps one nonce
per thread (304-306 MH/s from 1M to 16M); batch stays at 1M for the
smallest stale cost. All knobs remain as -D macros and MINER_NVCC_FLAGS
so the harness can keep testing variants.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 15:51:11 +03:00
d5e72d6a15 Merge pull request 'engine-cuda: native CUDA mining engine behind MinerEngine' (#16) from cuda/engine-skeleton into main
All checks were successful
ci / fmt (push) Successful in 20s
deploy / build (push) Successful in 1m19s
ci / clippy (push) Successful in 1m54s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 58s
ci / doc (push) Successful in 1m57s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 58s
ci / test (push) Successful in 7m25s
2026-09-03 11:57:42 +00:00
6ecc5ee6c7 engine-cuda: native CUDA mining engine behind MinerEngine
All checks were successful
ci / fmt (pull_request) Successful in 20s
bench / build (pull_request) Successful in 1m15s
ci / clippy (pull_request) Successful in 1m40s
ci / doc (pull_request) Successful in 2m6s
bench / measure (pull_request) Successful in 2m51s
ci / test (pull_request) Successful in 7m45s
quantus/miner#3. New crate, nothing in engine-gpu touched.

- kernels/mining.cu: the same host contract and sponge schedule as
  mining_u64.wgsl (host midstate, low-256-bit nonce increments, lazy second
  squeeze), bit-exact with pow_core. The field multiply is __umul64hi plus a
  plain multiply and one reduction, instead of four 32-bit partials with
  carry reconstruction.
- build.rs: generates poseidon2_constants.cuh from qp-poseidon-constants
  (a hash change at origin is a dependency bump, never a hand-copied table)
  and, when nvcc is present, compiles one fat binary with cubins for
  sm_86/89/120 plus compute_120 PTX. Without nvcc it writes an empty image,
  the crate still builds, and CudaEngine::try_new fails with a clear message
  so the miner falls back to wgpu.
- lib.rs: CudaEngine with the same batch loop, cancellation, thread-local
  device assignment, metrics (kernel label "cuda") and stale accounting as
  engine-gpu. cudarc 0.19 with dynamic loading; no link-time CUDA dependency.
- miner-service: resolve_gpu_configuration tries CUDA first unless
  --gpu-engine wgpu; --gpu-engine cuda makes its absence an error.
  miner-cli gains the flag on serve and benchmark (MINER_GPU_ENGINE).
- bench-harness: --engine auto|cuda|wgpu.
- deploy.yaml: matrix rows carry the kernel validate expects; a wgpu
  fallback on a CUDA host now fails the deploy instead of passing at a
  fraction of the hashrate.

Measured on quadbrat (RTX 3060, 130 W, miner paused, 2 x 10 s windows,
parity 5/5 verified against CPU): wgpu 37.3 MH/s, CUDA 59.5 MH/s (1.59x).
Grid sizing 8192 threads/SM or more is flat; 2048 loses 10%.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 14:52:10 +03:00
48ab3933ba Merge pull request 'metrics: stale work is the batch in flight at cancellation, not the whole search' (#15) from metrics/stale-semantics into main
All checks were successful
ci / fmt (push) Successful in 20s
deploy / build (push) Successful in 1m5s
deploy / deploy (1, benjy.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 1m10s
ci / clippy (push) Successful in 2m33s
ci / doc (push) Successful in 2m32s
deploy / deploy (1, quadbrat.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 47s
ci / test (push) Successful in 7m25s
2026-09-03 11:28:43 +00:00
20749c622b metrics: stale work is the batch in flight at cancellation, not the whole search
All checks were successful
ci / fmt (pull_request) Successful in 21s
bench / build (pull_request) Successful in 1m1s
ci / clippy (pull_request) Successful in 1m55s
ci / doc (pull_request) Successful in 1m55s
bench / measure (pull_request) Successful in 2m56s
ci / test (pull_request) Successful in 6m35s
miner_stale_hashes_total was recorded in the QUIC loop for every result whose
job id no longer matched, which is every cancelled search: 74% of all hashes
on benjy flagged as stale on the first hour of data. That is the loop's
notion of a stale *result*, not wasted work; the hashes were done while the
job was current.

The wasted work is the batch that completes after the job was superseded.
Record that in the engine at the cancellation check, per device and kernel,
and drop the loop-level accounting. Expect one batch per job switch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 14:23:37 +03:00
1c8174951c Merge pull request 'metrics: attribute hashrate to build, device, kernel and job outcomes' (#14) from metrics/build-device-job into main
All checks were successful
ci / fmt (push) Successful in 32s
deploy / build (push) Successful in 1m14s
ci / clippy (push) Successful in 1m42s
deploy / deploy (1, benjy.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 51s
deploy / deploy (1, quadbrat.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 51s
ci / doc (push) Successful in 1m55s
ci / test (push) Successful in 6m56s
2026-09-03 11:16:25 +00:00
3ba996dcdf metrics: attribute hashrate to build, device, kernel and job outcomes
All checks were successful
ci / fmt (pull_request) Successful in 20s
bench / build (pull_request) Successful in 1m2s
ci / clippy (pull_request) Successful in 1m37s
ci / doc (pull_request) Successful in 1m58s
bench / measure (pull_request) Successful in 2m55s
ci / test (pull_request) Successful in 6m50s
quantus/miner#9. Additive metrics in a lair-owned module of the metrics
crate; origin's metrics are untouched so the fleet dashboard keeps working
across origin merges.

Identity:
- miner_build_info{version, commit}: the join key for everything below.
- miner_config_info{engine, gpu_batch_size, gpu_devices, cpu_workers,
  gpu_throttle_ms}: two deploys of one commit with different flags are
  different experiments.

Per device (handles resolved once at engine init, no label lookups in the
batch path):
- miner_device_hashes_total{device, kernel}, miner_device_solutions_total,
  miner_device_lost_total.
- miner_gpu_batch_seconds{device, kernel, phase=gpu|host}: submit-to-mapped
  on the device versus everything else in the batch. This is the bubble
  #4, #5 and #6 attack, measured directly.

Jobs and results:
- miner_jobs_received_total, miner_stale_hashes_total{engine},
  miner_job_pickup_seconds{engine} (issued to picked up; the cancel latency
  of a busy worker), miner_results_submitted_total,
  miner_results_send_failed_total, miner_seal_latency_seconds (found to
  sent), miner_job_idle_seconds_total (sent to next job, node-attributable).

Connection: miner_connects_total, miner_connect_failures_total,
miner_disconnects_total, miner_connected, miner_disconnected_seconds_total.

Origin-owned code touched, each block marked lair: engine-gpu gains a
metrics dependency, a DeviceMetrics handle on GpuContext and timing points
in run_single_batch; miner-service gains found_at on WorkerResult,
created_at on MiningJob and the recording calls; miner-cli sets build info
at startup. Deploy validate now asserts miner_build_info carries the
deployed commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 14:09:43 +03:00
f1064345e5 Merge pull request 'deploy: wait for the miner to connect before judging its metrics' (#13) from deploy/validate-readiness into main
All checks were successful
ci / fmt (push) Successful in 44s
deploy / build (push) Successful in 1m9s
deploy / deploy (1, benjy.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 1m31s
ci / clippy (push) Successful in 1m51s
deploy / deploy (1, quadbrat.hanzalova.internal, bob.hanzalova.internal) (push) Successful in 50s
ci / doc (push) Successful in 2m16s
ci / test (push) Successful in 7m7s
2026-09-03 10:38:21 +00:00
40b4ea32ca deploy: restore the previous binary with install, not cp
All checks were successful
ci / fmt (pull_request) Successful in 22s
ci / clippy (pull_request) Successful in 1m51s
ci / doc (pull_request) Successful in 2m10s
ci / test (pull_request) Successful in 6m56s
The first rollback failed with "cp: cannot create regular file
'/usr/local/bin/quantus-miner': Text file busy": cp writes in place and the
binary was executing. install unlinks the destination first, the same reason
rsync's temp-file-and-rename push works. Sudoers grant updated and applied
to benjy and quadbrat.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 13:38:09 +03:00
b10f2f0099 deploy: wait for the miner to connect before judging its metrics
All checks were successful
ci / fmt (pull_request) Successful in 20s
ci / clippy (pull_request) Successful in 1m49s
ci / doc (pull_request) Successful in 2m40s
ci / test (pull_request) Successful in 8m1s
The first deploy-mode run sampled /metrics one second after the restart.
miner_gpu_devices is exported only once the miner has connected to the node
and miner_hashes_total only once it has hashed, so both were absent, validate
failed, and rollback restored the previous binary on a good deploy.

Validate now waits up to 90 s for miner_gpu_devices to appear (readiness),
then takes the first non-empty miner_hashes_total sample as the baseline and
requires a larger one within 120 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 13:37:23 +03:00
95b6420793 Merge pull request 'deploy: build on push to main and land the binary on the mining hosts' (#12) from deploy/on-main into main
Some checks failed
ci / test (push) Blocked by required conditions
ci / fmt (push) Successful in 25s
deploy / build (push) Successful in 1m5s
ci / clippy (push) Successful in 1m50s
ci / doc (push) Successful in 1m49s
deploy / deploy (1, benjy.hanzalova.internal, bob.hanzalova.internal) (push) Failing after 2m37s
deploy / deploy (1, quadbrat.hanzalova.internal, bob.hanzalova.internal) (push) Failing after 2m30s
2026-09-03 10:33:17 +00:00
b9e1d21ec9 deploy: build on push to main and land the binary on the mining hosts
All checks were successful
ci / fmt (pull_request) Successful in 24s
ci / clippy (pull_request) Successful in 1m38s
ci / doc (pull_request) Successful in 2m0s
ci / test (pull_request) Successful in 7m47s
quantus/miner#8. The miner is now deployed from this repo, not from
lair/quantus's release-pinned workflow (lair/quantus#4 retires that half).

- .gitea/workflows/deploy.yaml: build on cuda-13.0 (Fedora 43 like the
  hosts, and the runner with nvcc for #3) with the commit embedded, then per
  host in the matrix: sudoers preflight against deploy/infra-setup.sh,
  credentials copied from the node, miner.env rendered with the node's mesh
  IP resolved on the miner host, checksum-gated push of binary and unit,
  scoped firewalld rule for the exporter, restart only on change. Validate
  asserts the unit is active, --version carries this commit, the miner sees
  the matrix's device count, and miner_hashes_total advances within 120 s.
  If validate fails after a restart, the previous binary is restored.
- deploy/: the unit, sysusers, firewalld service and env template moved
  verbatim from lair/quantus asset/, plus infra-setup.sh with the miner role
  (and two new cp grants for the rollback). Applied to benjy and quadbrat.
- Matrix: benjy and quadbrat; beast present but commented out per the
  operating policy in #1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 13:28:52 +03:00
ad846af984 Merge pull request 'bench: quantus-bench harness and a workflow that measures on a mining host' (#11) from bench/harness into main
All checks were successful
ci / fmt (push) Successful in 20s
ci / clippy (push) Successful in 1m55s
ci / doc (push) Successful in 1m52s
ci / test (push) Successful in 6m21s
2026-09-03 10:19:33 +00:00
ca6642960c bench: serialise measurements per host with a flock, per-run file names
All checks were successful
ci / fmt (pull_request) Successful in 31s
bench / build (pull_request) Successful in 1m1s
ci / clippy (pull_request) Successful in 1m55s
bench / measure (pull_request) Successful in 2m59s
ci / test (pull_request) Successful in 9m7s
ci / doc (pull_request) Successful in 1m42s
Gitea's concurrency group did not serialise two runs on benjy: the second
run's scp failed with ETXTBSY on the binary the first was executing (safe,
it happened before the miner was touched, but the run was lost). The pause,
lock, measure and resume sequence now lives in bench-on-host.sh, executed
over one ssh call under flock on the host, with per-run binary and record
names so staging never collides.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 13:06:45 +03:00
5f3c51c5cb bench: allow more worker threads than cards to measure submission overlap
Some checks failed
ci / fmt (pull_request) Successful in 21s
bench / build (pull_request) Successful in 56s
bench / measure (pull_request) Failing after 8s
ci / clippy (pull_request) Successful in 1m49s
ci / doc (pull_request) Successful in 1m59s
ci / test (pull_request) Successful in 7m0s
Two threads on one card overlap one thread's readback with the other's
dispatch, which is the bubble quantus/miner#4 and #5 target. Measuring it in
the harness first means #4 lands with a number instead of a hypothesis.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 13:03:21 +03:00
36ffbbadbf Merge pull request 'ci: gitea workflow, drop inherited github workflows, embed commit in --version' (#10) from ci/gitea-workflows-and-build-sha into main
All checks were successful
ci / fmt (push) Successful in 51s
ci / clippy (push) Successful in 1m46s
ci / doc (push) Successful in 2m50s
ci / test (push) Successful in 10m57s
2026-09-03 10:02:39 +00:00
bad02fbc33 bench: quantus-bench harness and a workflow that measures on a mining host
All checks were successful
ci / fmt (pull_request) Successful in 20s
bench / build (pull_request) Successful in 57s
ci / doc (pull_request) Successful in 1m46s
ci / clippy (pull_request) Successful in 2m16s
bench / measure (pull_request) Successful in 2m49s
ci / test (pull_request) Successful in 10m33s
quantus/miner#2. The measurement gate every kernel and submission change
passes before merge.

crates/bench-harness (lair-owned, never touched by origin merges):
- drives GpuEngine through MinerEngine, one worker thread per card, with
  fixed timed windows after a warm-up; reports per-worker median MH/s and
  spread and refuses (exit 5) when spread exceeds 2%
- captures nvidia-smi state (driver, power limit, draw, clock, temperature)
  before and after, and refuses to run (exit 3) when the enforced power limit
  is not the expected one, since that is the largest confound on these cards
- GPU/CPU parity on random jobs, same shape as engine-gpu's gpu_cpu_parity
  example, exit 4 on any mismatch
- one JSON record per run (schema 1) plus a markdown summary on stdout
- build.rs embeds the commit like miner-cli's

.gitea/workflows/bench.yaml:
- runner containers on the GPU hosts have no device passthrough, so the
  binary is built on cuda-13.0 (Fedora 43, matching the hosts; the rust
  image is Fedora 44 and its binaries need a newer glibc) and executed on
  the host over ssh as gitea_ci
- stops quantus-miner.service for the window and starts it again under a
  trap; refuses to measure a card that is busy with the miner stopped
- benjy (4090, dedicated) by default; host, duration, runs, batch size and
  workers are dispatch inputs; PRs touching engine or service crates trigger
  it and get the summary as a comment
- one measurement per host at a time (concurrency group)

The stop/start sudoers grants were added to lair/quantus infra-setup.sh
(lair/quantus#5) and applied to benjy and quadbrat.

Origin coupling: one line in the workspace members list, marked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 12:58:48 +03:00
db6316713a ci: add rustfmt and clippy components explicitly
All checks were successful
ci / fmt (pull_request) Successful in 20s
ci / clippy (pull_request) Successful in 2m4s
ci / doc (pull_request) Successful in 2m8s
ci / test (pull_request) Successful in 6m27s
rustup auto-installs the pinned toolchain from rust-toolchain on the runner
but not its listed components; the fmt job failed with "cargo-fmt is not
installed for the toolchain". Add the component in the two jobs that need it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 12:54:29 +03:00
094536a341 ci: gitea workflow, drop inherited github workflows, embed commit in --version
Some checks failed
ci / fmt (pull_request) Failing after 39s
ci / clippy (pull_request) Has been skipped
ci / test (pull_request) Has been skipped
ci / doc (pull_request) Has been skipped
First lair-only change on top of origin v4.0.2 (quantus/miner#8, #1).

- Delete .github/ (workflows and the disk action). Gitea Actions reads
  .github/workflows too, so origin's GitHub-hosted jobs would queue forever
  for runners that do not exist here. Standing resolution on every
  origin-main merge: `git rm -r .github/workflows`.
- Add .gitea/workflows/ci.yml on the `rust` runner: fmt, clippy with
  -D warnings (same invocation as clippy.sh), build, test, doc, and a check
  that the built binary reports a commit in --version.
- crates/miner-cli/build.rs embeds MINER_BUILD_SHA (env override, else
  git rev-parse with a -dirty marker, else "unknown") and --version prints
  "<semver> (<sha>)". A deploy that follows main needs to assert the running
  binary is the commit it shipped; the semver alone cannot do that.

Origin coupling: .gitea/ and build.rs are new files; main.rs gains one const
and one attribute argument behind a `// lair:` marker.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 12:50:45 +03:00
Nikolaus Heger
cb6deb9139 bump version to v4.0.2 (#93)
Some checks failed
CI / 🏁 Fast Checks (Format) (push) Has been cancelled
CI / 🛠️ Build & Test (push) Has been cancelled
CI / 🤖 Analysis (Clippy & Doc) (push) Has been cancelled
CI / 🏃 Benchmark (push) Has been cancelled
Co-authored-by: n13 <n13@users.noreply.github.com>
v4.0.2
2026-09-01 23:25:24 +08:00
Nikolaus Heger
09323d33e4 Different kernels for Apple Metal vs Nvidia and everything else (#92)
* Select Apple Metal 4.0.1 kernel; keep 4.0.0 u64 elsewhere

NVIDIA was ~37% slower on the 4.0.1 Metal-tuned kernel. Split
Poseidon2 kernels and pick by backend:

- Metal + SHADER_INT64: v4.0.1 u64
- other SHADER_INT64: v4.0.0 u64
- no SHADER_INT64: 32-bit fallback (same as 4.0.0)

CI uploads the Linux release binary as an artifact so we can
bench NVIDIA without building on the GPU box.

* Drop version numbers from GPU kernel labels

Log native-u64 vs native-u64 Apple Metal without tying
the strings to release tags.
2026-09-01 23:21:43 +08:00
Nikolaus Heger
ceff470a5f ci: pin the stable rust toolchain (#91)
CI resolved the floating stable channel, so Rust 1.98 shipped the
chunks_exact_to_as_chunks clippy lint and failed checks that passed on
an older local stable. Pin the exact version, matching how chain pins
its toolchains.
2026-09-01 19:03:54 +08:00
Nikolaus Heger
41ec132139 bump version to v4.0.1 (#89)
Co-authored-by: n13 <n13@users.noreply.github.com>
v4.0.1
2026-09-01 19:03:05 +08:00
Nikolaus Heger
ea5e5e742e Speed up the u64 mining kernel ~1.8x on Apple Metal (#87)
Apple M5 Pro, CLI benchmark: 24.0 -> 43.2 MH/s. Kernel output is
unchanged and bit-exact against the CPU reference.

Field arithmetic:
- Deferred-carry additions (Acc): the external layer and the internal
  row sum accumulate unreduced with a carry counter and fold once per
  output instead of two epsilon corrections per add.
- gf64_mul_add folds the row sum into the 128-bit product before a
  single plain reduction.
- mul_wide / gf64_sqr assemble the 128-bit product from 32-bit-valued
  partial sums with no carry compares.

Kernel structure (code size dominates on Apple's compiler; every
unrolled variant measured slower):
- One loop drives all 30 rounds so each layer is emitted once, with
  round constants added by the linear layer preceding each S-box
  (RC_EXT table, RC_INTERNAL padded with a trailing zero).
- One S-box site with a runtime lane count (12 external, 1 internal).
- mining_main runs pad / first squeeze / second squeeze through a
  single inlined permute64 call in a phase loop.

Measured and rejected: uniform buffers and unhoisted loads, workgroup
sizes 64/128/512, 8-add MDS, one-compare reduce, fully unrolled
layers, two nonces per thread.
2026-08-31 22:50:27 +08:00
I Dewa Gede Bisma Mahendra
696eb51125 feat: add dependency cooldown (#88) 2026-08-28 14:11:24 +08:00
Nikolaus Heger
8d0c9f8023 docs: update systemd example for the serve CLI with required node auth (#86)
* docs: update systemd example for the serve CLI with required node auth

The unit and readme still described the old HTTP-API miner (MINER_PORT,
MINER_ENGINE, no subcommand). Update ExecStart to 'serve' and document the
now-required MINER_AUTH_TOKEN_FILE / MINER_TLS_CERT_SHA256_FILE, including
how to copy the node's miner-auth-token and miner-tls-cert-sha256 files
past ProtectHome=true, and refresh the env var reference to the current
CLI.

* docs: fix env-file example and CPU-worker claims per review

- Move inline comments off the env assignments: systemd EnvironmentFile=
  keeps inline '# ...' text as part of the value, so the copied example fed
  Clap unparseable values and the unit crash-looped under Restart=always.
- Describe the real worker behavior: CPUs are counted via the process
  affinity mask (num_cpus); unset MINER_CPU_WORKERS auto-detects ~50%, and
  an explicit value is used as-is — there is no clamp or warning.
- Drop the 'logs the detected cpuset mask' claim from the unit and override
  comments; no such logging exists in the miner.

* docs: metrics exporter is always on; purge-chain does not rotate credentials

- The Prometheus exporter starts unconditionally and binds plaintext HTTP on
  0.0.0.0:9900 by default; MINER_METRICS_PORT only changes the port. Say so
  everywhere instead of 'when metrics are enabled', and add a hardening note
  to firewall the port.
- Rephrase the credential-rotation pitfall: purge-chain removes only the
  database, so the node reloads the same miner-auth-token and TLS cert;
  rotation only happens when the base path changes or the files themselves
  are deleted.
2026-08-14 11:46:35 +08:00
illuzen
37f7837f76 bump version to v4.0.0 (#85)
Co-authored-by: n13 <n13@users.noreply.github.com>
v4.0.0
2026-08-13 19:34:16 +08:00
illuzen
2c8d530de4 Send miner auth token in Ready handshake. (#78)
* Send miner auth token in Ready handshake.

Require --auth-token or --auth-token-file when connecting to a node so the
miner matches the node's shared-secret QUIC auth.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Pin the node's miner TLS certificate by SHA-256 fingerprint.

Require --tls-cert-sha256 or --tls-cert-sha256-file so miners reject
MITM certs instead of accepting any self-signed server certificate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fmt

* Docs: prefer reading miner auth token from file, not node logs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Version the miner ALPN as quantus-miner/2 to match the node.

A node/miner protocol mismatch now fails cleanly at the TLS handshake with
"no application protocol" instead of an opaque auth error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* point at git

* Depend on published quantus-miner-api 0.3.0 from crates.io.

The temporary git branch pin is no longer needed now that 0.3.0 is published.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fail closed on permanent auth/TLS misconfig instead of reconnect looping.

Validate the fingerprint and Ready frame size at miner/pool startup before
metrics, workers, or HTTP come up. Treat node auth rejection and pin mismatch
as PermanentConnectError (no retry), and only reset reconnect backoff after
the first NewJob proves auth succeeded.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Point miner auth docs and examples at miner-auth-token / TLS pin files.

The node never logs the auth token; CLI/pool help and every serve/Docker
example now require the chain config files (or env vars) instead of implying
credentials can be copied from logs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fmt

* Remove the miner Dockerfile and Docker packaging docs/CI.

Container GPU access is a poor fit for this miner, and the Docker examples
were unusable with SocketAddr (hostnames rejected). Run the native binary
instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Shut down the pool on permanent upstream auth failure; keep reconnecting otherwise.

Supervise run_node_client alongside HTTP so a bad token/pin ends the process
instead of leaving a live API on a dead upstream. Node restarts still reconnect
with backoff; the current job is cleared while disconnected so captchas are not
issued against a stale header.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Replace obsolete EXTERNAL_MINER_PROTOCOL.md with a pointer to MINING.md.

The local copy still described the pre-auth protocol (no Ready token, no pin,
old ALPN, 16 MB frames). Canonical docs live in quantus-miner-api and the
node's MINING.md.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Delete EXTERNAL_MINER_PROTOCOL.md; README already points at MINING.md.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 09:50:03 +08:00
Nikolaus Heger
2b966e780a Revert "Fuzz GPU/CPU parity at scale and CPU-verify every seal before submission"
This reverts commit b0cfc3005e.
2026-08-12 15:41:38 +08:00
Nikolaus Heger
b0cfc3005e Fuzz GPU/CPU parity at scale and CPU-verify every seal before submission
Investigating reported seal submission failures. Root cause analysis:
the miner's job-id staleness chain (worker check, quic-loop filter,
node-side job_id compare) cannot mislabel a candidate — internal and
node job ids are updated atomically together. The remaining benign race
is node-side: a block-template rebuild (new pre_hash, same parent) does
not bump the node's job counter, so an in-flight seal mined on the old
template passes the job-id check and fails seal verification, logging
"Failed to submit seal". Fixing that belongs in the chain repo (bump the
job counter on template rebuild).

Miner-side hardening in this commit:

- gpu_cpu_parity is now a two-phase fuzzer. Phase 1 dumps FULL 512-bit
  hashes for randomized (header, nonce) pairs via the real
  midstate-resume datapath and verifies each against the canonical CPU
  implementation, multi-threaded (defaults to 1M hashes, ~5s; 10M runs
  in ~15s). Phase 2 fuzzes whole mining jobs with five profiles: random,
  carry-edge starts (saturated low limbs), 2^256-boundary crossings,
  impossible difficulty with exact hash-count assertion, and CPU-known
  solutions to catch false negatives. Seeded and reproducible; also
  asserts the submitted work bytes match the nonce.
  Verified clean: 10M bulk hashes + ~1,500 seals across 6 seeds.

- miner-service re-verifies every candidate on the CPU (one hash) before
  sending JobResult; an engine-produced invalid seal is never submitted
  and is logged loudly instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 14:24:05 +08:00
Nikolaus Heger
9b16df1dff Merge pull request #81 from Quantus-Network/gpu-poseidon2-optimization
GPU mining 4.1x faster on Apple Silicon: native-u64 Poseidon2, midstate precompute, lazy second squeeze
2026-08-12 12:10:58 +08:00
Nikolaus Heger
7e449c0a5f Benchmark: honor --gpu-batch-size/--cpu-batch-size and advance nonce ranges
The benchmark fed each worker a fixed range (10k CPU / 1M GPU nonces),
capping GPU dispatches at 1M regardless of --gpu-batch-size, and
re-searched the same range on every iteration. Worker ranges now step
through the nonce space and are sized from the batch flags, floored at
the old widths so tiny flags keep engine-internal cancellation batching
instead of turning per-call harness overhead into the measured quantity.
Batch-size flags are validated non-zero at parse time (serve and
benchmark) and echoed in the benchmark banner.
2026-08-12 12:04:44 +08:00
illuzen
22d87d4772 fix benches 2026-08-12 11:16:26 +08:00