From 70de5cc2d205672743379f2e1a94290ee8b4b84b Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 24 Nov 2025 15:34:45 +0100 Subject: [PATCH] ipc mining: pass missing context to BlockTemplate methods (incompatible schema change) Adding a context parameter ensures that these methods are run in their own thread and don't block other calls. They were missing for: - createNewBlock() - checkBlock() The missing parameters were first pointed out by plebhash in https://github.com/bitcoin/bitcoin/issues/33575#issuecomment-3383290115 and adding them should prevent possible performance problems and lockups, especially with #34184 which can make the createNewBlock method block for a long time before returning. It would be straightforward to make this change in a backward compatible way (https://github.com/bitcoin/bitcoin/pull/34184#discussion_r2770232149) but nice to not need to go through the trouble. Warning: This is an intermediate, review-only commit. Binaries built from it should not be distributed or used to connect to other clients or servers. It makes incompatible changes to the `mining.capnp` schema without updating the `Init.makeMining` version, causing binaries to advertise support for a schema they do not actually implement. Mixed versions may therefore exchange garbage requests/responses instead of producing clear errors. The final commit in this series bumps the mining interface number to ensure mismatches are detected. git-bisect-skip: yes Co-authored-by: Ryan Ofsky --- src/ipc/capnp/mining.capnp | 4 ++-- test/functional/interface_ipc_mining.py | 10 +++++----- test/functional/test_framework/ipc_util.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp index 0727fcc0a50..47cd242b81d 100644 --- a/src/ipc/capnp/mining.capnp +++ b/src/ipc/capnp/mining.capnp @@ -22,8 +22,8 @@ interface Mining $Proxy.wrap("interfaces::Mining") { isInitialBlockDownload @1 (context :Proxy.Context) -> (result: Bool); getTip @2 (context :Proxy.Context) -> (result: Common.BlockRef, hasResult: Bool); waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64 = .maxDouble) -> (result: Common.BlockRef); - createNewBlock @4 (options: BlockCreateOptions) -> (result: BlockTemplate); - checkBlock @5 (block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool); + createNewBlock @4 (context :Proxy.Context, options: BlockCreateOptions) -> (result: BlockTemplate); + checkBlock @5 (context :Proxy.Context, block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool); } interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") { diff --git a/test/functional/interface_ipc_mining.py b/test/functional/interface_ipc_mining.py index 0a530827cd1..2221d462879 100755 --- a/test/functional/interface_ipc_mining.py +++ b/test/functional/interface_ipc_mining.py @@ -242,7 +242,7 @@ class IPCMiningTest(BitcoinTestFramework): self.log.debug("Enforce minimum reserved weight for IPC clients too") opts.blockReservedWeight = 0 try: - await mining.createNewBlock(opts) + await mining.createNewBlock(ctx, opts) raise AssertionError("createNewBlock unexpectedly succeeded") except capnp.lib.capnp.KjException as e: if e.type == "DISCONNECTED": @@ -269,7 +269,7 @@ class IPCMiningTest(BitcoinTestFramework): current_block_height = self.nodes[0].getchaintips()[0]["height"] check_opts = self.capnp_modules['mining'].BlockCheckOptions() - async with destroying((await mining.createNewBlock(self.default_block_create_options)).result, ctx) as template: + async with destroying((await mining.createNewBlock(ctx, self.default_block_create_options)).result, ctx) as template: block = await mining_get_block(template, ctx) balance = self.miniwallet.get_balance() coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet) @@ -282,7 +282,7 @@ class IPCMiningTest(BitcoinTestFramework): self.log.debug("Submit a block with a bad version") block.nVersion = 0 block.solve() - check = await mining.checkBlock(block.serialize(), check_opts) + check = await mining.checkBlock(ctx, block.serialize(), check_opts) assert_equal(check.result, False) assert_equal(check.reason, "bad-version(0x00000000)") submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())).result @@ -292,7 +292,7 @@ class IPCMiningTest(BitcoinTestFramework): block.solve() self.log.debug("First call checkBlock()") - block_valid = (await mining.checkBlock(block.serialize(), check_opts)).result + block_valid = (await mining.checkBlock(ctx, block.serialize(), check_opts)).result assert_equal(block_valid, True) # The remote template block will be mutated, capture the original: @@ -324,7 +324,7 @@ class IPCMiningTest(BitcoinTestFramework): self.miniwallet.rescan_utxos() assert_equal(self.miniwallet.get_balance(), balance + 1) self.log.debug("Check block should fail now, since it is a duplicate") - check = await mining.checkBlock(block.serialize(), check_opts) + check = await mining.checkBlock(ctx, block.serialize(), check_opts) assert_equal(check.result, False) assert_equal(check.reason, "inconclusive-not-best-prevblk") diff --git a/test/functional/test_framework/ipc_util.py b/test/functional/test_framework/ipc_util.py index 2ab74abad96..11497463eb3 100644 --- a/test/functional/test_framework/ipc_util.py +++ b/test/functional/test_framework/ipc_util.py @@ -108,7 +108,7 @@ async def make_capnp_init_ctx(self): async def mining_create_block_template(mining, stack, ctx, opts): """Call mining.createNewBlock() and return template, then call template.destroy() when stack exits.""" - response = await mining.createNewBlock(opts) + response = await mining.createNewBlock(ctx, opts) if not response._has("result"): return None return await stack.enter_async_context(destroying(response.result, ctx))