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.
251blackhole_peer.send_and_ping(msg_addr())253# Confirm node has now received addr-related messages from blackhole peer254peerinfo = self.nodes[0].getpeerinfo()255assert_greater_than(self.sum_addr_messages(peerinfo[2]['bytesrecv_per_msg']), 0)256assert_equal(peerinfo[2]['addr_relay_enabled'], True)258msg = self.setup_addr_msg(2)259self.send_addr_msg(addr_source, msg, [receiver_peer, blackhole_peer])261# And that peer received addresses262assert_equal(blackhole_peer.num_ipv4_received, 2)264self.nodes[0].disconnect_p2ps()266def getaddr_tests(self):267# In the previous tests, the node answered GETADDR requests with an268# empty addrman. Due to GETADDR response caching (see269# CConnman::GetAddresses), the node would continue to provide 0 addrs270# in response until enough time has passed or the node is restarted.271self.restart_node(0)273self.log.info('Test getaddr behavior')274self.log.info('Check that we send a getaddr message upon connecting to an outbound-full-relay peer')275full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")276full_outbound_peer.sync_with_ping()277assert full_outbound_peer.getaddr_received()279# to avoid the node evicting the outbound peer, protect it by announcing the most recent header to it280tip_header = from_hex(CBlockHeader(), self.nodes[0].getblockheader(self.nodes[0].getbestblockhash(), False))281full_outbound_peer.send_and_ping(msg_headers([tip_header]))283self.log.info('Check that we do not send a getaddr message to a block-relay-only, feeler or inbound peer')284block_relay_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=1, connection_type="block-relay-only")285block_relay_peer.sync_with_ping()286assert_equal(block_relay_peer.getaddr_received(), False)287block_relay_peer.send_and_ping(msg_headers([tip_header]))289feeler_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=2, connection_type="feeler")290# bitcoind closes feeler connections as soon as it receives a version message291assert_equal(feeler_peer.is_connected, False)292assert_equal(feeler_peer.getaddr_received(), False)294inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(send_getaddr=False))295inbound_peer.sync_with_ping()296assert_equal(inbound_peer.getaddr_received(), False)297assert_equal(inbound_peer.addr_received(), False)299self.log.info('Check that we answer getaddr messages only from inbound peers')300# Add some addresses to addrman301for i in range(1000):302first_octet = i >> 8303second_octet = i % 256304a = f"{first_octet}.{second_octet}.1.1"305self.nodes[0].addpeeraddress(a, 8333)307full_outbound_peer.send_and_ping(msg_getaddr())308block_relay_peer.send_and_ping(msg_getaddr())309inbound_peer.send_and_ping(msg_getaddr())311# invoke m_next_addr_send timer, see under send_addr_msg() function for rationale312self.mocktime += 10 * 60313self.nodes[0].setmocktime(self.mocktime)314inbound_peer.wait_until(lambda: inbound_peer.addr_received() is True)316assert_equal(full_outbound_peer.num_ipv4_received, 0)317assert_equal(block_relay_peer.num_ipv4_received, 0)318assert inbound_peer.num_ipv4_received > 100320self.log.info('Check that we answer getaddr messages only once per connection')321received_addrs_before = inbound_peer.num_ipv4_received322with self.nodes[0].assert_debug_log(['Ignoring repeated "getaddr".']):323inbound_peer.send_and_ping(msg_getaddr())324self.mocktime += 10 * 60325self.nodes[0].setmocktime(self.mocktime)326inbound_peer.sync_with_ping()327received_addrs_after = inbound_peer.num_ipv4_received328assert_equal(received_addrs_before, received_addrs_after)330self.nodes[0].disconnect_p2ps()332def blocksonly_mode_tests(self):333self.log.info('Test addr relay in -blocksonly mode')334self.restart_node(0, ["-blocksonly", "[email protected]"])335self.mocktime = int(time.time())337self.log.info('Check that we send getaddr messages')338full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")339full_outbound_peer.sync_with_ping()340assert full_outbound_peer.getaddr_received()342self.log.info('Check that we relay address messages')343addr_source = self.nodes[0].add_p2p_connection(P2PInterface())344msg = self.setup_addr_msg(2)345self.send_addr_msg(addr_source, msg, [full_outbound_peer])346assert_equal(full_outbound_peer.num_ipv4_received, 2)348self.nodes[0].disconnect_p2ps()350def send_addrs_and_test_rate_limiting(self, peer, no_relay, *, new_addrs, total_addrs):351"""Send an addr message and check that the number of addresses processed and rate-limited is as expected"""353peer.send_and_ping(self.setup_addr_msg(new_addrs, sequential_ips=False))355peerinfo = self.nodes[0].getpeerinfo()[0]356addrs_processed = peerinfo['addr_processed']357addrs_rate_limited = peerinfo['addr_rate_limited']358self.log.debug(f"addrs_processed = {addrs_processed}, addrs_rate_limited = {addrs_rate_limited}")360if no_relay:361assert_equal(addrs_processed, 0)362assert_equal(addrs_rate_limited, 0)363else:364assert_equal(addrs_processed, min(total_addrs, peer.tokens))365assert_equal(addrs_rate_limited, max(0, total_addrs - peer.tokens))367def rate_limit_tests(self):368self.mocktime = int(time.time())369self.restart_node(0, [])370self.nodes[0].setmocktime(self.mocktime)372for conn_type, no_relay in [("outbound-full-relay", False), ("block-relay-only", True), ("inbound", False)]:373self.log.info(f'Test rate limiting of addr processing for {conn_type} peers')374if conn_type == "inbound":375peer = self.nodes[0].add_p2p_connection(AddrReceiver())376else:377peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type=conn_type)379# Send 600 addresses. For all but the block-relay-only peer this should result in addresses being processed.380self.send_addrs_and_test_rate_limiting(peer, no_relay, new_addrs=600, total_addrs=600)382# Send 600 more addresses. For the outbound-full-relay peer (which we send a GETADDR, and thus will383# process up to 1001 incoming addresses), this means more addresses will be processed.384self.send_addrs_and_test_rate_limiting(peer, no_relay, new_addrs=600, total_addrs=1200)386# Send 10 more. As we reached the processing limit for all nodes, no more addresses should be procesesd.387self.send_addrs_and_test_rate_limiting(peer, no_relay, new_addrs=10, total_addrs=1210)389# Advance the time by 100 seconds, permitting the processing of 10 more addresses.390# Send 200 and verify that 10 are processed.391self.mocktime += 100392self.nodes[0].setmocktime(self.mocktime)393peer.increment_tokens(10)395self.send_addrs_and_test_rate_limiting(peer, no_relay, new_addrs=200, total_addrs=1410)397# Advance the time by 1000 seconds, permitting the processing of 100 more addresses.398# Send 200 and verify that 100 are processed.399self.mocktime += 1000400self.nodes[0].setmocktime(self.mocktime)401peer.increment_tokens(100)403self.send_addrs_and_test_rate_limiting(peer, no_relay, new_addrs=200, total_addrs=1610)405self.nodes[0].disconnect_p2ps()407def get_nodes_that_received_addr(self, peer, receiver_peer, addr_receivers,408time_interval_1, time_interval_2):410# Clean addr response related to the initial getaddr.411for addr_receiver in addr_receivers:412addr_receiver.num_ipv4_received = 0414for _ in range(10):415self.mocktime += time_interval_1416self.msg.addrs[0].time = self.mocktime + TEN_MINUTES417self.nodes[0].setmocktime(self.mocktime)418with self.nodes[0].assert_debug_log(['received: addr (31 bytes) peer=0']):419peer.send_and_ping(self.msg)420self.mocktime += time_interval_2421self.nodes[0].setmocktime(self.mocktime)422receiver_peer.sync_with_ping()423return [node for node in addr_receivers if node.addr_received()]425def destination_rotates_once_in_24_hours_test(self):426self.restart_node(0, [])428self.log.info('Test within 24 hours an addr relay destination is rotated at most once')429self.mocktime = int(time.time())430self.msg = self.setup_addr_msg(1)431self.addr_receivers = []432peer = self.nodes[0].add_p2p_connection(P2PInterface())433receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver())434addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)]435nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, 0, TWO_HOURS) # 10 intervals of 2 hours436# Per RelayAddress, we would announce these addrs to 2 destinations per day.437# Since it's at most one rotation, at most 4 nodes can receive ADDR.438assert_greater_than_or_equal(ADDR_DESTINATIONS_THRESHOLD, len(nodes_received_addr))439self.nodes[0].disconnect_p2ps()441def destination_rotates_more_than_once_over_several_days_test(self):442self.restart_node(0, [])444self.log.info('Test after several days an addr relay destination is rotated more than once')445self.msg = self.setup_addr_msg(1)446peer = self.nodes[0].add_p2p_connection(P2PInterface())447receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver())448addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)]449# 10 intervals of 1 day (+ 1 hour, which should be enough to cover 30-min Poisson in most cases)450nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, ONE_DAY, ONE_HOUR)451# Now that there should have been more than one rotation, more than452# ADDR_DESTINATIONS_THRESHOLD nodes should have received ADDR.453assert_greater_than(len(nodes_received_addr), ADDR_DESTINATIONS_THRESHOLD)454self.nodes[0].disconnect_p2ps()457if __name__ == '__main__':458AddrTest(__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.