Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test the IPC (multiprocess) interface."""6import asyncio8from contextlib import ExitStack9from test_framework.test_framework import BitcoinTestFramework10from test_framework.util import assert_equal11from test_framework.ipc_util import (12load_capnp_modules,13make_capnp_init_ctx,14make_mining_ctx,15)17# Test may be skipped and not have capnp installed18try:19import capnp # type: ignore[import] # noqa: F40120except ModuleNotFoundError:21pass24class IPCInterfaceTest(BitcoinTestFramework):26def skip_test_if_missing_module(self):27self.skip_if_no_ipc()28self.skip_if_no_py_capnp()30def set_test_params(self):31self.num_nodes = 133def setup_nodes(self):34self.extra_init = [{"ipcbind": True}]35super().setup_nodes()36# Use this function to also load the capnp modules (we cannot use set_test_params for this,37# as it is being called before knowing whether capnp is available).38self.capnp_modules = load_capnp_modules(self.config)40def run_echo_test(self):41self.log.info("Running echo test")42async def async_routine():43ctx, init = await make_capnp_init_ctx(self)44self.log.debug("Create Echo proxy object")45echo = init.makeEcho(ctx).result46self.log.debug("Test a few invocations of echo")47for s in ["hallo", "", "haha"]:48result_eval = (await echo.echo(ctx, s)).result49assert_equal(s, result_eval)50self.log.debug("Destroy the Echo object")51echo.destroy(ctx)52asyncio.run(capnp.run(async_routine()))54def run_mining_test(self):55self.log.info("Running mining test")56block_hash_size = 3258async def async_routine():59ctx, mining = await make_mining_ctx(self)60self.log.debug("Test simple inspectors")61assert (await mining.isTestChain(ctx)).result62assert not (await mining.isInitialBlockDownload(ctx)).result63blockref = await mining.getTip(ctx)64assert blockref.hasResult65assert_equal(len(blockref.result.hash), block_hash_size)66current_block_height = self.nodes[0].getchaintips()[0]["height"]67assert_equal(blockref.result.height, current_block_height)69asyncio.run(capnp.run(async_routine()))71def run_deprecated_mining_test(self):72self.log.info("Running deprecated mining interface test")73async def async_routine():74node = self.nodes[0]75connection = await capnp.AsyncIoStream.create_unix_connection(node.ipc_socket_path)76init = capnp.TwoPartyClient(connection).bootstrap().cast_as(self.capnp_modules['init'].Init)77self.log.debug("Calling deprecated makeMiningOld2 should raise an error")78try:79await init.makeMiningOld2()80raise AssertionError("makeMiningOld2 unexpectedly succeeded")81except capnp.KjException as e:82assert_equal(e.description, "remote exception: std::exception: Old mining interface (@2) not supported. Please update your client!")83assert_equal(e.type, "FAILED")84asyncio.run(capnp.run(async_routine()))86def run_unclean_disconnect_test(self):87"""Test behavior when disconnecting during an IPC call that later88returns a non-null interface pointer. This used to cause a crash as89reported https://github.com/bitcoin/bitcoin/issues/34250, but now just90results in a cancellation log message"""91node = self.nodes[0]92self.log.info("Running disconnect during BlockTemplate.waitNext")93timeout = self.rpc_timeout * 1000.094disconnected_log_check = ExitStack()96async def async_routine():97ctx, mining = await make_mining_ctx(self)98self.log.debug("Create a template")99opts = self.capnp_modules['mining'].BlockCreateOptions()100template = (await mining.createNewBlock(ctx, opts)).result102self.log.debug("Wait for a new template")103waitoptions = self.capnp_modules['mining'].BlockWaitOptions()104waitoptions.timeout = timeout105waitoptions.feeThreshold = 1106with node.assert_debug_log(expected_msgs=["BlockTemplate.waitNext", "IPC server post request"], timeout=2):107promise = template.waitNext(ctx, waitoptions)108await asyncio.sleep(0.1)109disconnected_log_check.enter_context(node.assert_debug_log(expected_msgs=["IPC server: socket disconnected", "canceled while executing"], timeout=2))110del promise112asyncio.run(capnp.run(async_routine()))114# Wait for socket disconnected log message, then generate a block to115# cause the waitNext() call to return a new template. Look for a116# canceled IPC log message after waitNext returns.117with node.assert_debug_log(expected_msgs=["interrupted (canceled)"], timeout=2):118disconnected_log_check.close()119self.generate(node, 1)121def run_thread_busy_test(self):122"""Test behavior when sending multiple calls to the same server thread123which used to cause a crash as reported124https://github.com/bitcoin/bitcoin/issues/33923."""125node = self.nodes[0]126self.log.info("Running thread busy test")127timeout = self.rpc_timeout * 1000.0129async def async_routine():130ctx, mining = await make_mining_ctx(self)131self.log.debug("Create a template")132opts = self.capnp_modules['mining'].BlockCreateOptions()133template = (await mining.createNewBlock(ctx, opts)).result135self.log.debug("Wait for a new template")136waitoptions = self.capnp_modules['mining'].BlockWaitOptions()137waitoptions.timeout = timeout138waitoptions.feeThreshold = 1140# Make multiple waitNext calls where the first will start to141# execute, and the second and third will be posted waiting to142# execute. Previously, the third call would fail calling143# mp::Waiter::post() because the waiting function slot is occupied,144# but now posts are queued.145with node.assert_debug_log(expected_msgs=["BlockTemplate.waitNext", "IPC server post request"], timeout=2):146promise1 = template.waitNext(ctx, waitoptions)147await asyncio.sleep(0.1)148with node.assert_debug_log(expected_msgs=["BlockTemplate.waitNext", "IPC server post request"], timeout=2):149promise2 = template.waitNext(ctx, waitoptions)150await asyncio.sleep(0.1)151with node.assert_debug_log(expected_msgs=["BlockTemplate.waitNext", "IPC server post request"], timeout=2):152promise3 = template.waitNext(ctx, waitoptions)153await asyncio.sleep(0.1)155# Generate a new block to make the active waitNext calls return, then clean up.156with node.assert_debug_log(expected_msgs=["IPC server send response"], timeout=2):157self.generate(node, 1, sync_fun=self.no_op)158await ((await promise1).result).destroy(ctx)159await ((await promise2).result).destroy(ctx)160await ((await promise3).result).destroy(ctx)161await template.destroy(ctx)163asyncio.run(capnp.run(async_routine()))165def run_test(self):166self.run_echo_test()167self.run_mining_test()168self.run_deprecated_mining_test()169self.run_unclean_disconnect_test()170self.run_thread_busy_test()172if __name__ == '__main__':173IPCInterfaceTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.