Documentation
The PowMachina miner
Status: lane C (CPU) and lane G (GPU, NVIDIA) at the same time in one process;
solo against your own node (RPC) or over stratum (a node with -stratum, or a pool —
the stratum protocol).
How it works
powmachina-miner ──gettemplate {lane:C}──▶ powmachinad
│ a coinbase to its own address (chain.BuildCoinbase), the Merkle root, the header
│ RandomX v2 fast (a 2 GiB dataset shared by the threads), nonces i, i+n, i+2n…
├──submitshare {hex}──────────────▶ verifies the share, pools it, relays it; answers
│ whether it is a block too
└──submitblock {hex}──────────────▶ verifies the header (PoW, lane rotation) and the body,
stores it, cleans the mempool, announces it to the peers
- Shares (Sharepool §2): the miner looks for Sharepool shares, not only
blocks, and hands every one to its node with
submitshare; one the node says is a block goes on tosubmitblock. The weight of a lane’s shares follows the hashrate the miner measured, so that the lane sends at most one share every 2 s — a share of weight k counts 2^k, so the pay is the same and the network verifies fewer. Until the first measurement (30 s) it is the heaviest, 11. A node that does not hold the full DAG takes no shares (takes_shares: falsein the template), and the miner then looks for blocks only. - The template carries the transactions (
txs, hex); the miner builds the coinbase itself with a random extranonce — two miners with the same address never hash the same thing. - A new template: on a new tip (the miner asks every second), after 20 s (new transactions, the time) and immediately after a block is found. The threads switch within ~10 ms.
- At a new epoch (20,000 blocks, ~7 days at the 30 s block) the RandomX dataset is rebuilt (~5 s on 8 cores).
submitblockonly accepts a block on the current tip; otherwise it is “stale” and the miner takes a new template.- The algorithm calls
internal/mhdirectly — the PoW has exactly one implementation. The testTestMinerFindsValidBlocksverifies the blocks it finds with exactly what the node verifies them with.
Usage
POWM_RPC_TOKEN=… powmachina-miner --address powm1… # solo, the CPU + every GPU
powmachina-miner --pool stratum+tcp://node:7699 --address powm1… --worker rig1 # stratum
powmachina-miner --benchmark 60s # the speed without a node
On the testnet without a node of your own: two public full nodes take stratum. There is no
pool and no fee behind them — every share goes into the Sharepool and the chain pays it to the
address you mine to. The miner logs in with its own finality key by default (--finality-key), so
the finality vote of the blocks it finds stays its own, not the node’s.
powmachina-miner --testnet --pool stratum+tcp://stratum-t1.powmachina.com:17699 --address tpowm1… --worker rig1
# the second one: stratum-t2.powmachina.com:17699
Solo mining needs a node of your own with its RPC on — a node has it off unless asked:
powmachinad -rpc 127.0.0.1:7697 -datadir … # mainnet; testnet: -testnet -rpc 127.0.0.1:17697
The log then says RPC listening on 127.0.0.1:7697, which is where the miner looks by default
(--node for anywhere else). POWM_RPC_TOKEN is needed only when the node was started with one.
The node inside the desktop wallet cannot be mined against: it opens its RPC on a port and with a
token of its own, new at every start.
Started with no arguments — on Windows that is a double click — the miner asks instead: the
network, pool or solo (with the address format explained), the reward address, a worker name, and
whether the CPU, the GPU or both should mine. The answers become the same flags as above and can be
saved to powmachina-miner.conf next to the binary (one flag per line), so the next start goes
straight to mining; --setup asks again. A node RPC token is never saved.
Sharing a card: --gpu-duty <percent> leaves the card free the rest of the time — a kernel
cannot be slowed down, it can only be launched less often, so after each launch the miner waits long
enough for the ratio to work out (at 20 % with a 10 ms kernel it waits 40 ms). It does not free
the card’s memory: the lane G DAG has to stay uploaded, otherwise it would be rebuilt on every
round.
The GPU plugin sits next to the binary: libpowm-cuda.so on Linux (the build:
machinahash-cuda/plugin/build.sh), powm-cuda.dll on Windows (built by nvcc driving MSVC,
CI: .github/workflows/cuda-windows.yml). Both cover Turing sm_75 through Blackwell sm_120 + PTX
and need only an NVIDIA driver ≥ 580, no CUDA install. Without the plugin only the CPU mines. At
every epoch the miner compares the card’s hash with internal/mh — if it differed, it switches the
card off.
| flag | |
|---|---|
--address |
the address for the rewards (from powmachina-wallet-cli) — mandatory |
--node |
the node’s RPC, by default 127.0.0.1:<the network's RPC port> |
--threads |
the threads, by default every core |
--light |
RandomX without the 2 GiB dataset — orders of magnitude slower |
--no-large-pages |
do not try large pages (with them it is ~2× faster on an EPYC; needs vm.nr_hugepages) |
--benchmark 60s |
measures the speed without a node |
--testnet, --regtest |
the network; the miner verifies that the node runs on the same one |
--pool, --worker |
stratum: stratum+tcp:// or stratum+ssl://, the device’s name |
--finality-key |
stratum: your own finality key — auto (default: finality.key in your config directory, e.g. ~/.config/powmachina-miner/<network>/, made the first time), a path, or none. The blocks you find give the key a seat in the finality quorum and the miner signs for it, so the vote of your work stays yours and not the pool’s (finality §2) |
--cpu=false |
the CPU does not mine (GPU only) |
--gpu auto|on|off, --gpu-devices 0,2, --cuda-lib |
the GPU: auto = if there is a plugin and a card |
--gpu-duty 20 |
what % of the time the miner may occupy the card (the rest is left to other work) |
--no-affinity |
do not pin the threads to cores |
The default thread count: the cores, but at most L3 / 2 MiB (RandomX needs ~2 MiB of L3 per thread).
The threads are pinned to the physical cores first, then to the SMT siblings. Hashing is chained
(randomx_calculate_hash_first/next).
Lane rotation: after two blocks of one lane, a block from the other one has to come. The CPU
miner waits then (“no work … rotation rule”); if a whole lane goes down, the rule is waived after 15
minutes. On regtest without a GPU miner, generatetoaddress fills in for lane G.
Measured
| device | lane | speed | note |
|---|---|---|---|
| RTX PRO 6000 Blackwell | G | 1.79 MH/s | a 1 GiB DAG, part of it in the 128 MB L2 |
| RTX 3090 | G | 834 kH/s | 95 % of the theoretical memory ceiling |
RTX 4080 (Windows, powm-cuda.dll) |
G | 615 kH/s | 717 GB/s of memory bandwidth; the card was also driving the desktop |
| Ryzen 9 7950X3D (a VM, 16 vCPUs) | C | 11.75 kH/s | without large pages |
| a VM with 8 vCPUs (QEMU) | C | 4.47 kH/s | without large pages |
| EPYC 9474F (mh-bench, algorithm §7.7) | C | 12 / 25 kH/s | W^X on / off (the miner has it off) |
The GPU kernel: a straightforward port reaches less than half of these numbers. Nsight Compute shows why — every thread reading its DAG item in 16 B pieces from random places (50 % wasted sectors), and the mix in shared memory limiting the occupancy. So 16 threads of a warp load an item together in one coalesced read and write it straight into the owner’s mix, and the BLAKE3 inputs are assembled word by word in registers (0 B of stack). Rejected: a register cap (spilling), and a kernel with RANDMATH baked in at compile time (unrolling 64× does not fit in the instruction cache — 277 kH/s).