Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 953bf4a239)
446 lines
49 KiB
Markdown
446 lines
49 KiB
Markdown
# Pool v0: what it does, what it does not, what was measured, and the listing checklist
|
|
|
|
5 October 2026, branch `pool-v0` (main worktree `../igneum-wt-pool`, fork worktree `vendor/igneum-node-pool` on the
|
|
fork's `pool-v0` from `release-0.3.6` a24ab01a). REBASED 6 October 2026 (section 9): branch `pool-v0-rebase` on master, fork
|
|
branch `pool-v0-rebase` on `release-0.3.14-node` 4c6b129d (worktree `../igneum-wt-pool-rebase`, fork worktree
|
|
`vendor/igneum-node-pr`); the share re-check hashes the template's program class, never a fixed one. the project lead's ask: a public mining pool with the stats API WhatToMine and
|
|
HiveOS read, "the biggest step toward being listable". Built against `docs/spec/09-pool-protocol.md`, the miner's
|
|
template subscription and worker protocol (`igneum/miner/src/main.rs`), the Hive package's `local` mode
|
|
(`packaging/hive`), ledger F10 and G6 (pools and votes), and `docs/plans/explorer.md` section 6. Nothing is deployed.
|
|
|
|
## 1. Rust, and why
|
|
|
|
Spec 9.3 fixes the member protocol as newline-delimited JSON over TLS, not the node's gRPC. The miner is Rust and
|
|
talks gRPC to its node; the pool must verify every share on the CPU warp verifier (spec 9.8 item 5), which is the
|
|
node's own `IgneumEngine` in `consensus/pow/src/igneum.rs` (same program, cache and init words as block validation),
|
|
and it must build templates and submit blocks over the node's gRPC. Both are Rust crates by path. A Node service
|
|
would have had to reimplement the verifier or shell out per share; the Rust pool calls it in process at 0.44 ms per
|
|
share (measured, section 5). JSON lines are three serde derives. So: Rust, `pool/`, its own Cargo workspace reading
|
|
the fork by path. The miner side is a module in the fork (`igneum/miner/src/pool.rs`), because the identity, voter,
|
|
template and worker code it reuses lives there.
|
|
|
|
## 2. What v0 does
|
|
|
|
| Area | v0 | Spec section |
|
|
|---|---|---|
|
|
| Transport | newline JSON over plain TCP on 4463 (devnet), 4462, 4461; one object per line, `"t"` names the type, unknown fields ignored, 4 MiB line cap | 9.3, 9.5 |
|
|
| Session | `hello` / `welcome` (version, chain id, pool address, modes, `vote_mode: member`, `share_scheme` with fee, window and min payout), `authorize` with the member's BLS pubkey and proof of possession (verified with `verify_pop`), payout address and worker name / `authorized` | 9.3, 9.5 |
|
|
| Templates | mode A, one template per member per tip (NewBlockTemplate subscription, 1 s refresh, on demand): the member's `vote_key_hash` in the header, its key reveal and the pool's `IGNA` payout address in the coinbase extra data; sent as the node's `RpcRawBlock` JSON so a member with a node can submit it there | 9.4, 9.4.1 items 1 and 2, 9.6 item 2 |
|
|
| Seeds | `seeds` on connection and on every change, from the template's `pow_epoch` (current and next epoch seed, boundary DAA, day, the schedule; since 6 October 2026 also `program_class`, `next_program_class`, `era_seed`, `era_index`, `genesis_day_index`, `genesis_dataset_log2`, `program_class_v3_activation_daa`, so a member without a node builds the same program and day cache as the pool's node) | 9.9 |
|
|
| Jobs | `job` with prehash, `target64`, `share_target64`, a random 2^32 nonce space, the seed pair, `program_class` and `era_seed` (6 October 2026: the member refuses a job without a class, code `seeds`, and never hashes a guessed one), `clean` on a tip change | 9.5 |
|
|
| Shares | `share` = a lane hash at or below `share_target64`; the pool evaluates the lane on the CPU and answers `share_result` with `ok`, `stale` (superseded job past the 2,000 ms grace), `duplicate`, `above_target`, `wrong_hash`, `unknown_job`; a share at or below `target64` is a block, submitted to every node | 9.8 items 1, 5, 7 |
|
|
| Vardiff | per member, one share per 10 s, shift `s` with `share_target = target64 << s` never saturated; first correction sized from the measured rate (up to 8 steps), then one step per 30 s at most; idle members eased one step per 3 intervals | 9.8 items 2 and 4 (one deviation: the sized first step, below) |
|
|
| Weight | `2^-s` of a block per share | 9.8 item 3 |
|
|
| Payment | PPLNS over a window of N blocks of weight (default 2); the split is snapshotted when a block is found and credited when it is confirmed blue (a chain block or in a chain block's mergeset blues, the set the execution layer pays); orphans pay nobody; fee (default 1%) off the top | 9.8 item 8 |
|
|
| Payouts | EIP-1559 transfers from the pool's coinbase address through the node's eth_ JSON-RPC, one per payee at or above the minimum, at most 16 per round, receipts checked, a failed receipt re-credits; `--dry-run` records and sends nothing | design 1.1 (execution layer rewards) |
|
|
| The 20% | untouched: the execution layer credits the proving escrow by rule from consensus data (`executor.rs`); the pool's templates name only its own address and receive the 80% | 2.5, 5.3 |
|
|
| Votes | the member votes through its own node with its own key, as in solo mode; the pool holds no key and names each member's key in its header | 9.6, 9.7 items 1 and 2 |
|
|
| Member checks | own key in the header, the pool's address and own key reveal in the coinbase (refused with `vote_key`, `payout`, `reveal`); the pool's epoch seed, program class and era seed against the member's node (`seeds`; the class and era joined the check on 6 October 2026); a custodial pool (`vote_mode` not `member`) refused by default | 9.4.1 items 1 and 2, 9.9 item 2, 9.6 item 5 |
|
|
| Found blocks | the member submits to its own node and sends `solution`; the pool submits to every node | 9.2 |
|
|
| API | `/api/stats`, `/api/blocks`, `/api/miners/<address>`, `/api/payments`, `/api/pool-stats` (Hive-style flat object); field contracts in `pool/src/api.rs`, fixtures from the measured run | explorer.md section 6 |
|
|
| Page | `pool/web/index.html` under the site's tokens: tiles, connect card with the exact miner flags, address lookup, blocks, payments | |
|
|
| Persistence | `state.json` snapshot every 15 s and at shutdown (balances, blocks, payments, PPLNS window, lifetime counters) | |
|
|
| Miner | `igneum-miner mine <grpc url|none> <threads> <secs> <label> --pool <host:port> --evm-address <0x..> --worker-name <s> [--proving] [--worker <exe>]`: CPU threads or a GPU worker fed the share target; the CPU re-check of every GPU `found` before it is sent; STATUS lines with the Hive tokens (`accepted=`, `rejected=`, `now=<MH/s>`) | 9.8 item 6 |
|
|
| HiveOS | Flight Sheet pool URL `pool://host:4463`, `NODE=local` or `NODE=grpc://...` for a verifier node, `h-run.sh` passes `--pool` and `--worker-name` | packaging/hive |
|
|
| Dev fee | none in pool mode: the pool issues the templates, so the 1-in-100 template mechanism does not exist; the pool's fee is the only fee and is in `welcome`, on the page and in the Hive README (consequence C6, 5 October 2026) | |
|
|
| Proving | the member's `stats` line carries `proving: true` when it also proves (`--proving`; the app sets it when its prover runs); the pool shows it per worker and the page says a proving worker gives about 4% of its rate to the prover (bench-log "proving v1") and that proving income never passes through the pool (C6) | 9.8 item 8 |
|
|
|
|
## 3. What v0 does not
|
|
|
|
| Gap | Why | What closes it |
|
|
|---|---|---|
|
|
| TLS on the member port | spec 9.3 requires TLS 1.3 because a hijacked line could feed a member a checkpoint hash to sign. v0 relays no votes and the member signs nothing it got from the pool (votes go through its own node), so the hijack has nothing to feed; the gap is confidentiality of shares and the `binding` field. Plain TCP tonight; `rustls` is in the fork's dependency tree | a `--tls-cert/--tls-key` pair and the TLS exporter binding in `authorize` |
|
|
| Mode B (commitment) and mode C (declared templates) | the spec requires A and C of a conforming pool; C needs the member to build its own template with the pool's tag, and a node RPC that validates a template without submitting it (O-9.4) | O-9.4 first, then `declare_template` / `template_ack` / `template_refused` |
|
|
| Vote relay and carriage by the pool | 9.7 items 3 to 6; v0 members vote through their own node (road three of the three roads), so a member without a node does not vote | `vote` / `vote_ack` / `votes_carried`, aggregation per index (O-3.12), the carriage ratio on the page |
|
|
| Stratum compatibility | none, and the spec is not stratum-like: Stratum V1 is JSON-RPC with `mining.subscribe`, `mining.notify`, `mining.submit` over plain TCP and a job that carries a coinbase split and merkle branches; Igneum's protocol carries a full block, a 64-bit lane target and a BLS key, and its share is a lane of a 32-nonce group. A Stratum V1 bridge would have to hide the vote key, which is the thing 9.6 keeps with the member, so none is planned. Mining operating systems add custom miners by command line (Hive's custom-miner contract, `packaging/hive`), which is how this ships | |
|
|
| Sample verification at high shifts | spec 9.8 item 5 allows sampling above `sample_shift` 8; v0 verifies every share (2,270 per second per core keeps it cheap at any member count the first pool will see) | a sampling switch when a core is short |
|
|
| One template fetch per member per second | at 1,000 members that is 1,000 `getBlockTemplate` calls a second on the pool's node and 10 MB/s of mode A templates | a template RPC that takes a list of keys, or mode B |
|
|
| A database | `state.json` and an in-memory ledger; a crash between snapshots loses up to 15 s of shares | SQLite or Postgres when the first real pool runs |
|
|
| Custodial mode | `vote_mode: pool` is not offered (allowed by 9.6 item 5, refused by members by default) | not planned |
|
|
| Vardiff first step | the spec moves the shift one step per `set_target`; v0's first correction after 8 shares or one interval moves up to 8 steps from the measured rate, because a 100 MH/s card at the initial 2^20-hash share target sends 100 shares a second and one step per 30 s would take 5 minutes to reach one per 10 s | spec 9.10 row "Vardiff step" to say "after the first correction" |
|
|
| Luck | `luck_24h` is blocks found over block-weights of shares in the day; MiningPoolStats reads luck as expected over actual, and the field says which in the API source line | |
|
|
| App setting | design only (section 7) | |
|
|
|
|
## 4. How it was measured
|
|
|
|
`pool/tools/measure.mjs` under the `run` lock: one `igneumd` 0.3.6 (`vendor/igneum-node-036/target-integration`,
|
|
needs no change for the pool) on ports 30400 to 30403 (gRPC, p2p, JSON, EVM), network id `igneum-devnet-3040`, the
|
|
60x fast-time profile (`infra/fast-time/override-60x.json`) with real proof of work and `genesis_bits 0x1e400000`
|
|
(2^18 expected hashes per block, the CPU devnet value of the bench log), the pool on 30463 (members) and 30480 (API),
|
|
and three CPU miners of different identities, payout addresses and thread counts (4, 2, 2) pointed at the pool with
|
|
the node as their verifier (seed checks, votes). Ten minutes in dry run, then the same pool restarted without
|
|
`--dry-run` and a 0.1 IGN minimum for the real payout, then 100 requests per second against the API for 30 s.
|
|
|
|
Commands (the main checkout's lock script, from the pool worktree):
|
|
|
|
```
|
|
tools/lock/with-lock.sh build nice -n 19 cargo build --release -j 4 # pool/
|
|
tools/lock/with-lock.sh build nice -n 19 cargo build --release -j 4 -p igneum-miner # vendor/igneum-node-pool
|
|
tools/lock/with-lock.sh build nice -n 19 cargo test --release -j 4 # pool/ (unit tests)
|
|
tools/lock/with-lock.sh run node pool/tools/measure.mjs --secs 600 --payout-secs 240 --rps 100
|
|
```
|
|
|
|
## 5. Measured
|
|
|
|
Two runs, both 5 October 2026 on this Mac (M-series, 16 cores) while other agents' builds and test networks ran on
|
|
the same machine: every millisecond below is a number taken under load and says so. Counts, shares, blocks, payments
|
|
and balances are not disturbed by load. Summary JSON: `<scratch>/measure/summary.json` and `measure2/summary.json`.
|
|
|
|
Run 1: the pool was the whole network (three CPU miners, 8 threads in all, no other miner). Run 2: an 8-thread solo
|
|
CPU miner ("carrier") mined beside the pool so the pool held about half the hashrate and its blocks competed. Run 1
|
|
also found a bug: the member sends a `share` and then a `solution` for the same nonce (spec 9.5), and the pool counted
|
|
the solution as a duplicate share (604 "rejected" of 617 accepted, every one a solution); fixed before run 2 (a
|
|
solution whose nonce the share already credited is acknowledged, not counted), run 2 shows 0 rejected.
|
|
|
|
| Number | Run 1 (pool = network) | Run 2 (pool about half) | Source |
|
|
|---|---|---|---|
|
|
| Run length | 600 s dry run, 240 s real payout, 30 s API load | same | `measure.mjs` |
|
|
| Shares accepted / stale / rejected | 617 / 0 / 604 (the solution bug) | 317 / 0 / 0 | `/api/stats` pool.shares |
|
|
| Shares per minute, pool | 61.7 | 31.7 | accepted / 600 s |
|
|
| rig1 (4 threads): shares per minute, hashrate 10 min, final shift | 31.2, 86.9 kH/s, 0 | 15.8, 47.5 kH/s, 0 | `/api/miners/0x11..` |
|
|
| rig2 (2 threads) | 15.4, 40.9 kH/s, 0 | 7.8, 20.4 kH/s, 1 | `/api/miners/0x22..` |
|
|
| rig3 (2 threads) | 15.1, 40.3 kH/s, 0 | 8.1, 21.5 kH/s, 0 | `/api/miners/0x33..` |
|
|
| Vardiff changes over 10 min | 0 | 17 (rig3 to shift 1 at 25 s and back at 55 s; rig2 to 1 at 3 min; all at 0 at the end) | pool log `VARDIFF` lines |
|
|
| Share check cost, ms (mean / p50 / p99 / max) | 2.018 / 2.054 / 2.334 / 5.333 (n 617, under load) | 2.094 / 2.096 / 2.241 / 5.785 (n 317, under load) | `/api/stats` pool.share_check_ms, `Instant` around `hash_bound` |
|
|
| Share check cost in isolation, under the `measure` lock (every build and run slot held), 500 checks | 1.347 mean / 1.313 p50 / 2.074 p99 / 2.638 max ms | same binary | `cargo test --release share_check_cost_ms -- --nocapture` in `pool/`, `SHARE_CHECK_MS` line |
|
|
| Pool hashrate from shares | 168 kH/s | 89 kH/s (miners' own STATUS: 0.088 + 0.043 + 0.043 = 0.174 MH/s in run 1; 0.061 + 0.030 + 0.030 = 0.121 MH/s in run 2, which the carrier's 8 threads depressed) | `/api/stats`, miner STATUS `now=` |
|
|
| Blocks found / confirmed / orphaned | 604 / 604 / 0 | 289 / 288 / 0 (one pending at the end) | `/api/stats` pool.blocks_* |
|
|
| Orphan rate | 0 of 604 | 0 of 289 at 49.1% of the network's 589 blocks (the carrier found 288 in the same window, 444 over its run) | `ORPHAN` lines, `/api/blocks` |
|
|
| Pool's share of network blocks | 100% | 49.1% | pool.blocks_total / network.block_count |
|
|
| First block: found to confirmed | | 3.7 s (found 21:20:24.953, confirmed 21:20:28.678, daa 3, effort 1.00, one payee at fraction 1) | `/api/blocks` first_confirmed_block |
|
|
| Reward per block credited | 2.5351 IGN (80% of the ramp's 3.169 IGN at day 0) | same | `block_subsidy(daa, 1)` x 80% |
|
|
| PPLNS in dry run | 60 payment records computed, nothing sent, balances kept: 792.14 / 365.30 / 360.01 IGN after 600 s | 59 records; 389.16 / 164.54 / 169.85 IGN | `/api/payments` dry_run, `/api/miners` balance_ign |
|
|
| Pool fee kept | 15.33 IGN of 1,532.77 (1.00%) | 7.31 of 733.39 (1.00%) | pool.pool_fee_total_ign |
|
|
| Real payout, 240 s: transfers sent / confirmed / failed | 24 / 18 / 0 (6 sent, receipt not yet read at the end) | 24 / 18 / 0 | `/api/payments` status from `eth_getTransactionReceipt` |
|
|
| Pool EVM balance before / after | 1,532.77 / 180.18 IGN | 733.39 / 111.12 IGN | `eth_getBalance` on the private node |
|
|
| Miners' EVM balances received | 1,026.10 / 491.09 / 473.22 IGN | 481.99 / 208.06 / 252.36 IGN | `eth_getBalance` before and after |
|
|
| API at 100 requests per second for 30 s | 3,001 sent, 3,001 ok, 0 errors, p50 0.78 ms, p90 0.97, p99 1.17, max 5.69 | 3,001 / 3,001 / 0, p50 0.85, p90 1.05, p99 2.76, max 11.98 | `fetch()` from Node 22 on the same machine, four endpoints round-robin |
|
|
| Member checks | 0 jobs refused, 0 CPU mismatches, 16 epoch seed changes followed (60-DAA epochs) by every miner, seed check against its own node passed each time | same | miner logs `POOL SEEDS`, STATUS `refused=` |
|
|
| Votes | every miner voted through its own node as in solo mode (`VOTE` lines in the miner logs); the pool held no key | same | miner logs |
|
|
|
|
What is unusual about this network and why it does not change the reading: at `genesis_bits 0x1e400000` a block is
|
|
2^18 expected hashes, so a share worth 2^20 hashes (the vardiff's initial size) is harder than a block; the shift sat
|
|
at 0 and every share was a block. That is why blocks found equals shares accepted less the few found by two miners on
|
|
the same tip. Vardiff still ran (17 changes in run 2 when the carrier slowed the rigs) and the saturation cap held.
|
|
On a GPU network (devnet difficulty 0x1d100000, about 2^28 hashes a block, and more as cards join) shares sit far
|
|
below blocks and the shift moves into its working range; that run needs a GPU worker and is unverified (section 8).
|
|
|
|
Consequences, by tier (the standing rule of 5 October 2026):
|
|
|
|
| Tier | What the numbers mean | What is done about it |
|
|
|---|---|---|
|
|
| A pool operator on one Hetzner box (cx23 2 vCPU or cpx31 4 vCPU) | 1.35 ms per share check in isolation and 2.1 ms under load on an M-series core (the crate's tight-loop bench is 0.44 ms per warp; the check adds the init words and the engine's shared cache path) is one core for 480 to 740 shares a second, which at one share per 10 s per member is 4,800 to 22,700 members per core; the API at 100 rps costs under 3 ms p99, and WhatToMine polls once a minute. The cost that scales first is the template per member per second (section 3): at 1,000 members that is 1,000 `getBlockTemplate` calls a second on the box's node | `--verify-threads` sizes the check pool; the template RPC that takes a list of keys is the planned fix before a thousand members |
|
|
| A home miner on one card (8, 12, 16, 24 or 32 GB, NVIDIA, AMD or Apple, any OS) | pool mode changes nothing on the card: the same worker, the same 256 MiB cache plus program, the pool's share target in the job's target field. Income: 80% of each block over the PPLNS window less 1%, paid when the balance passes 1 IGN; at 2.5 IGN a block (ramp day 0) a miner holding 1% of a pool that finds a block a minute is paid about every 40 minutes, and a miner at 0.01% of a large pool every 3 days. A miner that also proves sends about 4% fewer shares and the page says so | the `--proving` flag and the Proving column; `--min-payout` is the operator's lever for small miners |
|
|
| A rig (several cards) | one `igneum-miner --pool` per card, one vote key per rig (`IDENTITIES` is ignored in pool mode), the worker name labels each card on the page | the Hive hooks' `pool://` mode |
|
|
| A pool user without a node of their own | hashes and is paid, does not vote and does not check the pool's seeds (spec 9.7 item 2); their finality weight still accrues to their own key through the blocks the pool finds under it | `NODE=local` in the Flight Sheet runs the bundled node as the verifier |
|
|
| The network | pool concentration does not become vote concentration: every block carried the finding member's key, 3 keys under one payout address in both runs (F10, O-9.1) | the explorer's keys-per-payout-address count is the public check |
|
|
|
|
Harness note: run 1's node "answered" nine minutes after it started because the harness waited for `igneum-miner watch 1`
|
|
with an 8 s timeout, and `watch 1` takes one sample, sleeps 10 s and then prints: every attempt was killed and the loop ran
|
|
out. Fixed in `measure.mjs` (15 s timeout). The same shape elsewhere: `packaging/hive/h-run.sh` runs `watch 1` with no
|
|
timeout (fine); no other script in `tools/` wraps `watch` in a timeout under 11 s (grep, 5 October 2026).
|
|
|
|
## 6. Listing checklist: WhatToMine's form against this pool
|
|
|
|
WhatToMine's "add a coin" form (as the project lead read it on 5 October 2026 and `docs/plans/explorer.md` summarises: an
|
|
explorer or pool with an API, the block reward and block time sources, a source for total coins) and what
|
|
MiningPoolStats asks of a pool (hashrate, miners, workers, blocks with heights and times, fee, minimum payout, luck):
|
|
|
|
| They ask for | Where it is | State |
|
|
|---|---|---|
|
|
| Algorithm name | `igneum` (`/api/stats` pool.algorithm, network.name) | ready |
|
|
| Network hashrate and difficulty | `/api/stats` network.hashrate, network.difficulty; also the site's `/api/stats` | ready |
|
|
| Block reward and block time | `/api/stats` network.block_reward_ign, miner_reward_ign (80%), block_time_target_s 1; the site's `/api/stats` block_reward with the halving table | ready |
|
|
| Total and circulating supply | the site's `/api/supply` | ready (explorer branch) |
|
|
| An explorer | the site's `/explorer`, `/block/<hash>`, `/address/<addr>` | ready (explorer branch) |
|
|
| A pool with an API | this pool: `/api/stats`, `/api/blocks`, `/api/miners/<address>`, `/api/payments`, `/api/pool-stats` | ready, not deployed |
|
|
| Pool hashrate, miners, workers | `/api/stats` pool.hashrate (10 min), pool.miners, pool.workers | ready |
|
|
| Blocks found with heights and times | `/api/blocks`: DAA score, blue score, ISO time, status | ready |
|
|
| Fee, minimum payout, scheme | `/api/stats` pool.fee_percent, pool.min_payout_ign, pool.scheme PPLNS | ready |
|
|
| Luck | `/api/stats` pool.luck_24h, pool.effort_current | ready, definition in section 3 |
|
|
| A public pool address and page | `--public-url`, the page at `/` | needs a box and a hostname (pool.igneum.network is held) |
|
|
| Exchange or price | none; nothing here is a token sale | not applicable |
|
|
| Mining software | igneum-miner with `--pool`; HiveOS custom miner package with `pool://` | ready, HiveOS untested on a rig |
|
|
|
|
What is still missing for a listing: a deployed pool with a hostname and TLS on the page, a public testnet for it to
|
|
mine, and the explorer branch merged so `/api/supply` is live. In that order.
|
|
|
|
## 7. App: "Mine to a pool" (design only)
|
|
|
|
Settings, under the rewards address field (`app/igneum-app/ui/index.html`, the `#settings` panel), one new field:
|
|
|
|
```
|
|
<div class="field">
|
|
<div class="k">mine to a pool</div>
|
|
<label class="switch"><input type="checkbox" id="s-pool"><span class="track"></span><span>Mine to a pool instead of solo</span></label>
|
|
<div class="row"><input type="text" class="addr-input mono" id="s-pool-url" placeholder="pool host:4463" spellcheck="false"><button class="btn small" id="s-pool-save">Apply</button></div>
|
|
<p class="note" id="s-pool-note">Your vote key stays on this machine and the pool names it in every block you find. The pool pays your rewards address by its own rule and fee (shown here once connected). The software dev fee is off in pool mode. Your node keeps running as the verifier.</p>
|
|
<div class="box mono" id="s-pool-terms" hidden></div>
|
|
</div>
|
|
```
|
|
|
|
Engine side (`config.rs` `Settings`): `pool_url: String` (empty = solo). `engine.rs` passes `--pool <url>
|
|
--worker-name <display_name>-gpuN [--proving]` to each card's miner instead of `--identities`/`--dev-fee`, keeps the
|
|
node as the first argument, and shows the pool's `welcome` terms (fee, scheme, minimum payout) in `#s-pool-terms`
|
|
from the miner's `POOL WELCOME` line. The dashboard's "found" counters become "shares accepted" and "blocks credited"
|
|
in pool mode. Not implemented tonight.
|
|
|
|
## 8. Unverified
|
|
|
|
- The GPU worker path in pool mode (`--worker`): written against the worker protocol and the solo miner's re-check,
|
|
not run on a card tonight (the measurement used CPU threads). The CUDA and OpenCL workers take the share target in
|
|
the job line's target field unchanged, so nothing on their side changes.
|
|
- HiveOS hooks in pool mode: `h-config.sh` exercised by hand in both modes (pool and solo), `h-run.sh` syntax-checked;
|
|
never run on a Hive rig (the package as a whole is untested there, `packaging/hive/README.md`).
|
|
- Testnet and mainnet ports and chain ids: set from the design, not run.
|
|
- The Hetzner deploy in `pool/README.md`: written from the seed-node scripts, not executed.
|
|
- The exact reward per block the execution layer credits against the pool's expected figure: the pool computes
|
|
`producer_share(block_subsidy(block daa))`; the executor uses the merging chain block's subsidy; equal on the
|
|
private run (section 5), may differ by the ramp slope across a day on a live chain. The pool's EVM balance is the
|
|
truth and the page shows both.
|
|
|
|
## 9. 6 October 2026: the rebase onto 0.3.14 and the program class
|
|
|
|
The fleet agent's load run (18:14Z to 18:24Z, ten GPU members, vardiff working): 0 shares accepted in ten minutes. Every
|
|
member's log: `WORKER MISMATCH nonce=... gpu=... cpu=...: share not sent`. The pool fork's miner re-checked every GPU
|
|
share with its own CPU hasher, and that hasher (`release-0.3.6`'s kaspa-pow) predates program class v3: it hashed a fixed
|
|
class v2 program while the 0.3.13 and 0.3.14 CUDA workers hashed the epoch's class v3 program, so the two never agreed and
|
|
the member refused every share before it left the machine. Everything else in the pool worked.
|
|
|
|
What changed (branch `pool-v0-rebase` on master, fork `pool-v0-rebase` on `release-0.3.14-node` 4c6b129d):
|
|
|
|
| Where | Change | Forced by the rebase? |
|
|
|---|---|---|
|
|
| Fork `igneum/miner/src/pool.rs` | `EpochSeeds` carries `class` and `era` on 0.3.14; the member builds them from the job's `program_class` and `era_seed` (`job_seeds`), refuses a job without a class or with one this fork does not know (`job_refused`, code `seeds`), checks class and era against its own node's template beside the epoch seed (`seeds_agree`, through `crate::template`, which installs the node's schedule, genesis day and class v3 activation), installs the same three from the pool's `seeds` line when it has no node (`install_from_pool_line`), and ends every worker `job` and `prepare` line with the class tokens (`class=v3 era=<hex>`); `prepare` writes a checked pack under `--prepare-packs` as the solo miner does; a worker `error ... program class mismatch` gets a fresh prepare | yes (the struct changed) and the fix itself |
|
|
| Fork `igneum/miner/src/main.rs` | `class_tokens`, `write_pack_checked`, `seeds_from_info` are `pub(crate)` | yes |
|
|
| Pool `node.rs` | seeds from the template with the class and the era; the node's genesis day, dataset size and class v3 activation installed beside the schedule; `seeds` and `job` carry the new fields | yes (the struct changed) |
|
|
| Pool `protocol.rs` | `Seeds` + 7 fields, `Job` + 2 fields (additive: a member ignores unknown fields, so the line still parses on the old build; the old build then hashes class v2 as before and keeps mismatching, so every member moves to this build) | yes |
|
|
| Pool `verify.rs` | the tests build seeds through `EpochSeeds::v2` | yes |
|
|
| Protocol, vardiff, PPLNS, stats API, page | unchanged | |
|
|
| `packaging/hive` | the pool:// mode merged with master's per-card `IDENTITIES=auto` and `OVERRIDE` handling; `selftest.sh` passes, the pool-mode config exercised by hand (NODE=local, OVERRIDE set) | merge only |
|
|
|
|
Tests (every suite on igneum-build-1, `IGNEUM_AGENT=pool`):
|
|
|
|
| Suite | Result | What it pins |
|
|
|---|---|---|
|
|
| `igneum-pow` `tests/recheck.rs` | 2 of 2 | class v3 (`packs-ca2-mixer/mx8-devnet-epoch0`) and class v4 (`packs-ca3-v4/v4-devnet-epoch0`): the pack's program (generator, id) reproduces the pack's 96 vectors (the worker's reference), the chain seam the engine calls (`Epoch::chain_program`, `chain_dataset_day`) builds the same program, and one known nonce (prehash 0x5a..., nonce 0x12345678_00000bb7) hashes equal through both paths for each class; v3 and v4 differ from each other and from the fixed-v2 program of the same seed (the fleet's mismatch); the v3 id 73bcbfe8ccf988f1 and v4 id c120d7963abdcd96 as pinned |
|
|
| fork `igneum-miner` | 21 of 21 | `pool::tests::cpu_recheck_equals_the_worker_reference_for_class_v3`: a job line of the pool's shape through `job_seeds`, then the real `IgneumEngine::epoch_for(seeds).hash_bound` against the pack's reference on the known nonce, the v2 re-check shown to disagree, the worker tokens checked; `a_class_v4_job_is_refused_until_the_fork_knows_class_v4`: this fork's `ProgramClass` has no V4, so a class 4 job is refused rather than hashed on a guess, and the test FAILS the day V4 lands (ca3-v4-node), which is when it becomes the v3 test's shape on the v4 pack; `pool_seeds_are_checked_against_the_verifier_class_and_era` |
|
|
| pool `igneum-pool` | 22 of 22 | unchanged suites on the new seeds |
|
|
|
|
Consequences per tier: a pool member on any card hashes the program its pool's node names, so the fix costs no hash rate
|
|
and no memory on any tier (the day cache size is the node's, 1 GiB on the devnet); a member without a verifier node now
|
|
gets the genesis day and dataset size from the pool's `seeds` line, which it had no source for before (it would have built
|
|
a wrong-size cache and mismatched silently); a pool on a class v4 network needs a fork with `ProgramClass::V4`
|
|
(0.3.15, ca3-v4-node); until then its members refuse class 4 jobs loudly (`JOB ... REFUSED (seeds)`), never hash on a guess.
|
|
|
|
Building the pool crate on igneum-build-1: the pool reads the fork through the `vendor/igneum-node` symlink; lib.sh syncs
|
|
the fork worktree it points at (`vendor/igneum-node-pr`) as a whole repository, and the symlink itself is created once on
|
|
the box by hand (`ln -s igneum-node-pr /srv/builds/<worktree>/vendor/igneum-node`), the one step the scripts do not do.
|
|
|
|
### 9.1 7 October 2026: the daemon refuses to start half-alive
|
|
|
|
The fleet agent's night (6 October, 23:01Z to 23:32Z): the rebased pair never ran as a test. The daemon was started while
|
|
the node still held its member port, then four members (the wind-down's weight rule freed four pods, not ten) sat
|
|
connected with no job while every template fetch timed out; nothing in the daemon's own log said so except one line per
|
|
member. The row from it, now code (pool `269364a`+): the member and API listeners are bound in `main` before anything
|
|
else and a port that cannot be bound ends the start with exit code 2 and `POOL NOT STARTED: cannot bind the members
|
|
listener on <addr>: <os error>`; the node must answer a template with its `pow_epoch` before the pool serves anyone,
|
|
retried for `--node-wait-secs` (default 60, each attempt logged), else exit code 3; a `STATUS` line every 30 s
|
|
(`members= jobs_issued= shares_accepted= shares_rejected= blocks= template_failures=; node ok | NODE NOT ANSWERING |
|
|
NO TEMPLATE YET`). Test: `server::bind_tests` (a held port refused with the address in the message, the freed port binds).
|
|
The 10-member run is still owed: a clean 30-minute window, the daemon bound and its `node ... answers templates` line
|
|
printed before the first member, on the next pods the weight rule frees (the table must read over 75 percent signed first).
|
|
|
|
### 9.2 7 October 2026, 06:00Z to 06:33Z: the re-run (five members), what it settled and what it found
|
|
|
|
Settled: the class question. pm-1 and pm-2 (RTX 3070, grpc url `none`) at +7 min: 36,917 and 34,323 shares accepted, 0
|
|
rejected, 0 WORKER MISMATCH, 0 refused, on the class v3 program of the pool's node's epoch (`5530a50d...`, era = the devnet
|
|
genesis hash), the genesis day (20729) and dataset size (2^28) installed from the daemon's `seeds` line. The 6 October
|
|
failure does not reproduce.
|
|
|
|
Found: the daemon stalls on a real chain. From 06:01Z (the first block found) no job was issued for seven minutes, pm-3
|
|
and pm-4 never got one, every block found on the stale templates (55, then 105, 480 DAA behind the virtual) was an
|
|
orphan, vardiff could not act (a new share target rides with a job) so the two hashing members sent about 90 shares a
|
|
second each and the share check read 9.8 ms on pool-1's cores (1.35 ms on the Mac); after a swap to a 20 s template
|
|
timeout (f808b3f3) with the data dir kept, the stall returned within a minute. The node built templates in 0.6 ms all
|
|
the while (its prewarm line) and served its own solo miner.
|
|
|
|
The cause, from the daemon's code: `confirm_loop` restarted its chain walk from the PRUNING POINT on any failed
|
|
`getVirtualChainFromBlock`, and then called `get_block` for every chain block since (about 120,000 on the devnet) over
|
|
the ONE gRPC connection the templates used (the client is one request stream per connection, 5 s request timeout); one
|
|
timed-out request under four parallel template fetches started the walk, every later request waited behind it and
|
|
timed out, which restarted the walk again. The kept state file (105 pending blocks) restarted it after the swap. The
|
|
private measurement run (section 5) never saw it: a 600-block chain walks in a moment.
|
|
|
|
Fixed (pool commit after bd49c2a9): the walk and the network numbers run on a second gRPC connection (`pool.walker`); a
|
|
failed chain call keeps its cursor (moved to the sink only when the node no longer knows it, never to the pruning
|
|
point; test `node::walk_tests`); a tick walks at most 600 chain blocks and says how many; a start with pending blocks
|
|
walks from the sink and says that older ones resolve by the orphan rule. `--template-timeout-s` stays (default 20).
|
|
|
|
Still open from the night, for the next window: the node log showed a new gRPC connection about every 0.8 s with the
|
|
count steady at 5 (something opens and closes one each time; the daemon's re-subscribe loop is the suspect, the
|
|
subscribe-line count in the daemon log decides it); vardiff's new target should apply to the member's current work
|
|
without waiting for a job (a one-line member change, needs a miner rebuild); the 9.8 ms share check on the pool box's
|
|
cores against 1.35 ms on the Mac (the verifier's day cache is 1 GiB and random reads on a rented box's memory are the
|
|
cost; two verify threads saturate at about 200 shares a second, which vardiff must keep far away); and the 10-member
|
|
load figure itself, not taken.
|
|
|
|
Consequences per tier: a pool operator needs a box whose memory serves the 1 GiB cache at speed (a rented 2 vCPU box
|
|
verifies about 100 shares a second per core at 9.8 ms; at one share per 10 s per member that is 1,000 members per
|
|
core, so the verifier is not the limit once vardiff holds); a member on any card is unaffected by any of this; the
|
|
chain walk now costs the node at most 600 `get_block` calls per 5 s on its own connection.
|
|
|
|
### 9.3 The re-run's close (06:33:33Z) and the member rows from its logs
|
|
|
|
After 2f6c0358 (06:20:34Z to 06:33:33Z, 13 minutes, four members): 207 blocks found, 144 confirmed, 69 orphaned (13
|
|
inherited pending at the swap, 56 on its own jobs: a 27 percent residual at 1 bps), 20,404 shares accepted, 0 rejected,
|
|
0 mismatches, 0 refused, every STATUS `node ok`, the gRPC connection churn gone (ids #3 to #5 once each in a minute; the
|
|
193 connections in 150 s were under f808b3f3's pruning-point walk), re-subscribe lines 2 (one per daemon start: the
|
|
re-subscribe loop was never the churn). Before it, f808b3f3 had worked through the walk by about 06:14Z: 245 blocks,
|
|
69 confirmed, 163 orphaned. First CONFIRMED line 06:24:32Z, 4.80 IGN to the finder. Logs: `~/Desktop/fleet/pool-run-0607/`.
|
|
|
|
The residual orphans are template latency: the daemon's STATUS reads `last template 1 s ago` and the solo miner on the
|
|
same node reads `template_ms=1632`; pool-1's node answers a template request in 1 to 1.6 s although its mining manager
|
|
answers a per-member request from its cache by a coinbase rewrite (`modify_block_template`) and its prewarm builds in
|
|
0.6 ms. At 1 bps a template that arrives 1.6 s old loses about a quarter of the blocks found on it, pool or solo. That
|
|
latency is the node's RPC path, a row for the node lane (measure `get_block_template` round trips on a devnet node under
|
|
a miner and a pool; the suspect is the RPC service's `pow_epoch` derivation per request). 82 "RPC request timeout" lines
|
|
after the 2f6c0358 swap are the gRPC client's own 5 s request timeout on that path; `--template-timeout-s` cannot
|
|
lengthen it.
|
|
|
|
From pm-1's log (1.09 GB): 4,342,515 lines are `worker: error N epoch seed mismatch: this worker holds epoch eea66ce7...`,
|
|
one per re-queued job, for the 40 s the worker compiled the new epoch's pack (NVRTC 40,327 ms) and again after every
|
|
reconnect, because a session respawned the worker (a second process beside the first, the same compile again) and
|
|
the member fed jobs for a pair the worker did not hold yet. Vardiff from the same log: shift 10 to 11 (idle easing
|
|
while the worker compiled, which consumed the sized first correction), then 11 down to 0 one step per 30 s over 5.5
|
|
minutes at about 190 shares a second, the load that put the share check at 10 to 11 ms on pool-1's cores.
|
|
|
|
Fixed in the member (fork commit after b8070476): one worker process per run, jobs held for a pair whose prepare is
|
|
pending and never re-prepared once held (`WorkerMemory`, test `jobs_are_held_while_a_pair_is_being_prepared`), worker
|
|
error lines summarised (one per class, then a count per 1,000), `set_target` applied to the current work at once.
|
|
Fixed in the pool (`vardiff.rs`): the idle easing no longer consumes the sized first correction (test
|
|
`an_idle_easing_does_not_consume_the_sized_first_correction`).
|
|
|
|
Consequences per tier: a member on a 3070-class card loses 40 s of hash at every epoch roll to the NVRTC compile unless
|
|
its pack is prepared ahead (the pool's `seeds` line carries `next_epoch_seed` for that, the member's prepare-ahead is
|
|
the next member row); a member on a reconnect now keeps its worker and its compiled pair; a pool operator's verifier
|
|
sees the sized correction within 10 s of a member's first shares (one share per 10 s per member from then on) instead of
|
|
5 minutes of a 190-share-a-second flood; the 10-member load figure is still owed and now has a clean form to run in.
|
|
|
|
## 10. 7 October 2026: pool-0 finished, TLS, and the open pool (mission item 11)
|
|
|
|
Branch `pool-finish` on `release-0.3.19` 44eee05b with the four daemon commits of `pool-v0-rebase` cherry-picked
|
|
(the node fork pinned for the pool work is 0.3.19, whose kaspa-pow needs the latency ladder's igneum-pow; master
|
|
b92a5fd4 does not carry it, so the branch sits on the release tree); fork branch `pool-finish-node` on
|
|
`release-0.3.19-node` dc141409 with `pool-v0-rebase` merged (the pool-mode miner). Ordered by the project lead on 7 October 2026,
|
|
10:1x UK, built in the order of mission 2.11: pool-0, TLS and the page rows, the share sidechain.
|
|
|
|
### 10.1 Pool-0
|
|
|
|
What 2.11 asks of pool-0 was in v0 (section 2): the member's vote key in every header, the pool with no key and no
|
|
vote, PPLNS, a payout round every 60 s, a 1 percent fee. What was missing was the fee published beside the dev fee
|
|
(reinvent 3.5): `welcome.share_scheme` now carries `software_dev_fee_percent` (0 in pool mode: the pool's fee is the only
|
|
fee), the page's Fees row says both, and `site/miner.html`'s dev-fee card says pool-0 charges the same 1 percent so solo
|
|
and pool cost the same and the choice is about variance alone. The jobs and seeds lines carry the latency ladder's rung
|
|
(`shadow_reps`) beside the class and the era, which 0.3.19's program needs; a member checks it against its own node as it
|
|
checks the seed, the class and the era (a pool that could choose the rung could choose the program).
|
|
|
|
### 10.2 TLS (spec 9.3, O-9.7 closed, Q67)
|
|
|
|
`pool/src/tls.rs`: rustls 0.23 with ring, TLS 1.3 only. `--tls-cert`/`--tls-key` (a chain from a trusted root) or
|
|
`--tls-self-signed` (a P-256 pair made under the data dir on first start, read back afterwards; the pin
|
|
`BLAKE2b("igneum-pool-cert-pin-v1" || DER)` printed at start, shown on the page and in `/api/stats`). The member
|
|
(`igneum/miner/src/pool.rs`, fork): `--pool-tls` (the Mozilla roots, the pool's host as the server name) or `--pool-pin
|
|
<hex>` (a verifier that accepts exactly the pinned certificate). The binding: both sides export 32 bytes with the label
|
|
`EXPORTER-igneum-pool-binding` and the chain id (8 bytes LE) as the context; the member signs `igneum-pool-binding-v1/
|
|
|| chain id LE || exporter` under its vote key with its own tag (`DST_BINDING`, `consensus/core/src/finality.rs`); the
|
|
pool verifies it against the member's key and refuses the `authorize` with `bye` otherwise. Tests: `finality.rs`
|
|
(the signature holds for one exporter and one chain id, is no proof of possession); `tls.rs` (a self-signed pair read
|
|
back with one pin, a pinned handshake, both sides' exporters equal, the binding verified on its own connection and
|
|
refused on a second, a wrong pin never handshakes). Testnet and mainnet refuse to start in the clear without
|
|
`--allow-plain`; the devnet's local daemons stay plain. HiveOS: `pools://` or `POOL_PIN=` (`packaging/hive`).
|
|
|
|
### 10.3 The page rows (polish Q68 to Q73)
|
|
|
|
| Row | Done |
|
|
|---|---|
|
|
| Q68 | the page's node line: "node ok, <version>", "node syncing", "no template", "node unreachable since <time>"; `/api/stats` `pool.node_state` |
|
|
| Q69 | one formatter set in the page: en-GB separators on every count, hash rate to one decimal on a kH to PH ladder, difficulty one spelling, IGN 4 decimals on tiles and 6 in tables; luck in MiningPoolStats' convention (expected over actual, over 100 percent is lucky), stated in the API source line |
|
|
| Q70 | the hashrate samples and the check costs are persisted in `state.json` (one snapshot interval, 15 s, is the loss window; the README says so); a restart keeps the rate tiles and the luck |
|
|
| Q71 | the payments of a looked-up address under its workers |
|
|
| Q72 | hourly buckets per address kept 7 days (`history` in `/api/miners/<address>`, a sparkline on the page); `--alert-webhook` POSTed once per address silent for 10 minutes; `/metrics` Prometheus text; `/health` 200 only when the node is synced and answered a template in the last 30 s |
|
|
| Q73 | the network's finality state from `getFinalityCheckpoints` (active, paused, window filling, unknown) with the latest locked index and its age, on the page and in `/api/stats`, beside the sentence "payouts follow blue confirmation, not finality" (`network.payout_rule`) |
|
|
|
|
### 10.4 The open pool: the share sidechain with no operator
|
|
|
|
The design is spec 09 section 9.12 (written with the code). In one paragraph: the member runs its own node and
|
|
`igneum-pool --open` beside it; the daemon holds no key, builds the member's templates from the member's node with the
|
|
member's key in the header and the member's own address in the coinbase, stamps the share chain's parent (`IGNS`) and
|
|
the window's split (`IGNP`) into every coinbase, re-stamps without a node call when the chain tip moves
|
|
(`restamp_extra_data`: the coinbase payload rewritten and the merkle root re-derived as the body check derives it), and
|
|
gossips shares with the other members' daemons. A share is a template at the chain's target; a block is a share at the
|
|
block target; the executor pays a blue block's producer share by its split from `pool_split_activation_daa` on
|
|
(`evm::split_producer`: integer parts by weight, the dust to the finder), so every node computes the credits from the
|
|
block alone, as it does the 20 percent. The chain: one parent per share, heaviest work wins, a fork's loser is stale and
|
|
pays nothing, a 10-second target by default held by a 30-share retarget (the DAA's shape: the window's mean target times
|
|
actual over expected span, clamped x4 per share and ten doublings above the first target; the first version compounded
|
|
the last target by the window's ratio at every share and ran to the saturation cap in thirty shares, every hash a share:
|
|
the first smoke run), a first target that is a chain constant (the network's genesis block target eight times easier,
|
|
never a node's current template, which differs between members), a PPLNS window of 2,160 shares including the share
|
|
itself, the dev fee as one split entry (the weights scaled by 2^32 before the fee's division, or a genesis share's few
|
|
dozen hashes of work rounded the dev entry to a third of its share: the unit test).
|
|
|
|
| File | What |
|
|
|---|---|
|
|
| `consensus/core/src/evm.rs` (fork) | `IGNS` and `IGNP` encode and parse, `split_producer`, the switch `install_pool_split_activation` |
|
|
| `consensus/core/src/config/params.rs` (fork) | `pool_split_activation_daa`: never by default, in the digest once set, the fees' pattern; the override test |
|
|
| `igneum/exec/src/executor.rs`, `service.rs` (fork) | `SegmentBlock.split`; the reward loop pays by the split at or above the switch; the test with an odd subsidy (the dust) |
|
|
| `kaspad/src/daemon.rs` (fork) | installs the switch from the params beside the fees |
|
|
| `pool/src/sidechain.rs` | the share, the chain, the target, the window's split, fork choice, `verify-share` |
|
|
| `pool/src/open.rs` | the daemon side: stamping, accepting (structure, the seeds, the PoW), the shares log, orphans waiting for a parent, the API. The seeds check cost two gate runs on the box: the first keyed the node's epochs by `DAA / epoch_blocks` (not how the node numbers them: every share past epoch 1 refused), the second required the daemon's own node's seed exactly, and four nodes on the 60x profile named three different seed blocks for epoch 1 with no node refusing any block (a node checks a block's PoW under the seed of the block's own ancestry); the rule now is the node's seed, or a block the node holds at the epoch's seed depth with the epoch's class, era and rung |
|
|
| `pool/src/p2p.rs` | the gossip: hello, share, get_shares, the relay, the sync when a peer is ahead |
|
|
| `pool/tools/open-gate.mjs` | the gate |
|
|
|
|
What the node fork needs merged (for the shipper, 0.3.20): the three commits of `pool-v0-rebase` (the pool-mode miner,
|
|
already on `pool-finish-node`), plus `pool-finish-node`'s own: the binding in `finality.rs`, the split codec and switch in
|
|
`evm.rs`, the params field (the digest moves only once the switch is set, so the live devnet's digest does not move), the
|
|
executor's split, the miner's TLS and rung. Nothing activates on the live devnet: `pool_split_activation_daa` stays never
|
|
until a cut sets it through the P2 mechanism or the override, as the fee switch was.
|
|
|
|
### 10.5 The gate
|
|
|
|
Run: `node pool/tools/open-gate.mjs` on igneum-build-1 from `/srv/builds/igneum-wt-pool-finish` with the box-built
|
|
binaries (the standing rule of 7 October 2026 afternoon: nothing builds or runs on the Mac; the first attempt ran on
|
|
the Mac at load 113 and the Mac crashed under it, section 10.4's retarget note): 4 nodes on the 60x fast-time profile
|
|
with real proof of work at `genesis_bits 0x1e400000`, the genesis dataset at 2^24 words (64 MiB per process against the
|
|
devnet's 1 GiB, so 100 CPU members, 10 daemons and 4 nodes fit 64 GB), `pool_split_activation_daa 0`; 10 open daemons
|
|
(one per node in turn, peered in a ring with two chords, the last one withholding its members' shares); the nodes
|
|
started one after another with `--addpeer` and their peer counts read by `getConnectedPeerInfo` before anything else
|
|
starts (the first box run started four nodes at once with `--connect`, nodes 2 and 3 never peered, and the one DAG was
|
|
three partitions with three epoch seeds, which the share chain's seeds check then reported as "not the node's": the
|
|
harness verifies the chain-side fact now, the standing rule); 100 CPU members
|
|
(one key and one address each, one thread each, niced); the chain at 0.1 s per share (at 1 block a second a 10-second
|
|
chain is sub-block work only for a pool under a tenth of the network, spec 9.12 item 8), window 2,160.
|
|
|
|
GATE_RESULTS_PLACEHOLDER
|
|
|
|
### 10.6 Open after this round
|
|
|
|
| Item | Why it is open | What closes it |
|
|
|---|---|---|
|
|
| Uncles | a fork's loser is stale; on the public internet at 100 ms between members a 10-share-a-second chain would lose several percent of shares to forks | P2Pool's uncle rule (a share may name up to N recent stale shares; they are paid at a discount), after the stale rate of a public chain is measured |
|
|
| Share bodies | a share carries the full template; on a chain with full blocks that is the block's size per share | the transaction ids plus the coinbase, the merkle root rebuilt from them (mode B's shape) |
|
|
| A late joiner | a share of an epoch the member's node no longer reports cannot be checked, so the sync is bounded to those epochs (O-9.11) | a node RPC for past epochs' seeds, or the seeds in the share chain's headers |
|
|
| The chain's rate | the public chain's seconds per share against its hashrate (O-9.10) | one chain per decade of hashrate (P2Pool's main, mini, nano), or uncles |
|
|
| The Hive package, the app | neither starts an open daemon yet; the app's "mine to a pool" setting (section 7) is still design | the app spawns `igneum-pool --open` beside its node when the setting says open |
|
|
| Vote relay and mode C | unchanged from section 3 | unchanged |
|
|
|
|
### 10.7 Consequences by tier
|
|
|
|
| Tier | Pool-0 | The open pool |
|
|
|---|---|---|
|
|
| A home miner on one card (8, 12, 16, 24, 32 GB; NVIDIA, AMD, Apple; Windows, Linux, macOS) | nothing changes on the card; the member port is TLS and the miner's `--pool-pin` or `--pool-tls` is one flag; the 1 percent is the same money as solo's dev fee | runs its own node (the app already does) and one more process, the open daemon, whose memory is the day cache (1 GiB on the devnet, as the miner's); the 1 percent is the split entry; the first payout follows the first block found on the chain after its first share, seconds at devnet scale; at a public chain's rate its share interval is the chain's rate times the members' count, the row O-9.10 is about |
|
|
| A rig | one daemon per rig, every card's miner on it; one key per operator as before | the same |
|
|
| A pool user without a node | pool-0 as before (hashes, does not vote) | cannot join the open pool: the daemon builds templates from a node; a user without one takes pool-0 |
|
|
| The pool operator (pool-0) | TLS on the member port, `/metrics`, `/health`, the alert webhook, the persisted ledger | none exists |
|
|
| The network | pool concentration is not vote concentration, as before; an open pool's blocks carry as many keys as it has members and pay as many addresses as the window holds, so the coinbase grows by 48 bytes per payee (at most 256) | the executor's split is the one new consensus-visible rule, behind a switch that is never on the devnet |
|