interfaces: add interruptWait method

- This method can be used to cancel a running
  waitNext().

- This commit also adds a test case for interruptWait method
This commit is contained in:
ismaelsadeeq
2025-10-22 12:02:12 +02:00
parent 563747971b
commit dcb56fd4cb
6 changed files with 54 additions and 4 deletions

View File

@@ -72,6 +72,11 @@ public:
* the tip is more than 20 minutes old.
*/
virtual std::unique_ptr<BlockTemplate> waitNext(const node::BlockWaitOptions options = {}) = 0;
/**
* Interrupts the current wait for the next block template.
*/
virtual void interruptWait() = 0;
};
//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)

View File

@@ -33,6 +33,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
submitSolution @9 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool);
waitNext @10 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate);
interruptWait @11() -> ();
}
struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {

View File

@@ -918,15 +918,21 @@ public:
std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override
{
auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options);
auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options, m_interrupt_wait);
if (new_template) return std::make_unique<BlockTemplateImpl>(m_assemble_options, std::move(new_template), m_node);
return nullptr;
}
void interruptWait() override
{
InterruptWait(notifications(), m_interrupt_wait);
}
const BlockAssembler::Options m_assemble_options;
const std::unique_ptr<CBlockTemplate> m_block_template;
bool m_interrupt_wait{false};
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
KernelNotifications& notifications() { return *Assert(m_node.notifications); }
NodeContext& m_node;

View File

@@ -453,12 +453,20 @@ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t
block.hashMerkleRoot = BlockMerkleRoot(block);
}
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait)
{
LOCK(kernel_notifications.m_tip_block_mutex);
interrupt_wait = true;
kernel_notifications.m_tip_block_cv.notify_all();
}
std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainman,
KernelNotifications& kernel_notifications,
CTxMemPool* mempool,
const std::unique_ptr<CBlockTemplate>& block_template,
const BlockWaitOptions& options,
const BlockAssembler::Options& assemble_options)
const BlockAssembler::Options& assemble_options,
bool& interrupt_wait)
{
// Delay calculating the current template fees, just in case a new block
// comes in before the next tick.
@@ -483,8 +491,12 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
// method on BlockTemplate and no template could have been
// generated before a tip exists.
tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock;
return tip_changed || chainman.m_interrupt;
return tip_changed || chainman.m_interrupt || interrupt_wait;
});
if (interrupt_wait) {
interrupt_wait = false;
return nullptr;
}
}
if (chainman.m_interrupt) return nullptr;

View File

@@ -238,6 +238,9 @@ void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& opti
/* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */
void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce);
/* Interrupt the current wait for the next block template. */
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait);
/**
* Return a new block template when fees rise to a certain threshold or after a
* new tip; return nullopt if timeout is reached.
@@ -247,7 +250,8 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
CTxMemPool* mempool,
const std::unique_ptr<CBlockTemplate>& block_template,
const BlockWaitOptions& options,
const BlockAssembler::Options& assemble_options);
const BlockAssembler::Options& assemble_options,
bool& interrupt_wait);
/* Locks cs_main and returns the block hash and block height of the active chain if it exists; otherwise, returns nullopt.*/
std::optional<BlockRef> GetTip(ChainstateManager& chainman);

View File

@@ -182,6 +182,28 @@ class IPCInterfaceTest(BitcoinTestFramework):
template7 = await template6.result.waitNext(ctx, waitoptions)
assert_equal(template7.to_dict(), {})
self.log.debug("interruptWait should abort the current wait")
wait_started = asyncio.Event()
async def wait_for_block():
new_waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
new_waitoptions.timeout = waitoptions.timeout * 60 # 1 minute wait
new_waitoptions.feeThreshold = 1
wait_started.set()
return await template6.result.waitNext(ctx, new_waitoptions)
async def interrupt_wait():
await wait_started.wait() # Wait for confirmation wait started
await asyncio.sleep(0.1) # Minimal buffer
template6.result.interruptWait()
miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])
wait_task = asyncio.create_task(wait_for_block())
interrupt_task = asyncio.create_task(interrupt_wait())
result = await wait_task
await interrupt_task
assert_equal(result.to_dict(), {})
current_block_height = self.nodes[0].getchaintips()[0]["height"]
check_opts = self.capnp_modules['mining'].BlockCheckOptions()
template = await mining.result.createNewBlock(opts)