Add checkblock RPC and checkBlock() to Mining interface
Introduce checkBlock()
on the Mining interface for IPC, as well as a new checkblock
RPC that's easier to use than getblocktemplate
in proposal mode, and supports weak blocks.
Rationale
Verifying block templates (no PoW)
Stratum v2 allows miners to generate their own block template. Pools may wish (or need) to verify these templates. This typically involves comparing mempools, asking miners to providing missing transactions and then reconstructing the proposed block.1 This is not sufficient to ensure a proposed block is actually valid. In some schemes miners could take advantage of incomplete validation2.
The Stratum Reference Implementation (SRI), currently the only Stratum v2 implementation, collects all missing mempool transactions, but does not yet fully verify the block.3 There's currently no way for it to do so, short of re-implementing Bitcoin Core validation code.
(although SRI could use this PR, the Template Provider role doesn't need it, so this is not part of #31098)
Verifying weak blocks (reduced PoW)
Stratum v2 decentralises block template generation, but still hinge on a centralised entity to approve these templates.
There used to be a decentralised mining protocol P2Pool4 which relied on (what we would today call) weak blocks. In such a system all participants perform verification and there is no privileged pool operator (that can reject a template).
P2Pool shares form a "sharechain" with each share referencing the previous share's hash. Each share contains a standard Bitcoin block header, some P2Pool-specific data that is used to compute the generation transaction (total subsidy, payout script of this share, a nonce, the previous share's hash, and the current target for shares), and a Merkle branch linking that generation transaction to the block header's Merkle hash.
IIUC only the header and coinbase of these shares / weak blocks were distributed amongst all pool participants and verified. This was sufficient at the time because fees were negligible, so payouts could simply be distributed based on work.
However, if P2Pool were to be resurrected today5, it would need to account for fees in a similar manner as PPLNS2 above. In that case the share chain should include the full weak blocks6. The client software would need a way to verify those weak blocks.
A similar argument applies to Braidpool.
checkblock
and its IPC counterpart checkBlock()
Enter Given a serialised block, it performs the same checks as submitblock
, but without updating the block index, chainstate, etc. The proof-of-work check can be bypassed entirely, for the first use of verifying block templates. Alternatively a custom target
can be provided for the use case of weak blocks.
When called with check_pow=false
the checkblock
RPC is (almost) the same as getblocktemplate
in proposal
mode, and indeed they both call checkBlock()
on the Mining interface.
Implementation details:
TestBlockValidity()
is moved to the ChainstateManager
and refactored to no longer return BlockValidationState
. This avoids the new checkBlock()
method needing to pass BlockValidationState
over IPC.
The refactor commit contains additional changes explained in its commit message.
Another commit drops support for the unused BlockValidationState
encoding.
This PR contains a simple unit test and a more elaborate functional test.
Potential followups:
- if the block contains "better" transactions, (optionally) add them to our mempool 5
- keep track of non-standard transactions7
- allow rollback (one or two blocks) for pools to verify stale work / uncles
-
https://github.com/stratum-mining/sv2-spec/blob/main/06-Job-Declaration-Protocol.md ↩
-
https://delvingbitcoin.org/t/pplns-with-job-declaration/1099/45?u=sjors ↩ ↩2
-
https://github.com/stratum-mining/stratum/blob/v1.1.0/roles/jd-server/src/lib/job_declarator/message_handler.rs#L196 ↩
-
this improves compact block relay once the real block comes in ↩ ↩2
-
The share chain client software could use compact block encoding to reduce its orphan / uncle rate. To make that easier, we could provide a
reconstructblock
RPC (reconstructBlock
IPC) that takes the header and short ids, fills in aCBlock
from the mempool (and jail) and returns a list of missing items. The share chain client then goes and fetches missing items and calls the method again. It then passes the complete block tocheckblock
. This avoids the need for others to fully implement compact block relay. Note that even if we implemented p2p weak block relay, it would not be useful for share chain clients, because they need to relay additional pool-specific data. ↩ -
https://delvingbitcoin.org/t/second-look-at-weak-blocks/805 ↩