The fleet's 10-member load run (6 October 2026, 18:14Z to 18:24Z) accepted 0 shares: the pool fork's miner re-checked GPU shares with a fixed class v2 program while the 0.3.14 workers hashed class v3 (docs/plans/pool.md section 9). pool: node.rs builds the share verifier's seeds from the template's pow_epoch with the class and the era, installs the node's genesis day, dataset size and class v3 activation beside the schedule, and sends program_class, next_program_class, era_seed, era_index, genesis_day_index, genesis_dataset_log2 and program_class_v3_activation_daa in `seeds` and program_class and era_seed in `job` (protocol.rs, additive fields); verify.rs tests through EpochSeeds::v2. Protocol, vardiff, PPLNS, stats API and page otherwise unchanged. Suite 22 of 22 on igneum-build-1. igneum-pow tests/recheck.rs: for class v3 (packs-ca2-mixer/mx8-devnet-epoch0) and class v4 (packs-ca3-v4/v4-devnet-epoch0) the pack's program reproduces the pack's 96 vectors (the worker's reference), the chain seam the node engine calls (Epoch::chain_program, chain_dataset_day) builds the same program, one known nonce hashes equal through both paths, and the fixed-v2 program of the same seed disagrees; the pinned v3 and v4 program ids differ. 2 of 2 on igneum-build-1. packaging/hive: the pool:// mode merged with master's per-card IDENTITIES=auto and OVERRIDE handling (selftest passes). pool/README.md and pool.md: building on igneum-build-1 (the vendor/igneum-node symlink on the box). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
11 KiB
igneum-pool: the Igneum mining pool, version 0
A public mining pool for Igneum (docs/spec/09-pool-protocol.md, docs/plans/pool.md). It talks to one or more
igneumd nodes, builds a template per member with the member's own vote key in the header, hands out jobs over the
spec 09 protocol (newline JSON over TCP), sets a share target per member so each sends about one share every 10 s,
verifies every share on the CPU warp verifier the node uses, submits found blocks to every node, pays PPLNS from the
pool's coinbase address on the EVM side with a configurable fee (default 1%), and serves the stats API that
WhatToMine and MiningPoolStats read plus a page.
Built 5 October 2026 on branch pool-v0. Nothing is deployed. Measured on a private fast-time network
(docs/plans/pool.md section 5): three CPU miners of different identities, 0 rejected and 0 stale shares in the
second run, 289 blocks found and 0 orphaned at 49% of the network, PPLNS paid in dry run and then for real (24
transfers, 0 failed, the miners' EVM balances rose by what the ledger said), the API at 100 requests per second with
p99 under 3 ms, share checks 2.1 ms each under load (0.44 ms idle, the crate bench).
Build
The crate reads the node fork by path from ../vendor/igneum-node (the fork checkout, or a symlink to the fork
worktree that carries the pool-mode miner, branch pool-v0 of the fork) and ../igneum-pow from this repository.
cd pool
cargo build --release # target/release/igneum-pool, about 2 min warm on the Mac
cargo test --release # vardiff, PPLNS, share validation against the warp verifier, the API fixture
The miner with pool mode: cargo build --release -p igneum-miner in the fork worktree (branch pool-v0).
Run
igneum-pool --node grpc://127.0.0.1:26610 --evm-rpc http://127.0.0.1:26790 \
--listen 0.0.0.0:4463 --http 127.0.0.1:4480 --data-dir /var/lib/igneum-pool \
--network devnet --fee-percent 1 --min-payout 1 --pplns-window 2 --payout-interval-s 60 \
--name "Igneum pool" --public-url pool.example.net:4463 [--dry-run]
| Flag | Default | Meaning |
|---|---|---|
--node |
grpc://127.0.0.1:26610 |
gRPC of a node; repeat for more. The first builds templates and confirms blocks; every one receives found blocks |
--evm-rpc |
http://127.0.0.1:26790 (devnet), 26890 testnet, 8545 mainnet |
the first node's eth_ JSON-RPC, for payouts |
--listen |
0.0.0.0:4463 (devnet), 4462 testnet, 4461 mainnet |
where members connect (spec 9.3 ports) |
--http |
127.0.0.1:4480 |
the stats API and the page; put a TLS terminator in front |
--data-dir |
data |
state.json (the ledger snapshot) and the payout key |
--payout-key |
<data-dir>/payout-key.json |
the pool's secp256k1 key, created on first start, mode 0600 |
--fee-percent |
1 |
the pool fee, taken from each confirmed block's reward before the PPLNS split |
--min-payout |
1.0 IGN |
a member's balance is paid once it reaches this |
--pplns-window |
2.0 blocks |
the window, in blocks of expected work (shares weigh 2^-s) |
--payout-interval-s |
60 |
a payout round every so often, at most 16 transfers per round |
--dry-run |
off | compute and record payouts, send nothing |
--network |
devnet |
devnet, testnet or mainnet: chain id (4463, 4462, 4461), default ports |
--share-interval-s |
10 |
vardiff target: one share per member per interval |
--min-shift, --max-shift |
0, 60 |
bounds on the share shift; the saturation cap always applies |
--stale-grace-ms |
2000 |
a share on a superseded job inside the grace still pays |
--orphan-after-daa |
120 |
a pending block this far behind the virtual and not blue is an orphan |
--verify-threads |
2 |
share checks running at once on the CPU |
At start the pool prints its payout address. That address is the pool's coinbase address: every template names it in
the IGNA field, so the execution layer credits it 80% of every blue block the pool finds. The 20% proving share goes
to the proving escrow by the chain's own rule (consensus/core/src/igneum.rs, igneum/exec/src/executor.rs); the
pool never touches it and cannot.
What the operator must secure
- The payout key (
payout-key.json). It holds every reward the pool earns until the payout round sends it on. Back it up off the box before the first block. Anyone with the file can empty the pool. Keep the data directory at mode 0700, the file at 0600 (the pool sets 0600 on creation), and never put it in a repository or a log. - The pool's HTTP port behind a TLS terminator (Caddy or nginx) with the page on one hostname and the member port
(4463) reachable; v0 speaks plain TCP to members, so the terminator does not cover shares yet (TLS for the member
port is the v0 gap named in
docs/plans/pool.md). - The node's RPC (
--rpclisten) and EVM RPC (--evm-rpclisten) on loopback only. The pool talks to them locally. state.json: the ledger. It is rewritten every 15 s; a lost file loses unpaid balances since the last payout, so back it up with the key.
Deploy on one Hetzner box (the seed-node pattern)
The seed nodes (infra/seed-nodes/README.md) are Hetzner VMs with a fixed IPv4, igneumd as a systemd unit, RPC on
loopback, built by the cross-compile on the Mac (infra/cross/build-linux.sh). A pool box is the same plus this
binary and two more ports.
- Create the VM as a seed would be created:
SEED_NAME=igneum-pool-1 SEED_TYPE=cpx31 ./create-seed.shfrominfra/seed-nodes(the firewall script opens 22 and 26611; add 4463 and 443). The seeds run on cx23 (2 vCPU, 4 GB, USD 6.49 a month net plus the IPv4, approximate from the seed plan); a pool wants a core for the node, a core for share checks (one share per 10 s per member, 0.44 ms each on an M5 Max core, so a server core at roughly half that rate covers thousands of members, approximate) and 8 GB for the node plus the pool's 256 MiB cache per day seed. A cpx31 (4 vCPU, 8 GB) is about USD 14 a month net (approximate, price list not re-read tonight). - Provision the node exactly as
provision-seed.shdoes, with--utxoindexadded and--evm-rpclisten=127.0.0.1:26790(the pool needs the EVM RPC for payouts). The node must be the devnet-v4 line or later (it reportspow_epochwith every template; the pool refuses to run against one that does not). - Cross-compile
igneum-poolwith the samecargo-zigbuildtarget as the node (x86_64-unknown-linux-gnu.2.36) frompool/, copy it to/opt/igneum/bin/igneum-pool, and install the unit below. Re-stamp copied sources withtouchbefore any build on the far side (the stale-build rule in CLAUDE.md); nothing is built on the server. caddy(or nginx) in front of127.0.0.1:4480forhttps://pool.<domain>/; the member port 4463 open in the Hetzner firewall.- First start with
--dry-runfor a day, read/api/blocksand/api/payments, then restart without it.
# /etc/systemd/system/igneum-pool.service
[Unit]
Description=Igneum mining pool
After=network-online.target igneumd.service
Requires=igneumd.service
[Service]
User=igneum
EnvironmentFile=/etc/igneum/pool.env
ExecStart=/opt/igneum/bin/igneum-pool --node grpc://127.0.0.1:26610 --evm-rpc http://127.0.0.1:26790 \
--listen 0.0.0.0:4462 --http 127.0.0.1:4480 --data-dir /var/lib/igneum-pool --network testnet \
--fee-percent 1 --min-payout 1 --name "Igneum pool" --public-url pool.igneum.network:4462
Restart=always
RestartSec=5
UMask=0077
[Install]
WantedBy=multi-user.target
Costs, approximate: the VM (USD 6.49 to 14 a month net by type), the IPv4 (Hetzner's primary IPv4 price, a few dollars), outbound traffic inside Hetzner's included allowance at a few hundred members (a template is a few KB per member per second in mode A; 1,000 members at 10 KB/s each is 10 MB/s, which is where mode B or a template RPC that takes a list of keys comes in, O-9.4). Measure before quoting.
Connect a miner
igneum-miner mine grpc://127.0.0.1:26610 1 100000000 rig1 --pool pool.example.net:4463 \
--evm-address 0xYOURADDRESS --worker-name rig1-gpu0 --worker ./igneum-worker-cuda --worker-args "--device 0 --pack packs/devnet"
igneum-miner mine none 4 100000000 rig1 --pool pool.example.net:4463 --evm-address 0xYOURADDRESS --worker-name rig1-cpu
The first argument is the member's own node (its verifier: seed checks, voting with its own key, a second road for
found blocks) or none (hashes, does not vote). --evm-address is the payout address, --worker-name the label on the
page. HiveOS: Flight Sheet pool URL pool://pool.example.net:4463 (packaging/hive/README.md). In pool mode the miner
software takes no dev fee; the pool's fee is in the welcome line the miner prints.
The API
| Path | What |
|---|---|
/api/stats |
pool (hashrate, miners, workers, shares, blocks 24 h and total, last block, fee, min payout, luck, effort, paid, share check cost) and network (chain id, difficulty, hashrate, DAA, reward) |
/api/blocks?limit=N |
blocks found: hash, DAA, time, finder, status (pending, confirmed, orphan), reward, effort, the PPLNS split |
/api/miners/<address> |
hashrate (10 min, 1 h, reported), shares, workers (with shift, proving flag, online), balance, paid, payments |
/api/payments?limit=N |
every payout with its transaction hash and receipt status |
/api/pool-stats |
the flat camelCase object pool dashboards poll (hashrate, miners, workers, blocks, lastBlock, fee, minPayout) |
The field lists are constants in src/api.rs; cargo test checks the fixtures in tests/fixtures/ against them.
Captured responses from the measured run are the fixtures.
Files
| File | What |
|---|---|
src/main.rs |
start-up, tasks, the status line, shutdown snapshot |
src/config.rs |
flags and defaults |
src/protocol.rs |
the spec 09 messages, newline JSON |
src/server.rs |
member connections: hello, authorize (proof of possession checked), shares, solutions, stats |
src/node.rs |
templates per member, jobs, block submission, the blue-set walk that confirms or orphans, network numbers, vardiff ticks |
src/verify.rs |
the share check on the warp verifier, the spec's codes |
src/vardiff.rs |
the share shift per member |
src/pplns.rs |
the window, the split, the distribution with the fee |
src/payout.rs |
the payout key, EIP-1559 transfers through eth_ JSON-RPC, receipts, dry run |
src/state.rs |
the ledger and its snapshot |
src/api.rs |
the HTTP server, the routes, the field contracts |
web/index.html |
the page |
tools/measure.mjs |
the private-network measurement |
Building on igneum-build-1 (6 October 2026)
Every Linux build and suite runs on the box through tools/build-remote.sh from this directory (IGNEUM_AGENT=pool). The
crate reads the fork through the vendor/igneum-node symlink; the build library syncs the fork worktree it points at as a
whole repository, and the symlink is made once on the box by hand: ln -s <fork worktree name> /srv/builds/<worktree>/vendor/igneum-node.
Artefacts land in pool/target-remote/release/igneum-pool (x86_64 Linux); the Mac builds no Linux binary.