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"""Shared utilities for IPC (multiprocess) interface tests."""6import asyncio7import inspect8from contextlib import asynccontextmanager9from dataclasses import dataclass10from io import BytesIO11from pathlib import Path12import shutil13from typing import Optional15from test_framework.messages import CBlock16from test_framework.util import (17assert_equal18)20# Test may be skipped and not have capnp installed21try:22import capnp # type: ignore[import] # noqa: F40123except ModuleNotFoundError:24pass27# Stores the result of getCoinbaseTx()28@dataclass29class CoinbaseTxData:30version: int31sequence: int32scriptSigPrefix: bytes33witness: Optional[bytes]34blockRewardRemaining: int35requiredOutputs: list[bytes]36lockTime: int39@asynccontextmanager40async def destroying(obj, ctx):41"""Call obj.destroy(ctx) at end of with: block. Similar to contextlib.closing."""42try:43yield obj44finally:45await obj.destroy(ctx)48async def wait_and_do(wait_fn, do_fn):49"""Call wait_fn, then sleep, then call do_fn in a parallel task. Wait for50both tasks to complete."""51wait_started = asyncio.Event()52result = None54async def wait():55nonlocal result56wait_started.set()57result = await wait_fn59async def do():60await wait_started.wait()61await asyncio.sleep(0.1)62# Let do_fn be either a callable or an awaitable object63if inspect.isawaitable(do_fn):64await do_fn65else:66do_fn()68await asyncio.gather(wait(), do())69return result72def load_capnp_modules(config):73if capnp_bin := shutil.which("capnp"):74# Add the system cap'nproto path so include/capnp/c++.capnp can be found.75capnp_dir = Path(capnp_bin).resolve().parent.parent / "include"76else:77# If there is no system cap'nproto, the pycapnp module should have its own "bundled"78# includes at this location. If pycapnp was installed with bundled capnp,79# capnp/c++.capnp can be found here.80capnp_dir = Path(capnp.__path__[0]).parent81src_dir = Path(config['environment']['SRCDIR']) / "src"82mp_dir = src_dir / "ipc" / "libmultiprocess" / "include"83# List of import directories. Note: it is important for mp_dir to be84# listed first, in case there are other libmultiprocess installations on85# the system, to ensure that `import "/mp/proxy.capnp"` lines load the86# same file as capnp.load() loads directly below, and there are not87# "failed: Duplicate ID @0xcc316e3f71a040fb" errors.88imports = [str(mp_dir), str(capnp_dir), str(src_dir)]89return {90"proxy": capnp.load(str(mp_dir / "mp" / "proxy.capnp"), imports=imports),91"init": capnp.load(str(src_dir / "ipc" / "capnp" / "init.capnp"), imports=imports),92"echo": capnp.load(str(src_dir / "ipc" / "capnp" / "echo.capnp"), imports=imports),93"mining": capnp.load(str(src_dir / "ipc" / "capnp" / "mining.capnp"), imports=imports),94}97async def make_capnp_init_ctx(self, node_index=0):98node = self.nodes[node_index]99# Establish a connection, and create Init proxy object.100connection = await capnp.AsyncIoStream.create_unix_connection(node.ipc_socket_path)101client = capnp.TwoPartyClient(connection)102init = client.bootstrap().cast_as(self.capnp_modules['init'].Init)103# Create a remote thread on the server for the IPC calls to be executed in.104threadmap = init.construct().threadMap105thread = threadmap.makeThread("pythread").result106ctx = self.capnp_modules['proxy'].Context()107ctx.thread = thread108# Return both.109return ctx, init112async def mining_create_block_template(mining, stack, ctx, *args, **kwargs):113"""Call mining.createNewBlock() and return template, then call template.destroy() when stack exits."""114response = await mining.createNewBlock(ctx, *args, **kwargs)115if not response._has("result"):116return None117return await stack.enter_async_context(destroying(response.result, ctx))120async def mining_wait_next_template(template, stack, ctx, opts):121"""Call template.waitNext() and return template, then call template.destroy() when stack exits."""122response = await template.waitNext(ctx, opts)123if not response._has("result"):124return None125return await stack.enter_async_context(destroying(response.result, ctx))128async def mining_get_block(block_template, ctx):129block_data = BytesIO((await block_template.getBlock(ctx)).result)130block = CBlock()131block.deserialize(block_data)132return block135async def mining_get_coinbase_tx(block_template, ctx) -> CoinbaseTxData:136assert block_template is not None137# Note: the template_capnp struct will be garbage-collected when this138# method returns, so it is important to copy any Data fields from it139# which need to be accessed later using the bytes() cast. Starting with140# pycapnp v2.2.0, Data fields have type `memoryview` and are ephemeral.141template_capnp = (await block_template.getCoinbaseTx(ctx)).result142witness: Optional[bytes] = None143if template_capnp._has("witness"):144witness = bytes(template_capnp.witness)145return CoinbaseTxData(146version=int(template_capnp.version),147sequence=int(template_capnp.sequence),148scriptSigPrefix=bytes(template_capnp.scriptSigPrefix),149witness=witness,150blockRewardRemaining=int(template_capnp.blockRewardRemaining),151requiredOutputs=[bytes(output) for output in template_capnp.requiredOutputs],152lockTime=int(template_capnp.lockTime),153)155async def make_mining_ctx(self, node_index=0):156"""Create IPC context and Mining proxy object."""157ctx, init = await make_capnp_init_ctx(self, node_index)158self.log.debug("Create Mining proxy object")159mining = init.makeMining(ctx).result160return ctx, mining162def assert_capnp_failed(e, description_prefix):163assert e.description.startswith(description_prefix), f"Expected description starting with '{description_prefix}', got '{e.description}'"164assert_equal(e.type, "FAILED")167async def assert_create_new_block_fails(ctx, mining, opts, expected_msg):168"""Assert that mining.createNewBlock fails with the expected remote exception."""169try:170await mining.createNewBlock(ctx, opts)171raise AssertionError("createNewBlock unexpectedly succeeded")172except capnp.lib.capnp.KjException as e:173assert_capnp_failed(e, f"remote exception: std::exception: {expected_msg}")
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.