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) 2020-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"""Test asmap config argument for ASN-based IP bucketing.7Verify node behaviour and debug log when launching bitcoind with different8`-asmap` and `-noasmap` arg values, including absolute and relative paths, and9with missing and unparseable files.11The tests are order-independent.13"""14import hashlib15import os16import shutil18from test_framework.test_framework import BitcoinTestFramework19from test_framework.util import (20assert_equal,21assert_raises_rpc_error,22)24ASMAP = 'src/test/data/asmap.raw' # path to unit test skeleton asmap25VERSION = '55dcec00c72b8a33a271dad20271e14cfbe5526aa1dc12e2559f30c376d85a35'26EMBEDDED_VERSION = '03580ade8ec0036ad3d6a5a91602b995c89387f722d3fee58127219de6aafc12' # SHA256 of src/node/data/ip_asn.dat28def expected_messages(filename):29return [f'Opened asmap file "{filename}" (59 bytes) from disk',30f'Using asmap version {VERSION} for IP bucketing']32class AsmapTest(BitcoinTestFramework):33def set_test_params(self):34self.num_nodes = 135# Do addrman checks on all operations and use deterministic addrman36self.extra_args = [["-checkaddrman=1", "-test=addrman"]]38def fill_addrman(self, node_id):39"""Add 2 tried addresses to the addrman, followed by 2 new addresses."""40for addr, tried in [[0, True], [1, True], [2, False], [3, False]]:41self.nodes[node_id].addpeeraddress(address=f"101.{addr}.0.0", tried=tried, port=8333)43def test_without_asmap_arg(self):44self.log.info('Test bitcoind with no -asmap arg passed')45self.stop_node(0)46with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']):47self.start_node(0)48assert "asmap_version" not in self.node.getnetworkinfo()50def test_noasmap_arg(self):51self.log.info('Test bitcoind with -noasmap arg passed')52self.stop_node(0)53with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']):54self.start_node(0, ["-noasmap"])56def test_asmap_with_absolute_path(self):57self.log.info('Test bitcoind -asmap=<absolute path>')58self.stop_node(0)59filename = os.path.join(self.datadir, 'my-map-file.map')60shutil.copyfile(self.asmap_raw, filename)61with self.node.assert_debug_log(expected_messages(filename)):62self.start_node(0, [f'-asmap={filename}'])63assert_equal(self.node.getnetworkinfo()["asmap_version"], VERSION)64os.remove(filename)66def test_asmap_with_relative_path(self):67self.log.info('Test bitcoind -asmap=<relative path>')68self.stop_node(0)69name = 'ASN_map'70filename = os.path.join(self.datadir, name)71shutil.copyfile(self.asmap_raw, filename)72with self.node.assert_debug_log(expected_messages(filename)):73self.start_node(0, [f'-asmap={name}'])74os.remove(filename)76def test_embedded_asmap(self):77if self.is_embedded_asmap_compiled():78self.log.info('Test bitcoind -asmap (using embedded map data)')79for arg in ['-asmap', '-asmap=1']:80self.stop_node(0)81with self.node.assert_debug_log(["Opened asmap data", "from embedded byte array",82f"Using asmap version {EMBEDDED_VERSION} for IP bucketing"]):83self.start_node(0, [arg])84assert_equal(self.node.getnetworkinfo()["asmap_version"], EMBEDDED_VERSION)85else:86self.log.info('Test bitcoind -asmap (compiled without embedded map data)')87for arg in ['-asmap', '-asmap=1']:88self.stop_node(0)89msg = "Error: Embedded asmap data not available"90self.node.assert_start_raises_init_error(extra_args=[arg], expected_msg=msg)92def test_asmap_interaction_with_addrman_containing_entries(self):93self.log.info("Test bitcoind -asmap restart with addrman containing new and tried entries")94self.stop_node(0)95self.start_node(0, [f"-asmap={self.asmap_raw}", "-checkaddrman=1", "-test=addrman"])96self.fill_addrman(node_id=0)97rebucket_msg = "Bucketing method was updated, re-bucketing addrman entries from disk"98with self.node.assert_debug_log(expected_msgs=[], unexpected_msgs=[rebucket_msg]):99self.restart_node(0, [f"-asmap={self.asmap_raw}", "-checkaddrman=1", "-test=addrman"])100with self.node.assert_debug_log(101expected_msgs=[102"CheckAddrman: new 2, tried 2, total 4 started",103"CheckAddrman: completed",104]105):106self.node.getnodeaddresses() # getnodeaddresses re-runs the addrman checks108self.log.info("Test bitcoind restart without -asmap re-buckets the addrman entries")109with self.node.assert_debug_log(expected_msgs=[rebucket_msg]):110self.restart_node(0, ["-checkaddrman=1", "-test=addrman"])112def test_asmap_with_missing_file(self):113self.log.info('Test bitcoind -asmap with missing map file')114self.stop_node(0)115msg = f"Error: Could not find asmap file \"{self.datadir}{os.sep}missing\""116self.node.assert_start_raises_init_error(extra_args=['-asmap=missing'], expected_msg=msg)118def test_empty_asmap(self):119self.log.info('Test bitcoind -asmap with empty map file')120self.stop_node(0)121empty_asmap = os.path.join(self.datadir, "ip_asn.map")122with open(empty_asmap, "w") as f:123f.write("")124msg = f"Error: Could not parse asmap file \"{empty_asmap}\""125self.node.assert_start_raises_init_error(extra_args=[f'-asmap={empty_asmap}'], expected_msg=msg)126os.remove(empty_asmap)128def test_asmap_health_check(self):129self.log.info('Test bitcoind -asmap logs ASMap Health Check with basic stats')130msg = "ASMap Health Check: 4 clearnet peers are mapped to 3 ASNs with 0 peers being unmapped"131with self.node.assert_debug_log(expected_msgs=[msg]):132self.start_node(0, extra_args=[f'-asmap={self.asmap_raw}'])133raw_addrman = self.node.getrawaddrman()134asns = []135for _, entries in raw_addrman.items():136for _, entry in entries.items():137asn = entry['mapped_as']138if asn not in asns:139asns.append(asn)140assert_equal(len(asns), 3)142def test_export_embedded_asmap(self):143self.log.info('Test exportasmap RPC')144export_path = os.path.join(self.datadir, "asmap.dat")146if not self.is_embedded_asmap_compiled():147assert_raises_rpc_error(-1, "No embedded ASMap data available", self.node.exportasmap, export_path)148return150# Relative paths are resolved against the datadir.151result = self.node.exportasmap("asmap.dat")152assert_equal(result["path"], export_path)154with open(export_path, 'rb') as f:155data = f.read()156assert_equal(result["bytes_written"], len(data))158assert_equal(hashlib.sha256(data).hexdigest(), EMBEDDED_VERSION)159assert_equal(result["file_hash"], EMBEDDED_VERSION)161os.remove(export_path)163def run_test(self):164self.node = self.nodes[0]165self.datadir = self.node.chain_path166base_dir = self.config["environment"]["SRCDIR"]167self.asmap_raw = os.path.join(base_dir, ASMAP)168# The asmap version from the logs is the plain SHA256 of the file169with open(self.asmap_raw, 'rb') as f:170assert_equal(hashlib.sha256(f.read()).hexdigest(), VERSION)172self.test_without_asmap_arg()173self.test_noasmap_arg()174self.test_asmap_with_absolute_path()175self.test_asmap_with_relative_path()176self.test_embedded_asmap()177self.test_asmap_interaction_with_addrman_containing_entries()178self.test_asmap_with_missing_file()179self.test_empty_asmap()180self.test_asmap_health_check()181self.test_export_embedded_asmap()184if __name__ == '__main__':185AsmapTest(__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.