mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
net: CAddress & CAddrMan: (un)serialize as ADDRv2
Change the serialization of `CAddrMan` to serialize its addresses in ADDRv2/BIP155 format by default. Introduce a new `CAddrMan` format version (3). Add support for ADDRv2 format in `CAddress` (un)serialization. Co-authored-by: Carl Dong <contact@carldong.me>
This commit is contained in:
parent
1d3ec2a1fd
commit
201a4596d9
5 changed files with 170 additions and 16 deletions
|
@ -60,6 +60,14 @@ From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no
|
||||||
longer supported. Additionally, Bitcoin Core does not yet change appearance
|
longer supported. Additionally, Bitcoin Core does not yet change appearance
|
||||||
when macOS "dark mode" is activated.
|
when macOS "dark mode" is activated.
|
||||||
|
|
||||||
|
The node's known peers are persisted to disk in a file called `peers.dat`. The
|
||||||
|
format of this file has been changed in a backwards-incompatible way in order to
|
||||||
|
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
|
||||||
|
the file is modified by 0.21.0 or newer then older versions will not be able to
|
||||||
|
read it. Those old versions, in the event of a downgrade, will log an error
|
||||||
|
message that deserialization has failed and will continue normal operation
|
||||||
|
as if the file was missing, creating a new empty one. (#19954)
|
||||||
|
|
||||||
Notable changes
|
Notable changes
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <random.h>
|
#include <random.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
|
@ -264,6 +265,14 @@ protected:
|
||||||
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
//! Serialization versions.
|
||||||
|
enum class Format : uint8_t {
|
||||||
|
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
|
||||||
|
V1_DETERMINISTIC = 1, //!< for pre-asmap files
|
||||||
|
V2_ASMAP = 2, //!< for files including asmap version
|
||||||
|
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
|
||||||
|
};
|
||||||
|
|
||||||
// Compressed IP->ASN mapping, loaded from a file when a node starts.
|
// Compressed IP->ASN mapping, loaded from a file when a node starts.
|
||||||
// Should be always empty if no file was provided.
|
// Should be always empty if no file was provided.
|
||||||
// This mapping is then used for bucketing nodes in Addrman.
|
// This mapping is then used for bucketing nodes in Addrman.
|
||||||
|
@ -285,8 +294,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* serialized format:
|
* Serialized format.
|
||||||
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
|
* * version byte (@see `Format`)
|
||||||
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
||||||
* * nNew
|
* * nNew
|
||||||
* * nTried
|
* * nTried
|
||||||
|
@ -313,13 +322,16 @@ public:
|
||||||
* We don't use SERIALIZE_METHODS since the serialization and deserialization code has
|
* We don't use SERIALIZE_METHODS since the serialization and deserialization code has
|
||||||
* very little in common.
|
* very little in common.
|
||||||
*/
|
*/
|
||||||
template<typename Stream>
|
template <typename Stream>
|
||||||
void Serialize(Stream &s) const
|
void Serialize(Stream& s_) const
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
||||||
unsigned char nVersion = 2;
|
// Always serialize in the latest version (currently Format::V3_BIP155).
|
||||||
s << nVersion;
|
|
||||||
|
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
|
||||||
|
|
||||||
|
s << static_cast<uint8_t>(Format::V3_BIP155);
|
||||||
s << ((unsigned char)32);
|
s << ((unsigned char)32);
|
||||||
s << nKey;
|
s << nKey;
|
||||||
s << nNew;
|
s << nNew;
|
||||||
|
@ -370,14 +382,34 @@ public:
|
||||||
s << asmap_version;
|
s << asmap_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template <typename Stream>
|
||||||
void Unserialize(Stream& s)
|
void Unserialize(Stream& s_)
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
unsigned char nVersion;
|
|
||||||
s >> nVersion;
|
Format format;
|
||||||
|
s_ >> Using<CustomUintFormatter<1>>(format);
|
||||||
|
|
||||||
|
static constexpr Format maximum_supported_format = Format::V3_BIP155;
|
||||||
|
if (format > maximum_supported_format) {
|
||||||
|
throw std::ios_base::failure(strprintf(
|
||||||
|
"Unsupported format of addrman database: %u. Maximum supported is %u. "
|
||||||
|
"Continuing operation without using the saved list of peers.",
|
||||||
|
static_cast<uint8_t>(format),
|
||||||
|
static_cast<uint8_t>(maximum_supported_format)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int stream_version = s_.GetVersion();
|
||||||
|
if (format >= Format::V3_BIP155) {
|
||||||
|
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
|
||||||
|
// unserialize methods know that an address in addrv2 format is coming.
|
||||||
|
stream_version |= ADDRV2_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
|
||||||
|
|
||||||
unsigned char nKeySize;
|
unsigned char nKeySize;
|
||||||
s >> nKeySize;
|
s >> nKeySize;
|
||||||
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
||||||
|
@ -386,7 +418,7 @@ public:
|
||||||
s >> nTried;
|
s >> nTried;
|
||||||
int nUBuckets = 0;
|
int nUBuckets = 0;
|
||||||
s >> nUBuckets;
|
s >> nUBuckets;
|
||||||
if (nVersion != 0) {
|
if (format >= Format::V1_DETERMINISTIC) {
|
||||||
nUBuckets ^= (1 << 30);
|
nUBuckets ^= (1 << 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +481,7 @@ public:
|
||||||
supplied_asmap_version = SerializeHash(m_asmap);
|
supplied_asmap_version = SerializeHash(m_asmap);
|
||||||
}
|
}
|
||||||
uint256 serialized_asmap_version;
|
uint256 serialized_asmap_version;
|
||||||
if (nVersion > 1) {
|
if (format >= Format::V2_ASMAP) {
|
||||||
s >> serialized_asmap_version;
|
s >> serialized_asmap_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,13 +489,13 @@ public:
|
||||||
CAddrInfo &info = mapInfo[n];
|
CAddrInfo &info = mapInfo[n];
|
||||||
int bucket = entryToBucket[n];
|
int bucket = entryToBucket[n];
|
||||||
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
||||||
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
|
if (format >= Format::V2_ASMAP && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
|
||||||
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
|
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
|
||||||
// Bucketing has not changed, using existing bucket positions for the new table
|
// Bucketing has not changed, using existing bucket positions for the new table
|
||||||
vvNew[bucket][nUBucketPos] = n;
|
vvNew[bucket][nUBucketPos] = n;
|
||||||
info.nRefCount++;
|
info.nRefCount++;
|
||||||
} else {
|
} else {
|
||||||
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
|
// In case the new table data cannot be used (format unknown, bucket count wrong or new asmap),
|
||||||
// try to give them a reference based on their primary source address.
|
// try to give them a reference based on their primary source address.
|
||||||
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
||||||
bucket = info.GetNewBucket(nKey, m_asmap);
|
bucket = info.GetNewBucket(nKey, m_asmap);
|
||||||
|
|
|
@ -351,7 +351,8 @@ class CAddress : public CService
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAddress() : CService{} {};
|
CAddress() : CService{} {};
|
||||||
explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
|
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
|
||||||
|
CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nTime{nTimeIn}, nServices{nServicesIn} {};
|
||||||
|
|
||||||
SERIALIZE_METHODS(CAddress, obj)
|
SERIALIZE_METHODS(CAddress, obj)
|
||||||
{
|
{
|
||||||
|
@ -370,7 +371,14 @@ public:
|
||||||
// nTime.
|
// nTime.
|
||||||
READWRITE(obj.nTime);
|
READWRITE(obj.nTime);
|
||||||
}
|
}
|
||||||
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
|
if (nVersion & ADDRV2_FORMAT) {
|
||||||
|
uint64_t services_tmp;
|
||||||
|
SER_WRITE(obj, services_tmp = obj.nServices);
|
||||||
|
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
|
||||||
|
SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
|
||||||
|
} else {
|
||||||
|
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
|
||||||
|
}
|
||||||
READWRITEAS(CService, obj);
|
READWRITEAS(CService, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ public:
|
||||||
int GetVersion() const { return nVersion; }
|
int GetVersion() const { return nVersion; }
|
||||||
int GetType() const { return nType; }
|
int GetType() const { return nType; }
|
||||||
size_t size() const { return stream->size(); }
|
size_t size() const { return stream->size(); }
|
||||||
|
void ignore(size_t size) { return stream->ignore(size); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Minimal stream for overwriting and/or appending to an existing byte vector
|
/* Minimal stream for overwriting and/or appending to an existing byte vector
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
|
|
||||||
#include <net_permissions.h>
|
#include <net_permissions.h>
|
||||||
#include <netbase.h>
|
#include <netbase.h>
|
||||||
|
#include <protocol.h>
|
||||||
|
#include <serialize.h>
|
||||||
|
#include <streams.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
|
#include <version.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -443,4 +447,105 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
|
||||||
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
|
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
|
||||||
|
// try a few edge cases for port, service flags and time.
|
||||||
|
|
||||||
|
static const std::vector<CAddress> fixture_addresses({
|
||||||
|
CAddress(
|
||||||
|
CService(CNetAddr(in6addr_loopback), 0 /* port */),
|
||||||
|
NODE_NONE,
|
||||||
|
0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */
|
||||||
|
),
|
||||||
|
CAddress(
|
||||||
|
CService(CNetAddr(in6addr_loopback), 0x00f1 /* port */),
|
||||||
|
NODE_NETWORK,
|
||||||
|
0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */
|
||||||
|
),
|
||||||
|
CAddress(
|
||||||
|
CService(CNetAddr(in6addr_loopback), 0xf1f2 /* port */),
|
||||||
|
static_cast<ServiceFlags>(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED),
|
||||||
|
0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// fixture_addresses should equal to this when serialized in V1 format.
|
||||||
|
// When this is unserialized from V1 format it should equal to fixture_addresses.
|
||||||
|
static constexpr const char* stream_addrv1_hex =
|
||||||
|
"03" // number of entries
|
||||||
|
|
||||||
|
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
|
||||||
|
"0000000000000000" // service flags, NODE_NONE
|
||||||
|
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
|
||||||
|
"0000" // port
|
||||||
|
|
||||||
|
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
|
||||||
|
"0100000000000000" // service flags, NODE_NETWORK
|
||||||
|
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
|
||||||
|
"00f1" // port
|
||||||
|
|
||||||
|
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
|
||||||
|
"4804000000000000" // service flags, NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED
|
||||||
|
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
|
||||||
|
"f1f2"; // port
|
||||||
|
|
||||||
|
// fixture_addresses should equal to this when serialized in V2 format.
|
||||||
|
// When this is unserialized from V2 format it should equal to fixture_addresses.
|
||||||
|
static constexpr const char* stream_addrv2_hex =
|
||||||
|
"03" // number of entries
|
||||||
|
|
||||||
|
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
|
||||||
|
"00" // service flags, COMPACTSIZE(NODE_NONE)
|
||||||
|
"02" // network id, IPv6
|
||||||
|
"10" // address length, COMPACTSIZE(16)
|
||||||
|
"00000000000000000000000000000001" // address
|
||||||
|
"0000" // port
|
||||||
|
|
||||||
|
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
|
||||||
|
"01" // service flags, COMPACTSIZE(NODE_NETWORK)
|
||||||
|
"02" // network id, IPv6
|
||||||
|
"10" // address length, COMPACTSIZE(16)
|
||||||
|
"00000000000000000000000000000001" // address
|
||||||
|
"00f1" // port
|
||||||
|
|
||||||
|
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
|
||||||
|
"fd4804" // service flags, COMPACTSIZE(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED)
|
||||||
|
"02" // network id, IPv6
|
||||||
|
"10" // address length, COMPACTSIZE(16)
|
||||||
|
"00000000000000000000000000000001" // address
|
||||||
|
"f1f2"; // port
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
|
||||||
|
{
|
||||||
|
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
|
||||||
|
s << fixture_addresses;
|
||||||
|
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv1_hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
|
||||||
|
{
|
||||||
|
CDataStream s(ParseHex(stream_addrv1_hex), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
std::vector<CAddress> addresses_unserialized;
|
||||||
|
|
||||||
|
s >> addresses_unserialized;
|
||||||
|
BOOST_CHECK(fixture_addresses == addresses_unserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
|
||||||
|
{
|
||||||
|
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
|
||||||
|
|
||||||
|
s << fixture_addresses;
|
||||||
|
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv2_hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
|
||||||
|
{
|
||||||
|
CDataStream s(ParseHex(stream_addrv2_hex), SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
|
||||||
|
std::vector<CAddress> addresses_unserialized;
|
||||||
|
|
||||||
|
s >> addresses_unserialized;
|
||||||
|
BOOST_CHECK(fixture_addresses == addresses_unserialized);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Add table
Reference in a new issue