mining: clarify SubmitBlock result handling

Make the submitBlock return value explicit and check that it stays
consistent with the BIP22 reason string, so future changes do not return
success with a reason or failure without one.

Report "inconclusive" when no specific block rejection reason is
available. This covers blocks accepted without being connected, and
processing failures where ProcessNewBlock returns false without an
invalid BlockChecked result, for example when ActivateBestChain fails
after BlockChecked reported a valid block.

Also document why no validation-interface queue drain is needed before
unregistering: BlockChecked is emitted synchronously by ProcessNewBlock,
unlike most validation signals.
This commit is contained in:
w0xlt
2026-05-28 13:06:39 -07:00
parent 10dfdd4b9f
commit 83f3bc002d
3 changed files with 46 additions and 9 deletions

View File

@@ -111,10 +111,15 @@ class IPCMiningTest(BitcoinTestFramework):
coinbase_tx.nLockTime = coinbase_res.lockTime
return coinbase_tx
async def build_candidate_block(self, template, ctx):
"""Build a complete block from a remote BlockTemplate."""
async def build_candidate_block(self, template, ctx, extra_nonce=b""):
"""Build a complete block from a remote BlockTemplate.
The returned block replaces the dummy coinbase from CreateNewBlock()
with one constructed from getCoinbaseTx().
"""
block = await mining_get_block(template, ctx)
coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet)
coinbase = await self.build_coinbase_test(
template, ctx, self.miniwallet, extra_nonce=extra_nonce)
# Reduce payout for balance comparison simplicity.
coinbase.vout[0].nValue = COIN
block.vtx[0] = coinbase
@@ -623,6 +628,31 @@ class IPCMiningTest(BitcoinTestFramework):
assert_equal(submitted, True)
self.sync_all()
self.log.debug("submitBlock should report inconclusive for a valid stale block")
async with AsyncExitStack() as stack:
active_template = await mining_create_block_template(
mining2, stack, ctx2, self.default_block_create_options)
submit_block_template = await mining_create_block_template(
mining2, stack, ctx2, self.default_block_create_options)
assert active_template is not None
assert submit_block_template is not None
active_block = await self.build_candidate_block(
active_template, ctx2, extra_nonce=b"\x01")
submit_block = await self.build_candidate_block(
submit_block_template, ctx2, extra_nonce=b"\x02")
active_block.solve()
submit_block.solve()
# Both templates share a parent. The first block becomes active,
# so the remaining valid block is accepted as stale.
await self.assert_submit_block(
mining2, ctx2, active_block, result=True)
await self.assert_submit_block(
mining2, ctx2, submit_block, result=False, reason="inconclusive")
self.sync_all()
self.log.debug("Submit the same invalid block twice")
async with destroying((await mining2.createNewBlock(ctx2, self.default_block_create_options)).result, ctx2) as template2:
invalid_block = await self.build_candidate_block(template2, ctx2)