[RPC][Mining] Deliver sha256d work over rpc - #1084
Conversation
The functional test harness was left half renamed from Bitcoin. TestNode takes veild and veil_cli, but add_nodes still handed it bitcoind and bitcoin_cli, and the default binary paths still pointed at src/bitcoind and src/bitcoin-cli, so every test that brings up a node died before it started. It also never answered Veil's wallet seed prompt. Pass veild and veil_cli through, default the binaries to src/veild and src/veil-cli, start nodes with -generateseed=1, and drop the expected seed backup warning from the stderr check. With this, add_nodes based functional tests run again.
ProgPow and RandomX both hand an external miner ready made work through getblocktemplate (pprpcheader, rxrpcheader) and take a bare nonce back through pprpcsb and rxrpcsb. Sha256d never got either, so a miner had to rebuild the witness merkle root and the accumulator hash itself and then submit a fully serialized block, which is why the only sha256d miner in practice is the one inside the wallet. Give sha256d the same treatment: getblocktemplate now returns sharpcheader, the 80 byte hashing header with the nonce zeroed, and sharpcsb takes the job back with the 64 bit nonce that solved it. Sha256d templates advertise the wider noncerange, and the algo dispatch is now exhaustive, with an explicit randomx case and a defensive error on any future unhandled value. Consensus is untouched; this only exposes work the node already builds.
seanPhill
left a comment
There was a problem hiding this comment.
ACK!*
PR Review: #1084 – [RPC][Mining] Deliver sha256d work over rpc
Author: ohcee
Status: Open (awaiting review from seanPhill / Veil-Project)
Commits: 2
Base: master
Summary
This PR closes a long-standing gap: ProgPow and RandomX already expose ready-made work via getblocktemplate + a dedicated submit RPC (pprpcsb / rxrpcsb), but sha256d did not. External sha256d miners previously had to reconstruct the witness merkle root + accumulator hash themselves and submit a full serialized block. That friction meant virtually only the in-wallet miner was usable for sha256d.
The change is purely node-local convenience:
getblocktemplatewith{"algo": "sha256d"}now returnssharpcheader(80-byte header with 64-bit nonce zeroed) + a 64-bitnoncerange.- New RPC
sharpcsb "header" "sha_hash" "nonce"looks up the stored template, inserts the nonce, verifies PoW with the existingCheckProofOfWork(GetSha256DPoWHash(), ...), then callsProcessNewBlockexactly like the other submit paths. - Algo dispatch is made exhaustive (explicit
randomxcase + defensive error on unknown values).
Consensus, validation, and PoW rules are untouched. No fork required.
Commit breakdown
-
d29fea5– test: repair the functional test framework for Veil
Necessary prerequisite.TestNode/add_nodesstill carried Bitcoin binary names and paths; the wallet seed prompt was also unhandled. This was breaking everyadd_nodes-based functional test, not just the new one. Clean, focused fix. -
ad2b4ae– [RPC][Mining] Deliver sha256d work over rpc
The feature itself + new functional test.
Code quality & consistency
The implementation deliberately mirrors the existing ProgPow / RandomX paths:
- Same
std::map<std::string, CBlock>pattern (mapSha256dTemplates). - Same 60-second last-header reuse / cache.
- Same clear-on-new-template logic inside
getblocktemplate_impl. - Same
submitblock_StateCatcher+ProcessNewBlock+ BIP22 result handling. sha_hashis accepted purely for signature parity withpprpcsb/rxrpcsband then ignored (identical to howrx_hashis treated inrxrpcsb). Documented in both help text and code.
Key differences that are correct for sha256d:
- Uses
CSha256dInput+ midstate (proper for the 80-byte header). - 64-bit nonce (
nNonce64+ParseUInt64). - Noncerange
"0000000000000000ffffffffffffffff".
Minor nits (none blocking):
- Help-text examples still use a bare
100000(same as the other two RPCs). Harmless but a hex example would be slightly clearer. - Success path converts BIP22
null→true(consistent with the existingpprpcsb/rxrpcsbimplementations, and the new test assertsTrue). - The three template maps still have no size bound / eviction beyond the full clear on new work. This is pre-existing behaviour; not introduced here.
Testing
New functional test (test/functional/mining_sha256d_rpc.py) is thorough:
- Correct
sharpcheaderlength / zeroed nonce / noncerange. - Grinds a real 64-bit nonce against the target.
- Rejects unsolved nonce (
"Block does not solve the boundary"). - Rejects unknown header.
- Accepts valid work, advances tip, relays to second node, carries the sha256d version bit.
- Resubmit returns
"duplicate". - Explicitly checks that a bogus
sha_hashis still accepted (documents the ignore behaviour).
Author also reports live testnet validation with external miners (OpenCL on M4 + ccminer on RTX 3060 via stratum proxy) producing real blocks (heights ~3087602–3087641).
The framework repair commit is what makes the test (and other functional tests) runnable at all.
Recommendation
Concept ACK + code ACK.
This is a clean, well-scoped, low-risk improvement that brings sha256d external mining to parity with the other two algos. The test coverage is solid, the implementation follows established patterns, and the framework fix is independently valuable.
Happy to see this merged! Nice work.
[RPC][Mining] Deliver sha256d work over rpc
Problem
ProgPow and RandomX each let an external miner pull ready made work from the node and hand a solved nonce back, through getblocktemplate (pprpcheader, rxrpcheader) and the pprpcsb / rxrpcsb submit calls. Sha256d never got either side. A sha256d miner had to rebuild the witness merkle root and the accumulator hash on its own and then submit a fully serialized block. That is enough friction that the only sha256d miner anyone actually runs is the one built into the wallet.
Description
This gives sha256d the same rpc mining path the other two proof of work algos already have. getblocktemplate with algo sha256d now returns sharpcheader, the 80 byte hashing header with the 64 bit nonce zeroed, and a new sharpcsb rpc takes that header back with the nonce that solved it and submits the block. The template algo dispatch is now exhaustive, with an explicit randomx case and a defensive error on any value it does not recognise.
The PR is two commits. The first repairs Veil's functional test framework so tests can run at all, a pre-existing bug described under Unit Testing Results. The second is the sha256d rpc feature itself.
Root Cause
Not a regression. When the rpc mining path was added for ProgPow and RandomX, sha256d was left out. The node already assembles a full sha256d block template internally, the in wallet miner uses it, it was just never exposed over rpc, so nothing outside the wallet could mine sha256d without reimplementing the block assembly.
Why broke?
Sha256d is the odd one out structurally. Its work is an 80 byte header whose last 8 bytes are a 64 bit nonce, and building it needs the witness merkle root and the accumulator hash that the node computes while forming the template. With no rpc to hand the finished header over and take a bare nonce back, an external miner has to redo all of that and serialize a whole block to submit. That friction is why the gap sat there so long.
Solution
Expose the work the node already builds. getblocktemplate returns sharpcheader for algo sha256d, and sharpcsb reconstructs the block from the stored template plus the submitted nonce and pushes it through the normal block acceptance path. Consensus is untouched.
How fix?
No consensus, validation, or proof of work code is touched. The blocks this produces are ordinary sha256d blocks that every other node accepts, and the patched node accepts exactly the same blocks it did before. It is a node local convenience, not a network change, so it needs no fork of any kind.
Unit Testing Results
A new functional test, test/functional/mining_sha256d_rpc.py, covers:
The test cannot run on stock master, because Veil's functional test harness is half renamed from Bitcoin: TestNode takes veild and veil_cli, but add_nodes still passed bitcoind and bitcoin_cli, the default binary paths still pointed at src/bitcoind, and it never answered Veil's wallet seed prompt. That breaks every add_nodes based functional test, not just this one. The first commit in this PR repairs it. With that in place the test passes:
Beyond the unit test, the whole path has been driven end to end on live testnet. An external gpu sha256d miner (OpenCL on an M4 and ccminer on an RTX 3060) mined real testnet blocks through a stratum proxy that pulls sharpcheader from getblocktemplate and submits solved work with sharpcsb. Confirmed testnet sha256d blocks include height 3087602, 3087619, and 3087634 through 3087641.
How to test?
Functional test:
Manual, on regtest or testnet: