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) 2025-present 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"""6Test that a node sends a self-announcement with its external IP to7in- and outbound peers after connection open and again sometime later.9Additionally, this checks that the first self-announcement arrives10in its own message and that this message is the first we receive.11"""13import time15from test_framework.messages import (16CAddress,17from_hex,18msg_headers,19CBlockHeader,20)21from test_framework.p2p import P2PInterface22from test_framework.test_framework import BitcoinTestFramework23from test_framework.util import assert_equal, assert_greater_than25IP_TO_ANNOUNCE = "42.42.42.42"26ONION_ADDR = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"27ONE_DAY = 60 * 60 * 2430class SelfAnnouncementReceiver(P2PInterface):31self_announcements_received = 032addresses_received = 033addr_messages_received = 035expected = None36addrv2_test = False38def __init__(self, *, expected, addrv2):39super().__init__(support_addrv2=addrv2)40self.expected = expected41self.addrv2_test = addrv243def handle_addr_message(self, message):44self.addr_messages_received += 145for addr in message.addrs:46self.addresses_received += 147if addr == self.expected:48self.self_announcements_received += 149if self.self_announcements_received == 1:50# If it's the first self-announcement, check that it is51# in the first addr message we receive, and that this message52# only contains one address. This also implies that it is53# the first address we receive.54assert_equal(self.addr_messages_received, 1)55assert_equal(len(message.addrs), 1)57def on_addrv2(self, message):58assert (self.addrv2_test)59self.handle_addr_message(message)61def on_addr(self, message):62assert (not self.addrv2_test)63self.handle_addr_message(message)66class AddrSelfAnnouncementTest(BitcoinTestFramework):67def set_test_params(self):68self.num_nodes = 169self.extra_args = [[f"-externalip={IP_TO_ANNOUNCE}"]]71def run_test(self):72# populate addrman to have some addresses for a GETADDR response73for i in range(50):74a = f"{1 + i}.{i}.1.1"75self.nodes[0].addpeeraddress(a, 8333)77self.self_announcement_test(outbound=False, addrv2=False)78self.self_announcement_test(outbound=False, addrv2=True)79self.self_announcement_test(outbound=True, addrv2=False)80self.self_announcement_test(outbound=True, addrv2=True)81self.test_externalip_bypasses_onlynet()83@staticmethod84def inbound_connection_open_assertions(addr_receiver):85# In response to a GETADDR, we expect a message with the self-announcement86# and an addr message containing the GETADDR response.87assert_equal(addr_receiver.self_announcements_received, 1)88assert_equal(addr_receiver.addr_messages_received, 2)89assert_greater_than(addr_receiver.addresses_received, 1)91@staticmethod92def outbound_connection_open_assertions(addr_receiver):93# We expect only the self-announcement.94assert_equal(addr_receiver.self_announcements_received, 1)95assert_equal(addr_receiver.addr_messages_received, 1)96assert_equal(addr_receiver.addresses_received, 1)98def self_announcement_test(self, *, outbound, addrv2):99connection_type = "outbound" if outbound else "inbound"100addr_version = "addrv2" if addrv2 else "addrv1"101self.log.info(f"Test that the node does an address self-announcement to {connection_type} connections ({addr_version})")103# We only self-announce after initial block download is done104assert (not self.nodes[0].getblockchaininfo()["initialblockdownload"])106netinfo = self.nodes[0].getnetworkinfo()107port = netinfo["localaddresses"][0]["port"]108self.nodes[0].setmocktime(int(time.time()))110expected = CAddress()111expected.nServices = int(netinfo["localservices"], 16)112expected.ip = IP_TO_ANNOUNCE113expected.port = port114expected.time = self.nodes[0].mocktime116with self.nodes[0].assert_debug_log([f'Advertising address {IP_TO_ANNOUNCE}:{port}']):117if outbound:118self.log.info(f"Check that we get an initial self-announcement on an outbound connection from the node ({connection_type}, {addr_version})")119addr_receiver = self.nodes[0].add_outbound_p2p_connection(SelfAnnouncementReceiver(expected=expected, addrv2=addrv2), p2p_idx=0, connection_type="outbound-full-relay")120else:121self.log.info(f"Check that we get an initial self-announcement when connecting to a node and sending a GETADDR ({connection_type}, {addr_version})")122addr_receiver = self.nodes[0].add_p2p_connection(SelfAnnouncementReceiver(expected=expected, addrv2=addrv2))123addr_receiver.sync_with_ping()125if outbound:126self.outbound_connection_open_assertions(addr_receiver)127else:128self.inbound_connection_open_assertions(addr_receiver)130if outbound:131# to avoid the node evicting the outbound peer, protect it by announcing the most recent header to it132tip_header = from_hex(CBlockHeader(), self.nodes[0].getblockheader(self.nodes[0].getbestblockhash(), False))133addr_receiver.send_and_ping(msg_headers([tip_header]))135self.log.info(f"Check that we get more self-announcements sometime later ({connection_type}, {addr_version})")136for _ in range(5):137last_self_announcements_received = addr_receiver.self_announcements_received138last_addr_messages_received = addr_receiver.addr_messages_received139last_addresses_received = addr_receiver.addresses_received140with self.nodes[0].assert_debug_log([f'Advertising address {IP_TO_ANNOUNCE}:{port}']):141# m_next_local_addr_send and AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL:142# self-announcements are sent on an exponential distribution with mean interval of 24h.143# Setting the mocktime 20d forward gives a probability of (1 - e^-(480/24)) that144# the event will occur (i.e. this fails once in ~500 million repeats).145addr_receiver.expected.time = self.nodes[0].mocktime + 20 * ONE_DAY146self.nodes[0].bumpmocktime(20 * ONE_DAY)147addr_receiver.sync_with_ping()149assert_equal(addr_receiver.self_announcements_received, last_self_announcements_received + 1)150assert_equal(addr_receiver.addr_messages_received, last_addr_messages_received + 1)151assert_equal(addr_receiver.addresses_received, last_addresses_received + 1)153self.nodes[0].disconnect_p2ps()155def test_externalip_bypasses_onlynet(self):156self.log.info("Test that -externalip onion is advertised despite -onlynet=ipv4")157self.restart_node(0, extra_args=["-onlynet=ipv4", f"-externalip={ONION_ADDR}"])159netinfo = self.nodes[0].getnetworkinfo()161onion_net = next(n for n in netinfo["networks"] if n["name"] == "onion")162assert not onion_net["reachable"], "onion should not be reachable under -onlynet=ipv4"164addrs = [a["address"] for a in netinfo["localaddresses"]]165assert ONION_ADDR in addrs, f"onion address missing from localaddresses: {addrs}"167if __name__ == '__main__':168AddrSelfAnnouncementTest(__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.