Skip to content

Commit 44717da

Browse files
committed
init: Require explicit -asmap filename
Currently, if `-asmap` is specified without a filename bitcoind tries to load `ip_asn.map` data file. This change now requires `-asmap=ip_asn.map` or another filename to be specified explicitly. The change is intended to make behavior of the option explicit avoid confusion reported #33386 where documentation specifies a default file which is not actually loaded by default. It was originally implemented in #33631 (comment) and various alternatives are discussed there.
1 parent 25c45bb commit 44717da

File tree

3 files changed

+29
-44
lines changed

3 files changed

+29
-44
lines changed

doc/release-notes-33770.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`-asmap` requires explicit filename
2+
-----------------------------------
3+
4+
In previous releases, if `-asmap` was specified without a filename, this would try to load an `ip_asn.map` data file. Now loading an asmap file requires an explicit filename like `-asmap=ip_asn.map`. This change was made to make the option easier to understand, because it was confusing for there to be a default filename not actually loaded by default (https://github.com/bitcoin/bitcoin/issues/33386). Also this change makes the option more future-proof, because in upcoming releases, specifying `-asmap` will load embedded asmap data instead of an external file (https://github.com/bitcoin/bitcoin/pull/28792).

src/init.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
158158
#endif
159159

160160
static constexpr int MIN_CORE_FDS = MIN_LEVELDB_FDS + NUM_FDS_MESSAGE_CAPTURE;
161-
static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
162161

163162
/**
164163
* The PID file facilities.
@@ -532,7 +531,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
532531
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
533532

534533
argsman.AddArg("-addnode=<ip>", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
535-
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
534+
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: none). Relative paths will be prefixed by the net-specific datadir location."), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
536535
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
537536
argsman.AddArg("-bind=<addr>[:<port>][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet3: 127.0.0.1:%u=onion, testnet4: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultChainParams->GetDefaultPort() + 1, testnetChainParams->GetDefaultPort() + 1, testnet4ChainParams->GetDefaultPort() + 1, signetChainParams->GetDefaultPort() + 1, regtestChainParams->GetDefaultPort() + 1), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
538537
argsman.AddArg("-cjdnsreachable", "If set, then this host is configured for CJDNS (connecting to fc00::/8 addresses would lead us to the CJDNS network, see doc/cjdns.md) (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1548,11 +1547,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15481547
// Read asmap file if configured
15491548
std::vector<bool> asmap;
15501549
if (args.IsArgSet("-asmap") && !args.IsArgNegated("-asmap")) {
1551-
fs::path asmap_path = args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
1550+
fs::path asmap_path = args.GetPathArg("-asmap");
15521551
if (!asmap_path.is_absolute()) {
15531552
asmap_path = args.GetDataDirNet() / asmap_path;
15541553
}
1555-
if (!fs::exists(asmap_path)) {
1554+
if (!fs::is_regular_file(asmap_path)) {
15561555
InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
15571556
return false;
15581557
}

test/functional/feature_asmap.py

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,9 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test asmap config argument for ASN-based IP bucketing.
66
7-
Verify node behaviour and debug log when launching bitcoind in these cases:
8-
9-
1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing
10-
11-
2. `bitcoind -asmap=<absolute path>`, using the unit test skeleton asmap
12-
13-
3. `bitcoind -asmap=<relative path>`, using the unit test skeleton asmap
14-
15-
4. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap
16-
17-
5. `bitcoind -asmap` restart with an addrman containing new and tried entries
18-
19-
6. `bitcoind -asmap` with no file specified and a missing default asmap file
20-
21-
7. `bitcoind -asmap` with an empty (unparsable) default asmap file
7+
Verify node behaviour and debug log when launching bitcoind with different
8+
`-asmap` and `-noasmap` arg values, including absolute and relative paths, and
9+
with missing and unparseable files.
2210
2311
The tests are order-independent.
2412
@@ -29,7 +17,6 @@
2917
from test_framework.test_framework import BitcoinTestFramework
3018
from test_framework.util import assert_equal
3119

32-
DEFAULT_ASMAP_FILENAME = 'ip_asn.map' # defined in src/init.cpp
3320
ASMAP = 'src/test/data/asmap.raw' # path to unit test skeleton asmap
3421
VERSION = 'fec61fa21a9f46f3b17bdcd660d7f4cd90b966aad3aec593c99b35f0aca15853'
3522

@@ -79,52 +66,49 @@ def test_asmap_with_relative_path(self):
7966
self.start_node(0, [f'-asmap={name}'])
8067
os.remove(filename)
8168

82-
def test_default_asmap(self):
83-
shutil.copyfile(self.asmap_raw, self.default_asmap)
69+
def test_unspecified_asmap(self):
70+
msg = f"Error: Could not find asmap file \"{self.datadir}{os.sep}\""
8471
for arg in ['-asmap', '-asmap=']:
85-
self.log.info(f'Test bitcoind {arg} (using default map file)')
72+
self.log.info(f'Test bitcoind {arg} (and no filename specified)')
8673
self.stop_node(0)
87-
with self.node.assert_debug_log(expected_messages(self.default_asmap)):
88-
self.start_node(0, [arg])
89-
os.remove(self.default_asmap)
74+
self.node.assert_start_raises_init_error(extra_args=[arg], expected_msg=msg)
9075

9176
def test_asmap_interaction_with_addrman_containing_entries(self):
9277
self.log.info("Test bitcoind -asmap restart with addrman containing new and tried entries")
9378
self.stop_node(0)
94-
shutil.copyfile(self.asmap_raw, self.default_asmap)
95-
self.start_node(0, ["-asmap", "-checkaddrman=1", "-test=addrman"])
79+
self.start_node(0, [f"-asmap={self.asmap_raw}", "-checkaddrman=1", "-test=addrman"])
9680
self.fill_addrman(node_id=0)
97-
self.restart_node(0, ["-asmap", "-checkaddrman=1", "-test=addrman"])
81+
self.restart_node(0, [f"-asmap={self.asmap_raw}", "-checkaddrman=1", "-test=addrman"])
9882
with self.node.assert_debug_log(
9983
expected_msgs=[
10084
"CheckAddrman: new 2, tried 2, total 4 started",
10185
"CheckAddrman: completed",
10286
]
10387
):
10488
self.node.getnodeaddresses() # getnodeaddresses re-runs the addrman checks
105-
os.remove(self.default_asmap)
10689

107-
def test_default_asmap_with_missing_file(self):
108-
self.log.info('Test bitcoind -asmap with missing default map file')
90+
def test_asmap_with_missing_file(self):
91+
self.log.info('Test bitcoind -asmap with missing map file')
10992
self.stop_node(0)
110-
msg = f"Error: Could not find asmap file \"{self.default_asmap}\""
111-
self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg)
93+
msg = f"Error: Could not find asmap file \"{self.datadir}{os.sep}missing\""
94+
self.node.assert_start_raises_init_error(extra_args=['-asmap=missing'], expected_msg=msg)
11295

11396
def test_empty_asmap(self):
11497
self.log.info('Test bitcoind -asmap with empty map file')
11598
self.stop_node(0)
116-
with open(self.default_asmap, "w", encoding="utf-8") as f:
99+
100+
empty_asmap = os.path.join(self.datadir, "ip_asn.map")
101+
with open(empty_asmap, "w", encoding="utf-8") as f:
117102
f.write("")
118-
msg = f"Error: Could not parse asmap file \"{self.default_asmap}\""
119-
self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg)
120-
os.remove(self.default_asmap)
103+
msg = f"Error: Could not parse asmap file \"{empty_asmap}\""
104+
self.node.assert_start_raises_init_error(extra_args=[f'-asmap={empty_asmap}'], expected_msg=msg)
105+
os.remove(empty_asmap)
121106

122107
def test_asmap_health_check(self):
123108
self.log.info('Test bitcoind -asmap logs ASMap Health Check with basic stats')
124-
shutil.copyfile(self.asmap_raw, self.default_asmap)
125109
msg = "ASMap Health Check: 4 clearnet peers are mapped to 3 ASNs with 0 peers being unmapped"
126110
with self.node.assert_debug_log(expected_msgs=[msg]):
127-
self.start_node(0, extra_args=['-asmap'])
111+
self.start_node(0, extra_args=[f'-asmap={self.asmap_raw}'])
128112
raw_addrman = self.node.getrawaddrman()
129113
asns = []
130114
for _, entries in raw_addrman.items():
@@ -133,22 +117,20 @@ def test_asmap_health_check(self):
133117
if asn not in asns:
134118
asns.append(asn)
135119
assert_equal(len(asns), 3)
136-
os.remove(self.default_asmap)
137120

138121
def run_test(self):
139122
self.node = self.nodes[0]
140123
self.datadir = self.node.chain_path
141-
self.default_asmap = os.path.join(self.datadir, DEFAULT_ASMAP_FILENAME)
142124
base_dir = self.config["environment"]["SRCDIR"]
143125
self.asmap_raw = os.path.join(base_dir, ASMAP)
144126

145127
self.test_without_asmap_arg()
146128
self.test_noasmap_arg()
147129
self.test_asmap_with_absolute_path()
148130
self.test_asmap_with_relative_path()
149-
self.test_default_asmap()
131+
self.test_unspecified_asmap()
150132
self.test_asmap_interaction_with_addrman_containing_entries()
151-
self.test_default_asmap_with_missing_file()
133+
self.test_asmap_with_missing_file()
152134
self.test_empty_asmap()
153135
self.test_asmap_health_check()
154136

0 commit comments

Comments
 (0)