From 08b992675cf8d946db19b7bea747fa1085fdb2a2 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sat, 28 Dec 2019 12:50:20 +0100 Subject: [PATCH 1/9] test: add feature_asmap functional tests to verify node behaviour and debug log when launching bitcoind in these cases: 1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing 2. `bitcoind -asmap=`, using the unit test skeleton asmap 3. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap 4. `bitcoind -asmap` with no file specified, and a missing default asmap file The tests are order-independent. The slowest test (missing default asmap file) is placed last. --- test/functional/feature_asmap.py | 83 ++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 84 insertions(+) create mode 100755 test/functional/feature_asmap.py diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py new file mode 100755 index 0000000000..a196d7cc9d --- /dev/null +++ b/test/functional/feature_asmap.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test asmap config argument for ASN-based IP bucketing. + +Verify node behaviour and debug log when launching bitcoind in these cases: + +1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing + +2. `bitcoind -asmap=`, using the unit test skeleton asmap + +3. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap + +4. `bitcoind -asmap` with no file specified, and a missing default asmap file + +The tests are order-independent. The slowest test (missing default asmap file) +is placed last. + +""" +import os +import shutil + +from test_framework.test_framework import BitcoinTestFramework + +DEFAULT_ASMAP_FILENAME = 'ip_asn.map' # defined in src/init.cpp +ASMAP = '../../src/test/data/asmap.raw' # path to unit test skeleton asmap +VERSION = 'fec61fa21a9f46f3b17bdcd660d7f4cd90b966aad3aec593c99b35f0aca15853' + +def expected_messages(filename): + return ['Opened asmap file "{}" (59 bytes) from disk.'.format(filename), + 'Using asmap version {} for IP bucketing.'.format(VERSION)] + +class AsmapTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def test_without_asmap_arg(self): + self.log.info('Test bitcoind with no -asmap arg passed') + self.stop_node(0) + with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']): + self.start_node(0) + + def test_asmap_with_relative_path(self): + self.log.info('Test bitcoind -asmap=') + self.stop_node(0) + name = 'ASN_map' + filename = os.path.join(self.datadir, name) + shutil.copyfile(self.asmap_raw, filename) + with self.node.assert_debug_log(expected_messages(filename)): + self.start_node(0, ['-asmap={}'.format(name)]) + os.remove(filename) + + def test_default_asmap(self): + shutil.copyfile(self.asmap_raw, self.default_asmap) + for arg in ['-asmap', '-asmap=']: + self.log.info('Test bitcoind {} (using default map file)'.format(arg)) + self.stop_node(0) + with self.node.assert_debug_log(expected_messages(self.default_asmap)): + self.start_node(0, [arg]) + os.remove(self.default_asmap) + + def test_default_asmap_with_missing_file(self): + self.log.info('Test bitcoind -asmap with missing default map file') + self.stop_node(0) + msg = "Error: Could not find or parse specified asmap: '\"{}\"'".format(self.default_asmap) + self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) + + def run_test(self): + self.node = self.nodes[0] + self.datadir = os.path.join(self.node.datadir, self.chain) + self.default_asmap = os.path.join(self.datadir, DEFAULT_ASMAP_FILENAME) + self.asmap_raw = os.path.join(os.path.dirname(os.path.realpath(__file__)), ASMAP) + + self.test_without_asmap_arg() + self.test_asmap_with_relative_path() + self.test_default_asmap() + self.test_default_asmap_with_missing_file() + + +if __name__ == '__main__': + AsmapTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 06d939afb7..ac746ea9bf 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -101,6 +101,7 @@ BASE_SCRIPTS = [ 'rpc_txoutproof.py', 'wallet_listreceivedby.py', 'wallet_abandonconflict.py', + 'feature_asmap.py', 'feature_csv_activation.py', 'rpc_rawtransaction.py', 'wallet_address_types.py', From fbe9b024f01c29153afe494fed74b623ce3ffefa Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 29 Dec 2019 10:43:39 +0100 Subject: [PATCH 2/9] config: use default value in -asmap config and move to sorted position --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 1c9faec803..5b312af978 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -408,6 +408,7 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-addnode=", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + gArgs.AddArg("-asmap=", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Path should be relative to the -datadir path.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-banscore=", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); @@ -436,7 +437,6 @@ void SetupServerArgs() gArgs.AddArg("-peertimeout=", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); gArgs.AddArg("-torcontrol=:", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-torpassword=", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION); - gArgs.AddArg("-asmap=", "Specify asn mapping used for bucketing of the peers. Path should be relative to the -datadir path.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); From 81c38a24975f34e5894efe3d1aaf45ff6a8cee4a Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 29 Dec 2019 18:45:59 +0100 Subject: [PATCH 3/9] config: enable passing -asmap an absolute file path - allow passing an absolute file path to the -asmap config arg - update the -asmap config help - add a functional test in feature_asmap.py --- src/init.cpp | 12 +++++++----- test/functional/feature_asmap.py | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 5b312af978..222e33c1a7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -408,7 +408,7 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-addnode=", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); - gArgs.AddArg("-asmap=", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Path should be relative to the -datadir path.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + gArgs.AddArg("-asmap=", 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); gArgs.AddArg("-banscore=", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); @@ -1827,11 +1827,13 @@ bool AppInitMain(NodeContext& node) // Read asmap file if configured if (gArgs.IsArgSet("-asmap")) { - std::string asmap_file = gArgs.GetArg("-asmap", ""); - if (asmap_file.empty()) { - asmap_file = DEFAULT_ASMAP_FILENAME; + fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", "")); + if (asmap_path.empty()) { + asmap_path = DEFAULT_ASMAP_FILENAME; + } + if (!asmap_path.is_absolute()) { + asmap_path = GetDataDir() / asmap_path; } - const fs::path asmap_path = GetDataDir() / asmap_file; std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); if (asmap.size() == 0) { InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path)); diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index a196d7cc9d..07dde84775 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -8,11 +8,13 @@ Verify node behaviour and debug log when launching bitcoind in these cases: 1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing -2. `bitcoind -asmap=`, using the unit test skeleton asmap +2. `bitcoind -asmap=`, using the unit test skeleton asmap -3. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap +3. `bitcoind -asmap=`, using the unit test skeleton asmap -4. `bitcoind -asmap` with no file specified, and a missing default asmap file +4. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap + +5. `bitcoind -asmap` with no file specified and a missing default asmap file The tests are order-independent. The slowest test (missing default asmap file) is placed last. @@ -42,6 +44,15 @@ class AsmapTest(BitcoinTestFramework): with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']): self.start_node(0) + def test_asmap_with_absolute_path(self): + self.log.info('Test bitcoind -asmap=') + self.stop_node(0) + filename = os.path.join(self.datadir, 'my-map-file.map') + shutil.copyfile(self.asmap_raw, filename) + with self.node.assert_debug_log(expected_messages(filename)): + self.start_node(0, ['-asmap={}'.format(filename)]) + os.remove(filename) + def test_asmap_with_relative_path(self): self.log.info('Test bitcoind -asmap=') self.stop_node(0) @@ -74,6 +85,7 @@ class AsmapTest(BitcoinTestFramework): self.asmap_raw = os.path.join(os.path.dirname(os.path.realpath(__file__)), ASMAP) self.test_without_asmap_arg() + self.test_asmap_with_absolute_path() self.test_asmap_with_relative_path() self.test_default_asmap() self.test_default_asmap_with_missing_file() From b8d0412b213df18f23bf8677ab94068c6cca9f51 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 29 Dec 2019 18:52:10 +0100 Subject: [PATCH 4/9] config: separate the asmap finding and parsing checks and update the tests. --- src/init.cpp | 6 +++++- test/functional/feature_asmap.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 222e33c1a7..0f56f3881c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1834,9 +1834,13 @@ bool AppInitMain(NodeContext& node) if (!asmap_path.is_absolute()) { asmap_path = GetDataDir() / asmap_path; } + if (!fs::exists(asmap_path)) { + InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); + return false; + } std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); if (asmap.size() == 0) { - InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path)); + InitError(strprintf(_("Could not parse asmap file '%s'").translated, asmap_path)); return false; } const uint256 asmap_version = SerializeHash(asmap); diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index 07dde84775..6b9d90960a 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -75,7 +75,7 @@ class AsmapTest(BitcoinTestFramework): def test_default_asmap_with_missing_file(self): self.log.info('Test bitcoind -asmap with missing default map file') self.stop_node(0) - msg = "Error: Could not find or parse specified asmap: '\"{}\"'".format(self.default_asmap) + msg = "Error: Could not find asmap file '\"{}\"'".format(self.default_asmap) self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) def run_test(self): From dcaf543ba0241f9219cea70b67c7b066d4c9ca9b Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 29 Dec 2019 19:51:04 +0100 Subject: [PATCH 5/9] test: add functional test for an empty, unparsable asmap This is now testable after separating the asmap finding and parsing checks in the previous commit. --- test/functional/feature_asmap.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index 6b9d90960a..8f5a77c92d 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -16,8 +16,10 @@ Verify node behaviour and debug log when launching bitcoind in these cases: 5. `bitcoind -asmap` with no file specified and a missing default asmap file -The tests are order-independent. The slowest test (missing default asmap file) -is placed last. +6. `bitcoind -asmap` with an empty (unparsable) default asmap file + +The tests are order-independent. The slowest tests (missing default asmap and +empty asmap) are placed last. """ import os @@ -78,6 +80,15 @@ class AsmapTest(BitcoinTestFramework): msg = "Error: Could not find asmap file '\"{}\"'".format(self.default_asmap) self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) + def test_empty_asmap(self): + self.log.info('Test bitcoind -asmap with empty map file') + self.stop_node(0) + with open(self.default_asmap, "w", encoding="utf-8") as f: + f.write("") + msg = "Error: Could not parse asmap file \"{}\"".format(self.default_asmap) + self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) + os.remove(self.default_asmap) + def run_test(self): self.node = self.nodes[0] self.datadir = os.path.join(self.node.datadir, self.chain) @@ -89,6 +100,7 @@ class AsmapTest(BitcoinTestFramework): self.test_asmap_with_relative_path() self.test_default_asmap() self.test_default_asmap_with_missing_file() + self.test_empty_asmap() if __name__ == '__main__': From 819fb5549b0d02477f47b3c40338071f37b6d885 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 29 Dec 2019 18:54:33 +0100 Subject: [PATCH 6/9] logging: asmap logging and #include fixups - move asmap #includes to sorted positions in addrman and init (move-only) - remove redundant quotes in asmap InitError, update test - remove full stops from asmap logging to be consistent with debug logging, update tests --- src/addrman.cpp | 10 +++++----- src/addrman.h | 11 +++++------ src/init.cpp | 8 ++++---- test/functional/feature_asmap.py | 6 +++--- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index 121ae4bf7e..2f8a3a0bd5 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -6,8 +6,8 @@ #include #include -#include #include +#include int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector &asmap) const { @@ -15,7 +15,7 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector &asma uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash(); int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT; uint32_t mapped_as = GetMappedAS(asmap); - LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i.\n", ToStringIP(), mapped_as, tried_bucket); + LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket); return tried_bucket; } @@ -26,7 +26,7 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std: uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash(); int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT; uint32_t mapped_as = GetMappedAS(asmap); - LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i.\n", ToStringIP(), mapped_as, new_bucket); + LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket); return new_bucket; } @@ -630,12 +630,12 @@ std::vector CAddrMan::DecodeAsmap(fs::path path) FILE *filestr = fsbridge::fopen(path, "rb"); CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { - LogPrintf("Failed to open asmap file from disk.\n"); + LogPrintf("Failed to open asmap file from disk\n"); return bits; } fseek(filestr, 0, SEEK_END); int length = ftell(filestr); - LogPrintf("Opened asmap file %s (%d bytes) from disk.\n", path, length); + LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length); fseek(filestr, 0, SEEK_SET); char cur_byte; for (int i = 0; i < length; ++i) { diff --git a/src/addrman.h b/src/addrman.h index 5901611bee..8e82020df0 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -6,23 +6,22 @@ #ifndef BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H +#include #include #include #include #include #include #include -#include +#include +#include +#include #include #include #include -#include -#include #include -#include -#include - +#include /** * Extended statistics about a CAddress diff --git a/src/init.cpp b/src/init.cpp index 0f56f3881c..d7a44d1d4c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -47,11 +47,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -1840,14 +1840,14 @@ bool AppInitMain(NodeContext& node) } std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); if (asmap.size() == 0) { - InitError(strprintf(_("Could not parse asmap file '%s'").translated, asmap_path)); + InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); return false; } const uint256 asmap_version = SerializeHash(asmap); node.connman->SetAsmap(std::move(asmap)); - LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString()); + LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString()); } else { - LogPrintf("Using /16 prefix for IP bucketing.\n"); + LogPrintf("Using /16 prefix for IP bucketing\n"); } // ********************************************************* Step 13: finished diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index 8f5a77c92d..dd12633f50 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -32,8 +32,8 @@ ASMAP = '../../src/test/data/asmap.raw' # path to unit test skeleton asmap VERSION = 'fec61fa21a9f46f3b17bdcd660d7f4cd90b966aad3aec593c99b35f0aca15853' def expected_messages(filename): - return ['Opened asmap file "{}" (59 bytes) from disk.'.format(filename), - 'Using asmap version {} for IP bucketing.'.format(VERSION)] + return ['Opened asmap file "{}" (59 bytes) from disk'.format(filename), + 'Using asmap version {} for IP bucketing'.format(VERSION)] class AsmapTest(BitcoinTestFramework): def set_test_params(self): @@ -77,7 +77,7 @@ class AsmapTest(BitcoinTestFramework): def test_default_asmap_with_missing_file(self): self.log.info('Test bitcoind -asmap with missing default map file') self.stop_node(0) - msg = "Error: Could not find asmap file '\"{}\"'".format(self.default_asmap) + msg = "Error: Could not find asmap file \"{}\"".format(self.default_asmap) self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) def test_empty_asmap(self): From c90b9a2399f4cead37bad39f388ce1255e123dc4 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Wed, 5 Feb 2020 15:49:33 +0100 Subject: [PATCH 7/9] net: extract conditional to bool CNetAddr::IsHeNet and remove redundant public declaration --- src/netaddress.cpp | 7 ++++++- src/netaddress.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 1cac57a817..228caf74a9 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -210,6 +210,11 @@ bool CNetAddr::IsRFC7343() const return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20); } +bool CNetAddr::IsHeNet() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70); +} + /** * @returns Whether or not this is a dummy address that maps an onion address * into IPv6. @@ -516,7 +521,7 @@ std::vector CNetAddr::GetGroup(const std::vector &asmap) co } else if (IsTor()) { nStartByte = 6; nBits = 4; - } else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) { + } else if (IsHeNet()) { // for he.net, use /36 groups nBits = 36; } else { diff --git a/src/netaddress.h b/src/netaddress.h index b300b709f3..b7381c1eb4 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -45,7 +45,6 @@ class CNetAddr */ void SetRaw(Network network, const uint8_t *data); - public: bool SetInternal(const std::string& name); bool SetSpecial(const std::string &strName); // for Tor addresses @@ -66,6 +65,7 @@ class CNetAddr bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96) bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765) + bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36) bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; From 5ba829e12e99f119df56cab422f827b9be03fe57 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Wed, 29 Jan 2020 16:39:00 +0100 Subject: [PATCH 8/9] rpc: fix getpeerinfo RPCResult `mapped_as` type and mention it is only available if the asmap config flag is set. --- src/rpc/net.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 482c689285..085921095e 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -86,7 +86,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"}, {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"}, - {RPCResult::Type::STR, "mapped_as", "The AS in the BGP route to the peer used for diversifying peer selection"}, + {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n" + "peer selection (only available if the asmap config flag is set)"}, {RPCResult::Type::STR_HEX, "services", "The services offered"}, {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", { From 1ba3e1cc21150abe632a5b82a1a38998b33815dc Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Wed, 29 Jan 2020 16:38:25 +0100 Subject: [PATCH 9/9] init: move asmap code earlier in init process and update feature_asmap.py and test_runner.py This commit moves the asmap init.cpp code from the end of "Step 12: start node" to "Step 6: network initialization" to provide feedback on passing an -asmap config arg much more quickly. This change speeds up the feature_asmap.py functional test file from 60 to 5 seconds by accelerating the 2 tests that use `assert_start_raises_init_error`. Credit to Wladimir J. van der Laan for the suggestion. --- src/init.cpp | 50 ++++++++++++++++---------------- test/functional/feature_asmap.py | 3 +- test/functional/test_runner.py | 2 +- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index d7a44d1d4c..14d489617c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1418,6 +1418,31 @@ bool AppInitMain(NodeContext& node) return InitError(ResolveErrMsg("externalip", strAddr)); } + // Read asmap file if configured + if (gArgs.IsArgSet("-asmap")) { + fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", "")); + if (asmap_path.empty()) { + asmap_path = DEFAULT_ASMAP_FILENAME; + } + if (!asmap_path.is_absolute()) { + asmap_path = GetDataDir() / asmap_path; + } + if (!fs::exists(asmap_path)) { + InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); + return false; + } + std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); + if (asmap.size() == 0) { + InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); + return false; + } + const uint256 asmap_version = SerializeHash(asmap); + node.connman->SetAsmap(std::move(asmap)); + LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString()); + } else { + LogPrintf("Using /16 prefix for IP bucketing\n"); + } + #if ENABLE_ZMQ g_zmq_notification_interface = CZMQNotificationInterface::Create(); @@ -1825,31 +1850,6 @@ bool AppInitMain(NodeContext& node) return false; } - // Read asmap file if configured - if (gArgs.IsArgSet("-asmap")) { - fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", "")); - if (asmap_path.empty()) { - asmap_path = DEFAULT_ASMAP_FILENAME; - } - if (!asmap_path.is_absolute()) { - asmap_path = GetDataDir() / asmap_path; - } - if (!fs::exists(asmap_path)) { - InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); - return false; - } - std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); - if (asmap.size() == 0) { - InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); - return false; - } - const uint256 asmap_version = SerializeHash(asmap); - node.connman->SetAsmap(std::move(asmap)); - LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString()); - } else { - LogPrintf("Using /16 prefix for IP bucketing\n"); - } - // ********************************************************* Step 13: finished SetRPCWarmupFinished(); diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index dd12633f50..2c6553fbe2 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -18,8 +18,7 @@ Verify node behaviour and debug log when launching bitcoind in these cases: 6. `bitcoind -asmap` with an empty (unparsable) default asmap file -The tests are order-independent. The slowest tests (missing default asmap and -empty asmap) are placed last. +The tests are order-independent. """ import os diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index ac746ea9bf..2036d20852 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -101,7 +101,6 @@ BASE_SCRIPTS = [ 'rpc_txoutproof.py', 'wallet_listreceivedby.py', 'wallet_abandonconflict.py', - 'feature_asmap.py', 'feature_csv_activation.py', 'rpc_rawtransaction.py', 'wallet_address_types.py', @@ -207,6 +206,7 @@ BASE_SCRIPTS = [ 'p2p_dos_header_tree.py', 'p2p_unrequested_blocks.py', 'feature_includeconf.py', + 'feature_asmap.py', 'rpc_deriveaddresses.py', 'rpc_deriveaddresses.py --usecli', 'rpc_scantxoutset.py',