- POOL_INTEGRATION.md: exact job/submit format, pool-side validation & scoring on the SHA256D pow-hash, and a zero-downtime dual-accept rollout plan (deploy dual-accept pool before announcing drg-xmrig). - Restore doc/build/ docs removed during the initial copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
105 lines
5.1 KiB
Markdown
105 lines
5.1 KiB
Markdown
# Pool integration & migration guide (drg-xmrig)
|
|
|
|
`drg-xmrig` mines DragonX with the **pow-hash share model**: it filters every hash on
|
|
`SHA256D(header + RandomX(header))` (the block-bearing hash), not on the RandomX hash.
|
|
A pool must score shares on that same pow-hash. This document is the spec for updating
|
|
the DragonX stratum pool to support `drg-xmrig`, plus a zero-downtime rollout plan.
|
|
|
|
See `PROTOCOL.md` for the exact byte-level wire format. This file is the pool's view.
|
|
|
|
---
|
|
|
|
## 1. Job (pool → miner)
|
|
|
|
Send a standard XMRig `job` with:
|
|
|
|
| field | value |
|
|
|-------------|-----------------------------------------------------------------------------|
|
|
| `job_id` | unique per job |
|
|
| `algo` | `rx/dragonx` (aliases also accepted: `rx/hush`, `dragonx`) |
|
|
| `blob` | **140-byte** header hex (280 chars): `header[0:108] + nonce[108:140]` |
|
|
| `target` | 4- or 8-byte compact target, standard XMRig convention (`diff = 0xFFFF…/target`) |
|
|
| `seed_hash` | RandomX seed hash for the current key block |
|
|
| `height` | block height |
|
|
|
|
**Blob layout (140 bytes):** `version(4) + prevHash(32) + merkleRoot(32) + blockCommitments(32) + nTime(4) + nBits(4)` = 108, then a **32-byte nonce field** at `[108:140]`.
|
|
|
|
**Extranonce (REQUIRED for >1 miner):** the miner only varies the uint32 at `[108:112]`
|
|
and copies bytes `[112:140]` **verbatim** into the submitted nonce. So the pool MUST write
|
|
a **unique per-connection value into nonce bytes `[112:140]`** (28 bytes) of every blob.
|
|
Without it, two miners scan the same 2^32 space and waste work / collide. (This is the
|
|
existing extranonce mechanism — it just lives in the upper 28 nonce bytes.)
|
|
|
|
---
|
|
|
|
## 2. Submit (miner → pool)
|
|
|
|
```json
|
|
{ "id": "<rpcId>", "job_id": "<job_id>",
|
|
"nonce": "<64 hex = full 32-byte nonce field [108:140]>",
|
|
"result": "<64 hex = 32-byte RandomX hash>",
|
|
"algo": "rx/dragonx" } // present only if pool advertised the "algo" login extension
|
|
```
|
|
|
|
`nonce` is the full 32 bytes = `[4-byte counter | 28-byte extranonce you assigned]`.
|
|
`result` is the raw RandomX hash (the PoW *solution*), NOT the double-SHA.
|
|
|
|
---
|
|
|
|
## 3. Pool-side validation & scoring
|
|
|
|
For each submit:
|
|
|
|
1. Reconstruct the 140-byte header: `blob[0:108]` (from the job you sent) + `nonce[0:32]`
|
|
(from the submit, placed at `[108:140]`).
|
|
2. **Authenticity:** verify `RandomX(header140, seed) == result`. (Reject otherwise — this
|
|
stops a fake `result` + cheap SHA256D from farming credit.)
|
|
3. **Pow-hash:** `pow = SHA256D( header140 + 0x20 + result )` (0x20 = compact-size 32).
|
|
4. **Block?** `pow <= network_target` → submit block to daemon.
|
|
5. **Valid share?** compare the **last 8 bytes** of `pow` (little-endian uint64 at `pow[24:32]`)
|
|
against the worker `target` — i.e. `leu64(pow[24:32]) < target`. Credit `shareDiff = 0xFFFF…/target`.
|
|
|
|
Note: difficulty/target math is the **same** as today's RandomX-share path. The ONLY change
|
|
is *which hash* you score: the double-SHA `pow` instead of the RandomX `result`. Block
|
|
detection (step 4) is what today's pool already does on every submitted share.
|
|
|
|
---
|
|
|
|
## 4. Zero-downtime migration (dual-accept)
|
|
|
|
The current fleet runs `xmrig-hac` (`XMRig-HAC/6.25.1-hac`), which submits **RandomX-filtered**
|
|
shares. If the pool switches to scoring `pow` only, those miners' shares get rejected. So:
|
|
|
|
**Run both metrics during the transition.** For each submit, after the RandomX authenticity
|
|
check, accept the share if it clears the worker target on **either**:
|
|
- the RandomX hash `result` (legacy xmrig-hac), **or**
|
|
- the pow-hash `pow` (drg-xmrig).
|
|
|
|
Both represent ~`D` RandomX attempts, so credit `shareDiff` identically — hashrate accounting
|
|
stays consistent across both miner types. Block detection (step 4) runs for every accepted
|
|
share regardless of metric, so legacy miners keep finding blocks too.
|
|
|
|
Distinguish miner type (for stats/telemetry only) by login user-agent:
|
|
`DRG-XMRig/*` vs `XMRig-HAC/*`.
|
|
|
|
### Rollout order (important)
|
|
1. **Deploy the dual-accept pool first.** It is backward-compatible — the existing fleet is
|
|
unaffected (their RandomX shares still credit).
|
|
2. **Then announce / publish drg-xmrig.** Early adopters' pow-hash shares now credit
|
|
immediately, and their block candidates stop being dropped.
|
|
3. Once the fleet has migrated, optionally retire the RandomX-share acceptance path
|
|
(pow-only), which also lets you simplify/raise difficulty cleanly.
|
|
|
|
Do **not** announce drg-xmrig before the dual-accept pool is live, or early adopters will
|
|
have shares rejected.
|
|
|
|
---
|
|
|
|
## 5. What this fixes
|
|
|
|
- **Candidate gap:** blocks are a subset of shares, so the pool receives 100% of candidates
|
|
(the ~50% under-submission of xmrig-hac 6.25.x pool mode is gone).
|
|
- **Hashrate accuracy:** shares are on the block metric, so pool hashrate is directly
|
|
comparable to the network hashrate.
|
|
- **Nonce overlap:** handled by the per-connection extranonce in nonce bytes `[112:140]`.
|