Receipt for soft3 mine (crate/src/chain/mine/, branch feat/chain-mine)
against specs/chain.md §1.1–1.3, §1.5, §8 and
chain-wire §7. Measured 2026-10-15 on an Apple
M4 Max (16 cores, macOS 26), release build, stand ~/cyber/.stands/N3
(soft3 83e9012 + this branch, lens release/0.2 + feat/mine-kernel
= lens#28, hemera origin/main, honeycrisp origin/main for aruminium
contents_ptr).
1. hashes per second, soft3 mine bench --seconds 10
| backend | hashes | seconds | H/s | conformance §5 nonce |
|---|---|---|---|---|
| cpu (16 threads) | 30,146,560 | 10.1 | 3.00 MH/s | found |
| cpu (1 thread) | 1,392,640 | 5.1 | 275.4 kH/s | found |
| metal (Apple M4 Max) | 163,577,856 | 10.1 | 16.13 MH/s | found |
| webgpu (Apple M4 Max (Metal)) | 71,303,168 | 10.2 | 7.02 MH/s | found |
One hash = four hemera permutations (168 bytes = 3 rate blocks + pad).
"found" = the backend returns index 933 for base 40,165,000 on the
chain-conformance §5 mined header (nonce 40,165,933) and the header verifies
with hemera::hash.
Readings:
- the spec's 64.4 kH/s per core (chain.md §1.2) is the scalar
hemera::hashpath; hemera's 16-lane interleaved batch permutation (permute_batch, one shared inversion per partial round) gives 275 kH/s on one core, 4.3×, and 3.0 MH/s on the machine — the "0.6 MH/s per machine" estimate of §1.2 is 5× low for a CPU miner written against the batch path; - the first competent GPU kernel on the same chip is 16.1 MH/s: 5.4× the
CPU of the same machine, 58× one core. A discrete GPU with a comparable
kernel is expected an order above this (the M4 Max's 13 MH/s WebGPU
permute2path is bandwidth-free: everything stays in registers); - WebGPU through wgpu on Metal reaches 44 % of native Metal (32-bit limb arithmetic, two states a thread vs four with a shared inversion); it is the Vulkan/Android/Linux path, good enough that a GTX 1080 (deimos, chain.md §9.2) will mine on day one without an MSL port.
2. what the genesis target implies
GENESIS_BITS = 2^232 → 2^24 = 16,777,216 expected hashes a block;
T = 30 s → the network rate the target is sized for is 559 kH/s. One
M4 Max CPU miner is 5.4× that (a block every 5.6 s at genesis), one M4 Max
GPU 29× (0.6 s). ASERT takes over within minutes (§3). The three cybernode
machines are x86 and were not measured; at the x86 batch path's expected
~150–250 kH/s a core they will not out-hash one laptop GPU.
Consequence for §8's threat table: at launch the network's hash rate is whatever the owner's machines plus a few volunteers bring, order 10–50 MH/s; a single rented GPU box with a competent kernel (100+ MH/s) is the majority miner, as the spec says — the reorg limit and the checkpoints remain the only defence, and the reference miner shipping its GPU kernels removes the secret kernel advantage, not the majority one.
3. ASERT (H = 600 s, T = 30 s) under a 100× step — integer simulation
crate/src/chain/mine/asert_sim.rs asert_behaviour_and_hundredfold_step
over the kernel's integer daa::asert (conformance §4 vectors green there,
including the floor-division and clamp rows), a deterministic miner model: a block at
target t and rate r takes round(2^256 / (t+1) / r) seconds, no
randomness, no floats.
| scenario | result |
|---|---|
| T-spacing, 200 blocks | bits constant 0x1d100000 |
| T/2 spacing for 600 s of block time (40 blocks) | target halved exactly (0x1d080000), spec's [1.9, 2.1] |
| 100× step up | interval back to ≥ 27 s from block 192, 31 min of wall time; 30 s after 1,000 blocks |
| 100× step up at the real anchor 2^232 | first block 0.3 s (rounds to 1), settled from block 212, same 31 min |
| 100× collapse (farm leaves after the step is absorbed) | first block 49 min; blocks ≤ 60 s again after 62 min |
Readings:
- chain.md §1.5's "a 10× step within about two hours" is conservative for
100×: the lead the chain needs is
H · log2(100)= 3,986 s, it grows at most 30 s a block, so absorption needs ≥ 133 blocks — the blocks are fast while it happens, so it is 192 blocks and 31 minutes of wall time; - the collapse side matches the spec's "k = 100 → 66 min": the target doubles every 600 s of wall time whether or not blocks arrive, so the first post-collapse block takes 49 min and the chain is back under a minute per block at 62 min. During that hour a farm that joined, mined for an hour at 100× and left has taken ~190 blocks' subsidy and leaves the chain nearly stalled for 50 minutes — the §8 residual, measured;
- H = 600 s therefore handles a 100× step in both directions within the hour the spec budgets; the price is the 49-minute gap after a collapse. A shorter half-life would shorten the gap and widen the timestamp-play window (§1.5); the trade is the owner's and this receipt only measures it.
4. conformance pinned by the miner's own tests
target.rs: the miner asks the kernel (chain::header::decode_bits,chain::pow::id_meets_target,chain::daa::asert) and implements no consensus of its own; onfeat/chain-minebefore the kernel merged the same questions were answered by a local copy (local.rs) pinned to the §2/§4/§5 vectors (all 13 bits rows, both headers, six ASERT rows), which the branchfeat/chain-mine-on-kerneldeletes.job.rs: the lane-wise sponge equalshemera::hashfor nonces in both words of the 128-bit nonce.mod.rsevery_backend_agrees_and_verifies: CPU, Metal and WebGPU return the same index for the same dispatch — the conformance nonce, a base in the high word, a base 20,000 below a 2^64 boundary, and a target nothing passes.tests/mine_e2e.rs: two blocks mined against a mock node (a dropped first request → retry, a stale template → refresh, PoW verified natively by the mock, nonce salt in the high word).- lens#28
tests/differential.rs: both kernels against lens's CPU reference andcyber_hemera::hash.
5. API added to the spec
chain-wire §7 gains the JSON of GET /template?to=, POST /submit
(200/409/422/400) and GET /tip, as the reference miner speaks them;
target repeats bits decoded so an external worker needs no compact
codec. The node side of these routes belongs to the kernel/api work.