Merge bitcoin/bitcoin#28792: build: Embedded ASMap [3/3]: Build binary dump header file

24699fec84 doc: Add initial asmap data documentation (Fabian Jahr)
bab085d282 ci: Use without embedded asmap build option in one ci job (Fabian Jahr)
e53934422a doc: Expand documentation on asmap feature and tooling (Fabian Jahr)
6244212a55 init, net: Implement usage of binary-embedded asmap data (Fabian Jahr)
6202b50fb9 build: Generate ip_asn.dat.h during build process (Fabian Jahr)
634cd60dc8 build: Add embedded asmap data (Fabian Jahr)

Pull request description:

  This is the final in a series of PRs that implement the necessary changes for embedding of asmap data into the binary. This last part add the initial asmap data, implements the build changes and adds further documentation.

  Currently an asmap file needs to be acquired by there user from some location or the user needs to generate one themselves. Then they need to move the file to the right place in datadir or pass the path to the file as `-asmap=PATH` in order to use the asmap feature. The change here allows for builds to embed asmap data into the bitcoind binary which makes it possible to use the feature without handling of the asmap file by the user. If the user starts bitcoind with `-asmap` the embedded data will be used for bucketing of nodes.

  The data lives in the repository at `src/node/data/ip_asn.dat` and can be replaced with a new version at any time. The idea is that the data should be updated with every release. By default the data at that location is embedded into the binary but there is also a build option to prevent this (`-DWITH_EMBEDDED_ASMAP=OFF`). In this case the original behavior of the `-asmap` option is maintained.

ACKs for top commit:
  achow101:
    ACK 24699fec84
  sipa:
    ACK 24699fec84
  hodlinator:
    ACK 24699fec84

Tree-SHA512: c2e33dbeea387efdfd3d415432bf8fa64de80f272c1207015ea53b85bb77f5c29f1dae5644513a23c844a98fb0a4bb257bf765f38b15bfc4c41984f0315b4c6a
This commit is contained in:
Ava Chow
2026-02-18 16:43:34 -08:00
13 changed files with 147 additions and 29 deletions

View File

@@ -119,6 +119,10 @@
#include <zmq/zmqrpc.h>
#endif
#ifdef ENABLE_EMBEDDED_ASMAP
#include <node/data/ip_asn.dat.h>
#endif
using common::AmountErrMsg;
using common::InvalidPortErrMsg;
using common::ResolveErrMsg;
@@ -530,7 +534,13 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
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);
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);
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers. Relative paths will be prefixed by the net-specific datadir location.%s",
#ifdef ENABLE_EMBEDDED_ASMAP
" If a bool arg is given (-asmap or -asmap=1), the embedded mapping data in the binary will be used."
#else
""
#endif
), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
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);
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);
@@ -1560,29 +1570,50 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ApplyArgsManOptions(args, peerman_opts);
{
// Read asmap file if configured and initialize
// Read asmap file if configured or embedded asmap data and initialize
// Netgroupman with or without it
assert(!node.netgroupman);
if (args.IsArgSet("-asmap") && !args.IsArgNegated("-asmap")) {
fs::path asmap_path = args.GetPathArg("-asmap");
if (asmap_path.empty()) {
InitError(_("-asmap requires a file path. Use -asmap=<file>."));
return false;
uint256 asmap_version{};
if (!args.GetBoolArg("-asmap", false)) {
fs::path asmap_path = args.GetPathArg("-asmap");
if (!asmap_path.is_absolute()) {
asmap_path = args.GetDataDirNet() / asmap_path;
}
// If a specific path was passed with the asmap argument check if
// the file actually exists in that location
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
// If a file exists at the path, try to read the file
std::vector<std::byte> asmap{DecodeAsmap(asmap_path)};
if (asmap.empty()) {
InitError(strprintf(_("Could not parse asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
asmap_version = AsmapVersion(asmap);
node.netgroupman = std::make_unique<NetGroupManager>(NetGroupManager::WithLoadedAsmap(std::move(asmap)));
} else {
#ifdef ENABLE_EMBEDDED_ASMAP
// Use the embedded asmap data
std::span<const std::byte> asmap{node::data::ip_asn};
if (asmap.empty() || !CheckStandardAsmap(asmap)) {
InitError(strprintf(_("Could not read embedded asmap data")));
return false;
}
node.netgroupman = std::make_unique<NetGroupManager>(NetGroupManager::WithEmbeddedAsmap(asmap));
asmap_version = AsmapVersion(asmap);
LogInfo("Opened asmap data (%zu bytes) from embedded byte array\n", asmap.size());
#else
// If there is no embedded data, fail and report it since
// the user tried to use it
InitError(strprintf(_("Embedded asmap data not available")));
return false;
#endif
}
if (!asmap_path.is_absolute()) {
asmap_path = args.GetDataDirNet() / asmap_path;
}
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
std::vector<std::byte> asmap{DecodeAsmap(asmap_path)};
if (asmap.size() == 0) {
InitError(strprintf(_("Could not parse asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
const uint256 asmap_version = AsmapVersion(asmap);
node.netgroupman = std::make_unique<NetGroupManager>(NetGroupManager::WithLoadedAsmap(std::move(asmap)));
LogInfo("Using asmap version %s for IP bucketing", asmap_version.ToString());
} else {
node.netgroupman = std::make_unique<NetGroupManager>(NetGroupManager::NoAsmap());