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) 2010 ArtForz -- public domain half-a-node3# Copyright (c) 2012 Jeff Garzik4# Copyright (c) 2010-present The Bitcoin Core developers5# Distributed under the MIT software license, see the accompanying6# file COPYING or http://www.opensource.org/licenses/mit-license.php.7"""Test objects for interacting with a bitcoind node over the p2p protocol.9The P2PInterface objects interact with the bitcoind nodes under test using the10node's p2p interface. They can be used to send messages to the node, and11callbacks can be registered that execute when messages are received from the12node. Messages are sent to/received from the node on an asyncio event loop.13State held inside the objects must be guarded by the p2p_lock to avoid data14races between the main testing thread and the event loop.16P2PConnection: A low-level connection object to a node's P2P interface17P2PInterface: A high-level interface object for communicating to a node over P2P18P2PDataStore: A p2p interface class that keeps a store of transactions and blocks19and can respond correctly to getdata and getheaders messages20P2PTxInvStore: A p2p interface class that inherits from P2PDataStore, and keeps21a count of how many times each txid has been announced."""23import asyncio24from collections import defaultdict25import ipaddress26from io import BytesIO27import logging28import platform29import socket30import struct31import sys32import threading34from test_framework.messages import (35CBlockHeader,36MAX_HEADERS_RESULTS,37msg_addr,38msg_addrv2,39msg_block,40MSG_BLOCK,41msg_blocktxn,42msg_cfcheckpt,43msg_cfheaders,44msg_cfilter,45msg_cmpctblock,46msg_feature,47msg_feefilter,48msg_filteradd,49msg_filterclear,50msg_filterload,51msg_getaddr,52msg_getblocks,53msg_getblocktxn,54msg_getcfcheckpt,55msg_getcfheaders,56msg_getcfilters,57msg_getdata,58msg_getheaders,59msg_headers,60msg_inv,61msg_mempool,62msg_merkleblock,63msg_notfound,64msg_ping,65msg_pong,66msg_sendaddrv2,67msg_sendcmpct,68msg_sendheaders,69msg_sendtxrcncl,70msg_tx,71MSG_TX,72MSG_TYPE_MASK,73msg_verack,74msg_version,75MSG_WTX,76msg_wtxidrelay,77NODE_NETWORK,78NODE_WITNESS,79MAGIC_BYTES,80sha256,81)82from test_framework.netutil import (83set_ephemeral_port_range,84)85from test_framework.util import (86assert_not_equal,87MAX_NODES,88p2p_port,89wait_until_helper_internal,90)91from test_framework.v2_p2p import (92EncryptedP2PState,93MSGTYPE_TO_SHORTID,94SHORTID,95)97logger = logging.getLogger("TestFramework.p2p")99# The minimum P2P version that this test framework supports100MIN_P2P_VERSION_SUPPORTED = 60001101# The P2P version that this test framework implements and sends in its `version` message102# Version 70016 supports wtxid relay103# Version 70017 supports feature104P2P_VERSION = 70017105# The services that this test framework offers in its `version` message106P2P_SERVICES = NODE_NETWORK | NODE_WITNESS107# The P2P user agent string that this test framework sends in its `version` message108P2P_SUBVERSION = "/python-p2p-tester:0.0.3/"109# Value for relay that this test framework sends in its `version` message110P2P_VERSION_RELAY = 1111# Delay after receiving a tx inv before requesting transactions from non-preferred peers, in seconds112NONPREF_PEER_TX_DELAY = 2113# Delay for requesting transactions via txids if we have wtxid-relaying peers, in seconds114TXID_RELAY_DELAY = 2115# Delay for requesting transactions if the peer has MAX_PEER_TX_REQUEST_IN_FLIGHT or more requests116OVERLOADED_PEER_TX_DELAY = 2117# How long to wait before downloading a transaction from an additional peer118GETDATA_TX_INTERVAL = 60120MESSAGEMAP = {121b"addr": msg_addr,122b"addrv2": msg_addrv2,123b"block": msg_block,124b"blocktxn": msg_blocktxn,125b"cfcheckpt": msg_cfcheckpt,126b"cfheaders": msg_cfheaders,127b"cfilter": msg_cfilter,128b"cmpctblock": msg_cmpctblock,129b"feature": msg_feature,130b"feefilter": msg_feefilter,131b"filteradd": msg_filteradd,132b"filterclear": msg_filterclear,133b"filterload": msg_filterload,134b"getaddr": msg_getaddr,135b"getblocks": msg_getblocks,136b"getblocktxn": msg_getblocktxn,137b"getcfcheckpt": msg_getcfcheckpt,138b"getcfheaders": msg_getcfheaders,139b"getcfilters": msg_getcfilters,140b"getdata": msg_getdata,141b"getheaders": msg_getheaders,142b"headers": msg_headers,143b"inv": msg_inv,144b"mempool": msg_mempool,145b"merkleblock": msg_merkleblock,146b"notfound": msg_notfound,147b"ping": msg_ping,148b"pong": msg_pong,149b"sendaddrv2": msg_sendaddrv2,150b"sendcmpct": msg_sendcmpct,151b"sendheaders": msg_sendheaders,152b"sendtxrcncl": msg_sendtxrcncl,153b"tx": msg_tx,154b"verack": msg_verack,155b"version": msg_version,156b"wtxidrelay": msg_wtxidrelay,157}160class P2PConnection(asyncio.Protocol):161"""A low-level connection object to a node's P2P interface.163This class is responsible for:165- opening and closing the TCP connection to the node166- reading bytes from and writing bytes to the socket167- deserializing and serializing the P2P message header168- logging messages as they are sent and received170This class contains no logic for handing the P2P message payloads. It must be171sub-classed and the on_message() callback overridden."""173def __init__(self):174# The underlying transport of the connection.175# Should only call methods on this from the NetworkThread, c.f. call_soon_threadsafe176self._transport = None177# This lock is acquired before sending messages over the socket. There's an implied lock order and178# p2p_lock must not be acquired after _send_lock as it could result in deadlocks.179self._send_lock = threading.Lock()180self.v2_state = None # EncryptedP2PState object needed for v2 p2p connections181self.reconnect = False # set if reconnection needs to happen183@property184def is_connected(self):185return self._transport is not None187@property188def supports_v2_p2p(self):189return self.v2_state is not None191def peer_connect_helper(self, dstaddr, dstport, net, timeout_factor):192assert not self.is_connected193self.timeout_factor = timeout_factor194self.dstaddr = dstaddr195self.dstport = dstport196# The initial message to send after the connection was made:197self.on_connection_send_msg = None198self.recvbuf = b""199self.magic_bytes = MAGIC_BYTES[net]200self.p2p_connected_to_node = dstport != 0202def peer_connect(self, dstaddr, dstport, *, net, timeout_factor, supports_v2_p2p):203self.peer_connect_helper(dstaddr, dstport, net, timeout_factor)204if supports_v2_p2p:205self.v2_state = EncryptedP2PState(initiating=True, net=net)207loop = NetworkThread.network_event_loop208logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport))209coroutine = loop.create_connection(lambda: self, host=self.dstaddr, port=self.dstport)210return lambda: loop.call_soon_threadsafe(loop.create_task, coroutine)212def peer_accept_connection(self, connect_id, connect_cb=lambda: None, *, net, timeout_factor, supports_v2_p2p, reconnect):213self.peer_connect_helper('0', 0, net, timeout_factor)214self.reconnect = reconnect215if supports_v2_p2p:216self.v2_state = EncryptedP2PState(initiating=False, net=net)218logger.debug('Listening for Bitcoin Node with id: {}'.format(connect_id))219return lambda: NetworkThread.listen(self, connect_cb, idx=connect_id)221def peer_disconnect(self):222# Connection could have already been closed by other end.223NetworkThread.network_event_loop.call_soon_threadsafe(lambda: self._transport and self._transport.abort())225# Connection and disconnection methods227def connection_made(self, transport):228"""asyncio callback when a connection is opened."""229assert not self._transport230info = transport.get_extra_info("socket")231us = info.getsockname()232them = info.getpeername()233logger.debug(f"Connected: us={us[0]}:{us[1]}, them={them[0]}:{them[1]}")234self.dstaddr = them[0]235self.dstport = them[1]236self._transport = transport237# in an inbound connection to the TestNode with P2PConnection as the initiator, [TestNode <---- P2PConnection]238# send the initial handshake immediately239if self.supports_v2_p2p and self.v2_state.initiating and not self.v2_state.tried_v2_handshake:240send_handshake_bytes = self.v2_state.initiate_v2_handshake()241logger.debug(f"sending {len(self.v2_state.sent_garbage)} bytes of garbage data")242self.send_raw_message(send_handshake_bytes)243# for v1 outbound connections, send version message immediately after opening244# (for v2 outbound connections, send it after the initial v2 handshake)245if self.p2p_connected_to_node and not self.supports_v2_p2p:246self.send_version()247self.on_open()249def connection_lost(self, exc):250"""asyncio callback when a connection is closed."""
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.