mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03: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
|
||||
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
|
||||
===============
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <random.h>
|
||||
#include <sync.h>
|
||||
#include <timedata.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <fs.h>
|
||||
|
@ -264,6 +265,14 @@ protected:
|
|||
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
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.
|
||||
// Should be always empty if no file was provided.
|
||||
// This mapping is then used for bucketing nodes in Addrman.
|
||||
|
@ -285,8 +294,8 @@ public:
|
|||
|
||||
|
||||
/**
|
||||
* serialized format:
|
||||
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
|
||||
* Serialized format.
|
||||
* * version byte (@see `Format`)
|
||||
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
||||
* * nNew
|
||||
* * nTried
|
||||
|
@ -313,13 +322,16 @@ public:
|
|||
* We don't use SERIALIZE_METHODS since the serialization and deserialization code has
|
||||
* very little in common.
|
||||
*/
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s) const
|
||||
template <typename Stream>
|
||||
void Serialize(Stream& s_) const
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
unsigned char nVersion = 2;
|
||||
s << nVersion;
|
||||
// Always serialize in the latest version (currently Format::V3_BIP155).
|
||||
|
||||
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
|
||||
|
||||
s << static_cast<uint8_t>(Format::V3_BIP155);
|
||||
s << ((unsigned char)32);
|
||||
s << nKey;
|
||||
s << nNew;
|
||||
|
@ -370,14 +382,34 @@ public:
|
|||
s << asmap_version;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
template <typename Stream>
|
||||
void Unserialize(Stream& s_)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
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;
|
||||
s >> nKeySize;
|
||||
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
||||
|
@ -386,7 +418,7 @@ public:
|
|||
s >> nTried;
|
||||
int nUBuckets = 0;
|
||||
s >> nUBuckets;
|
||||
if (nVersion != 0) {
|
||||
if (format >= Format::V1_DETERMINISTIC) {
|
||||
nUBuckets ^= (1 << 30);
|
||||
}
|
||||
|
||||
|
@ -449,7 +481,7 @@ public:
|
|||
supplied_asmap_version = SerializeHash(m_asmap);
|
||||
}
|
||||
uint256 serialized_asmap_version;
|
||||
if (nVersion > 1) {
|
||||
if (format >= Format::V2_ASMAP) {
|
||||
s >> serialized_asmap_version;
|
||||
}
|
||||
|
||||
|
@ -457,13 +489,13 @@ public:
|
|||
CAddrInfo &info = mapInfo[n];
|
||||
int bucket = entryToBucket[n];
|
||||
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) {
|
||||
// Bucketing has not changed, using existing bucket positions for the new table
|
||||
vvNew[bucket][nUBucketPos] = n;
|
||||
info.nRefCount++;
|
||||
} 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.
|
||||
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
||||
bucket = info.GetNewBucket(nKey, m_asmap);
|
||||
|
|
|
@ -351,7 +351,8 @@ class CAddress : public CService
|
|||
|
||||
public:
|
||||
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)
|
||||
{
|
||||
|
@ -370,7 +371,14 @@ public:
|
|||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
int GetVersion() const { return nVersion; }
|
||||
int GetType() const { return nType; }
|
||||
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
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
|
||||
#include <net_permissions.h>
|
||||
#include <netbase.h>
|
||||
#include <protocol.h>
|
||||
#include <serialize.h>
|
||||
#include <streams.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/translation.h>
|
||||
#include <version.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
|
Loading…
Reference in a new issue