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.
501if self.options.coveragedir is not None:502coverage.write_all_rpc_commands(self.options.coveragedir, node._rpc)504def start_nodes(self, extra_args=None, *args, **kwargs):505"""Start multiple bitcoinds"""507if extra_args is None:508extra_args = [None] * self.num_nodes509assert_equal(len(extra_args), self.num_nodes)510for i, node in enumerate(self.nodes):511node.start(extra_args[i], *args, **kwargs)512for node in self.nodes:513node.wait_for_rpc_connection()515if self.options.coveragedir is not None:516for node in self.nodes:517coverage.write_all_rpc_commands(self.options.coveragedir, node._rpc)519def stop_node(self, i, expected_stderr='', wait=0):520"""Stop a bitcoind test node"""521self.nodes[i].stop_node(expected_stderr, wait=wait)523def stop_nodes(self, wait=0):524"""Stop multiple bitcoind test nodes"""525for node in self.nodes:526# Issue RPC to stop nodes527node.stop_node(wait=wait, wait_until_stopped=False)529for node in self.nodes:530# Wait for nodes to stop531node.wait_until_stopped()533def cleanup_partially_started_nodes(self):534"""Tear down nodes left running after a failed start_nodes().536After start_nodes() raises (e.g. FailedToStartError), some nodes may be537RPC-connected, some may have a live process without RPC, and some may538already have exited. Stop the connected ones cleanly and force-kill the539rest so the framework's teardown can proceed.540"""541for node in self.nodes:542if not node.running:543continue544if node.rpc_connected:545node.stop_node(wait=node.rpc_timeout)546else:547node.process.kill()548node.process.wait(timeout=node.rpc_timeout)549node.process = None550node.stdout.close()551node.stderr.close()552node.running = False554def restart_node(self, i, extra_args=None, clear_addrman=False, *, expected_stderr=''):555"""Stop and start a test node"""556self.stop_node(i, expected_stderr=expected_stderr)557if clear_addrman:558peers_dat = self.nodes[i].chain_path / "peers.dat"559os.remove(peers_dat)560with self.nodes[i].assert_debug_log(expected_msgs=[f'Creating peers.dat because the file was not found ("{peers_dat}")']):561self.start_node(i, extra_args)562else:563self.start_node(i, extra_args)565def wait_for_node_exit(self, i, timeout):566self.nodes[i].process.wait(timeout)568def connect_nodes(self, a, b, *, peer_advertises_v2=None):569from_connection = self.nodes[a]570to_connection = self.nodes[b]572# Use subversion as peer id. Test nodes have their node number appended to the user agent string573from_connection_subver = from_connection.getnetworkinfo()['subversion']574to_connection_subver = to_connection.getnetworkinfo()['subversion']576def find_conn(node, peer_subversion, inbound):577return next(filter(lambda peer: peer['subver'] == peer_subversion and peer['inbound'] == inbound, node.getpeerinfo()), None)579self.wait_until(lambda: not find_conn(from_connection, to_connection_subver, inbound=False))580self.wait_until(lambda: not find_conn(to_connection, from_connection_subver, inbound=True))582ip_port = "127.0.0.1:" + str(p2p_port(b))584if peer_advertises_v2 is None:585peer_advertises_v2 = from_connection.use_v2transport587if peer_advertises_v2 != from_connection.use_v2transport:588from_connection.addnode(node=ip_port, command="onetry", v2transport=peer_advertises_v2)589else:590# skip the optional third argument if it matches the default, for591# compatibility with older clients592from_connection.addnode(ip_port, "onetry")594self.wait_until(lambda: find_conn(from_connection, to_connection_subver, inbound=False) is not None)595self.wait_until(lambda: find_conn(to_connection, from_connection_subver, inbound=True) is not None)597def check_bytesrecv(peer, msg_type, min_bytes_recv):598assert peer is not None, "Error: peer disconnected"599return peer['bytesrecv_per_msg'].pop(msg_type, 0) >= min_bytes_recv601# Poll until version handshake (fSuccessfullyConnected) is complete to602# avoid race conditions, because some message types are blocked from603# being sent or received before fSuccessfullyConnected.604#605# As the flag fSuccessfullyConnected is not exposed, check it by606# waiting for a pong, which can only happen after the flag was set.607self.wait_until(lambda: check_bytesrecv(find_conn(from_connection, to_connection_subver, inbound=False), 'pong', 29))608self.wait_until(lambda: check_bytesrecv(find_conn(to_connection, from_connection_subver, inbound=True), 'pong', 29))610def disconnect_nodes(self, a, b):611def disconnect_nodes_helper(node_a, node_b):612def get_peer_ids(from_connection, node_num):613result = []614for peer in from_connection.getpeerinfo():615if "testnode{}".format(node_num) in peer['subver']:616result.append(peer['id'])617return result619peer_ids = get_peer_ids(node_a, node_b.index)620if not peer_ids:621self.log.warning("disconnect_nodes: {} and {} were not connected".format(622node_a.index,623node_b.index,624))625return626for peer_id in peer_ids:627try:628node_a.disconnectnode(nodeid=peer_id)629except JSONRPCException as e:630# If this node is disconnected between calculating the peer id631# and issuing the disconnect, don't worry about it.632# This avoids a race condition if we're mass-disconnecting peers.633if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED634raise636# wait to disconnect637self.wait_until(lambda: not get_peer_ids(node_a, node_b.index), timeout=5)638self.wait_until(lambda: not get_peer_ids(node_b, node_a.index), timeout=5)640disconnect_nodes_helper(self.nodes[a], self.nodes[b])642def split_network(self):643"""644Split the network of four nodes into nodes 0/1 and 2/3.645"""646self.disconnect_nodes(1, 2)647self.sync_all(self.nodes[:2])648self.sync_all(self.nodes[2:])650def join_network(self):651"""652Join the (previously split) network halves together.653"""654self.connect_nodes(1, 2)655self.sync_all()657def no_op(self):658pass660def generate(self, generator, *args, sync_fun=None, **kwargs):661blocks = generator.generate(*args, called_by_framework=True, **kwargs)662sync_fun() if sync_fun else self.sync_all()663return blocks665def generateblock(self, generator, *args, sync_fun=None, **kwargs):666blocks = generator.generateblock(*args, called_by_framework=True, **kwargs)667sync_fun() if sync_fun else self.sync_all()668return blocks670def generatetoaddress(self, generator, *args, sync_fun=None, **kwargs):671blocks = generator.generatetoaddress(*args, called_by_framework=True, **kwargs)672sync_fun() if sync_fun else self.sync_all()673return blocks675def generatetodescriptor(self, generator, *args, sync_fun=None, **kwargs):676blocks = generator.generatetodescriptor(*args, called_by_framework=True, **kwargs)677sync_fun() if sync_fun else self.sync_all()678return blocks680def create_outpoints(self, node, *, outputs):681"""Send funds to a given list of `{address: amount}` targets using the bitcoind682wallet and return the corresponding outpoints as a list of dictionaries683`[{"txid": txid, "vout": vout1}, {"txid": txid, "vout": vout2}, ...]`.684The result can be used to specify inputs for RPCs like `createrawtransaction`,685`createpsbt`, `lockunspent` etc."""686for output in outputs:687assert_equal(len(output.keys()), 1)688send_res = node.send(outputs)689assert send_res["complete"]690utxos = []691for output in outputs:692address = list(output.keys())[0]693vout = find_vout_for_address(node, send_res["txid"], address)694utxos.append({"txid": send_res["txid"], "vout": vout})695return utxos697def sync_blocks(self, nodes=None, wait=1, timeout=60):698"""699Wait until everybody has the same tip.700sync_blocks needs to be called with an rpc_connections set that has least701one node already synced to the latest, stable tip, otherwise there's a702chance it might return before all nodes are stably synced.703"""704rpc_connections = nodes or self.nodes705timeout = int(timeout * self.options.timeout_factor)706stop_time = time.time() + timeout707while time.time() <= stop_time:708best_hash = [x.getbestblockhash() for x in rpc_connections]709if best_hash.count(best_hash[0]) == len(rpc_connections):710return711# Check that each peer has at least one connection712assert (all([len(x.getpeerinfo()) for x in rpc_connections]))713time.sleep(wait)714raise AssertionError("Block sync timed out after {}s:{}".format(715timeout,716"".join("\n {!r}".format(b) for b in best_hash),717))719def sync_mempools(self, nodes=None, wait=1, timeout=60, flush_scheduler=True):720"""721Wait until everybody has the same transactions in their memory722pools723"""724rpc_connections = nodes or self.nodes725timeout = int(timeout * self.options.timeout_factor)726stop_time = time.time() + timeout727while time.time() <= stop_time:728pool = [set(r.getrawmempool()) for r in rpc_connections]729if pool.count(pool[0]) == len(rpc_connections):730if flush_scheduler:731for r in rpc_connections:732r.syncwithvalidationinterfacequeue()733return734# Check that each peer has at least one connection735assert (all([len(x.getpeerinfo()) for x in rpc_connections]))736time.sleep(wait)737raise AssertionError("Mempool sync timed out after {}s:{}".format(738timeout,739"".join("\n {!r}".format(m) for m in pool),740))742def sync_all(self, nodes=None):743self.sync_blocks(nodes)744self.sync_mempools(nodes)746def wait_until(self, test_function, timeout=60, check_interval=0.05):747return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor, check_interval=check_interval)749def fill_node_addrman(self, *, node_index, address_types_to_add):750ADDRESSES = {
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.