Skip to content

Commit 0690514

Browse files
committed
Merge bitcoin/bitcoin#33770: init: Require explicit -asmap filename
288b8c3 doc: Drop (default: none) from -i2psam description (Ryan Ofsky) f6ec351 init: Require explicit -asmap filename (Ryan Ofsky) Pull request description: 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 and avoid confusion reported bitcoin/bitcoin#33386 where documentation specifies a default file which is not actually loaded by default. It was originally implemented in bitcoin/bitcoin#33631 (comment) and various alternatives are discussed there. ACKs for top commit: brunoerg: reACK 288b8c3 fjahr: re-ACK 288b8c3 vostrnad: utACK 288b8c3 achow101: ACK 288b8c3 Tree-SHA512: 11a38a03892a58d6ccc1505cfbf915f58a86df9891761d89dc54b92d40593ee3cbb2d7c7bdbb922b871b3529072ef7f34cc98393aff6e8f0633b56352315b27c
2 parents b2f88b5 + 288b8c3 commit 0690514

4 files changed

Lines changed: 32 additions & 45 deletions

File tree

doc/files.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ Subdirectory | File(s) | Description
6666
`./` | `debug.log` | Contains debug information and general logging generated by `bitcoind` or `bitcoin-qt`; can be specified by `-debuglogfile` option
6767
`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees required for confirmation
6868
`./` | `guisettings.ini.bak` | Backup of former [GUI settings](#gui-settings) after `-resetguisettings` option is used
69-
`./` | `ip_asn.map` | IP addresses to Autonomous System Numbers (ASNs) mapping used for bucketing of the peers; path can be specified with the `-asmap` option
7069
`./` | `mempool.dat` | Dump of the mempool's transactions
7170
`./` | `onion_v3_private_key` | Cached Tor onion service private key for `-listenonion` option
7271
`./` | `i2p_private_key` | Private key that corresponds to our I2P address. When `-i2psam=` is specified the contents of this file is used to identify ourselves for making outgoing connections to I2P peers and possibly accepting incoming ones. Automatically generated if it does not exist.

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: 7 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.
@@ -530,7 +529,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
530529
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
531530

532531
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);
533-
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);
532+
argsman.AddArg("-asmap=<file>", "Specify asn mapping used for bucketing of the peers. Relative paths will be prefixed by the net-specific datadir location.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
534533
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
535534
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);
536535
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);
@@ -552,7 +551,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
552551
#else
553552
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
554553
#endif
555-
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
554+
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
556555
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
557556
argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
558557
argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1541,7 +1540,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15411540
// Read asmap file if configured
15421541
std::vector<bool> asmap;
15431542
if (args.IsArgSet("-asmap") && !args.IsArgNegated("-asmap")) {
1544-
fs::path asmap_path = args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
1543+
fs::path asmap_path = args.GetPathArg("-asmap");
1544+
if (asmap_path.empty()) {
1545+
InitError(_("-asmap requires a file path. Use -asmap=<file>."));
1546+
return false;
1547+
}
15451548
if (!asmap_path.is_absolute()) {
15461549
asmap_path = args.GetDataDirNet() / asmap_path;
15471550
}

test/functional/feature_asmap.py

Lines changed: 21 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,48 @@ 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 = "Error: -asmap requires a file path. Use -asmap=<file>."
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+
empty_asmap = os.path.join(self.datadir, "ip_asn.map")
100+
with open(empty_asmap, "w", encoding="utf-8") as f:
117101
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)
102+
msg = f"Error: Could not parse asmap file \"{empty_asmap}\""
103+
self.node.assert_start_raises_init_error(extra_args=[f'-asmap={empty_asmap}'], expected_msg=msg)
104+
os.remove(empty_asmap)
121105

122106
def test_asmap_health_check(self):
123107
self.log.info('Test bitcoind -asmap logs ASMap Health Check with basic stats')
124-
shutil.copyfile(self.asmap_raw, self.default_asmap)
125108
msg = "ASMap Health Check: 4 clearnet peers are mapped to 3 ASNs with 0 peers being unmapped"
126109
with self.node.assert_debug_log(expected_msgs=[msg]):
127-
self.start_node(0, extra_args=['-asmap'])
110+
self.start_node(0, extra_args=[f'-asmap={self.asmap_raw}'])
128111
raw_addrman = self.node.getrawaddrman()
129112
asns = []
130113
for _, entries in raw_addrman.items():
@@ -133,22 +116,20 @@ def test_asmap_health_check(self):
133116
if asn not in asns:
134117
asns.append(asn)
135118
assert_equal(len(asns), 3)
136-
os.remove(self.default_asmap)
137119

138120
def run_test(self):
139121
self.node = self.nodes[0]
140122
self.datadir = self.node.chain_path
141-
self.default_asmap = os.path.join(self.datadir, DEFAULT_ASMAP_FILENAME)
142123
base_dir = self.config["environment"]["SRCDIR"]
143124
self.asmap_raw = os.path.join(base_dir, ASMAP)
144125

145126
self.test_without_asmap_arg()
146127
self.test_noasmap_arg()
147128
self.test_asmap_with_absolute_path()
148129
self.test_asmap_with_relative_path()
149-
self.test_default_asmap()
130+
self.test_unspecified_asmap()
150131
self.test_asmap_interaction_with_addrman_containing_entries()
151-
self.test_default_asmap_with_missing_file()
132+
self.test_asmap_with_missing_file()
152133
self.test_empty_asmap()
153134
self.test_asmap_health_check()
154135

0 commit comments

Comments
 (0)