Skip to content

Commit 201a459

Browse files
vasilddongcarl
andcommitted
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 <[email protected]>
1 parent 1d3ec2a commit 201a459

File tree

5 files changed

+170
-16
lines changed

5 files changed

+170
-16
lines changed

doc/release-notes.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no
6060
longer supported. Additionally, Bitcoin Core does not yet change appearance
6161
when macOS "dark mode" is activated.
6262

63+
The node's known peers are persisted to disk in a file called `peers.dat`. The
64+
format of this file has been changed in a backwards-incompatible way in order to
65+
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
66+
the file is modified by 0.21.0 or newer then older versions will not be able to
67+
read it. Those old versions, in the event of a downgrade, will log an error
68+
message that deserialization has failed and will continue normal operation
69+
as if the file was missing, creating a new empty one. (#19954)
70+
6371
Notable changes
6472
===============
6573

src/addrman.h

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <random.h>
1313
#include <sync.h>
1414
#include <timedata.h>
15+
#include <tinyformat.h>
1516
#include <util/system.h>
1617

1718
#include <fs.h>
@@ -264,6 +265,14 @@ friend class CAddrManTest;
264265
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
265266

266267
public:
268+
//! Serialization versions.
269+
enum class Format : uint8_t {
270+
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
271+
V1_DETERMINISTIC = 1, //!< for pre-asmap files
272+
V2_ASMAP = 2, //!< for files including asmap version
273+
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
274+
};
275+
267276
// Compressed IP->ASN mapping, loaded from a file when a node starts.
268277
// Should be always empty if no file was provided.
269278
// This mapping is then used for bucketing nodes in Addrman.
@@ -285,8 +294,8 @@ friend class CAddrManTest;
285294

286295

287296
/**
288-
* serialized format:
289-
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
297+
* Serialized format.
298+
* * version byte (@see `Format`)
290299
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
291300
* * nNew
292301
* * nTried
@@ -313,13 +322,16 @@ friend class CAddrManTest;
313322
* We don't use SERIALIZE_METHODS since the serialization and deserialization code has
314323
* very little in common.
315324
*/
316-
template<typename Stream>
317-
void Serialize(Stream &s) const
325+
template <typename Stream>
326+
void Serialize(Stream& s_) const
318327
{
319328
LOCK(cs);
320329

321-
unsigned char nVersion = 2;
322-
s << nVersion;
330+
// Always serialize in the latest version (currently Format::V3_BIP155).
331+
332+
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
333+
334+
s << static_cast<uint8_t>(Format::V3_BIP155);
323335
s << ((unsigned char)32);
324336
s << nKey;
325337
s << nNew;
@@ -370,14 +382,34 @@ friend class CAddrManTest;
370382
s << asmap_version;
371383
}
372384

373-
template<typename Stream>
374-
void Unserialize(Stream& s)
385+
template <typename Stream>
386+
void Unserialize(Stream& s_)
375387
{
376388
LOCK(cs);
377389

378390
Clear();
379-
unsigned char nVersion;
380-
s >> nVersion;
391+
392+
Format format;
393+
s_ >> Using<CustomUintFormatter<1>>(format);
394+
395+
static constexpr Format maximum_supported_format = Format::V3_BIP155;
396+
if (format > maximum_supported_format) {
397+
throw std::ios_base::failure(strprintf(
398+
"Unsupported format of addrman database: %u. Maximum supported is %u. "
399+
"Continuing operation without using the saved list of peers.",
400+
static_cast<uint8_t>(format),
401+
static_cast<uint8_t>(maximum_supported_format)));
402+
}
403+
404+
int stream_version = s_.GetVersion();
405+
if (format >= Format::V3_BIP155) {
406+
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
407+
// unserialize methods know that an address in addrv2 format is coming.
408+
stream_version |= ADDRV2_FORMAT;
409+
}
410+
411+
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
412+
381413
unsigned char nKeySize;
382414
s >> nKeySize;
383415
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
@@ -386,7 +418,7 @@ friend class CAddrManTest;
386418
s >> nTried;
387419
int nUBuckets = 0;
388420
s >> nUBuckets;
389-
if (nVersion != 0) {
421+
if (format >= Format::V1_DETERMINISTIC) {
390422
nUBuckets ^= (1 << 30);
391423
}
392424

@@ -449,21 +481,21 @@ friend class CAddrManTest;
449481
supplied_asmap_version = SerializeHash(m_asmap);
450482
}
451483
uint256 serialized_asmap_version;
452-
if (nVersion > 1) {
484+
if (format >= Format::V2_ASMAP) {
453485
s >> serialized_asmap_version;
454486
}
455487

456488
for (int n = 0; n < nNew; n++) {
457489
CAddrInfo &info = mapInfo[n];
458490
int bucket = entryToBucket[n];
459491
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
460-
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
492+
if (format >= Format::V2_ASMAP && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
461493
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
462494
// Bucketing has not changed, using existing bucket positions for the new table
463495
vvNew[bucket][nUBucketPos] = n;
464496
info.nRefCount++;
465497
} else {
466-
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
498+
// In case the new table data cannot be used (format unknown, bucket count wrong or new asmap),
467499
// try to give them a reference based on their primary source address.
468500
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
469501
bucket = info.GetNewBucket(nKey, m_asmap);

src/protocol.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@ class CAddress : public CService
351351

352352
public:
353353
CAddress() : CService{} {};
354-
explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
354+
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
355+
CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nTime{nTimeIn}, nServices{nServicesIn} {};
355356

356357
SERIALIZE_METHODS(CAddress, obj)
357358
{
@@ -370,7 +371,14 @@ class CAddress : public CService
370371
// nTime.
371372
READWRITE(obj.nTime);
372373
}
373-
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
374+
if (nVersion & ADDRV2_FORMAT) {
375+
uint64_t services_tmp;
376+
SER_WRITE(obj, services_tmp = obj.nServices);
377+
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
378+
SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
379+
} else {
380+
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
381+
}
374382
READWRITEAS(CService, obj);
375383
}
376384

src/streams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class OverrideStream
6060
int GetVersion() const { return nVersion; }
6161
int GetType() const { return nType; }
6262
size_t size() const { return stream->size(); }
63+
void ignore(size_t size) { return stream->ignore(size); }
6364
};
6465

6566
/* Minimal stream for overwriting and/or appending to an existing byte vector

src/test/netbase_tests.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
#include <net_permissions.h>
66
#include <netbase.h>
7+
#include <protocol.h>
8+
#include <serialize.h>
9+
#include <streams.h>
710
#include <test/util/setup_common.h>
811
#include <util/strencodings.h>
912
#include <util/translation.h>
13+
#include <version.h>
1014

1115
#include <string>
1216

@@ -443,4 +447,105 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
443447
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
444448
}
445449

450+
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
451+
// try a few edge cases for port, service flags and time.
452+
453+
static const std::vector<CAddress> fixture_addresses({
454+
CAddress(
455+
CService(CNetAddr(in6addr_loopback), 0 /* port */),
456+
NODE_NONE,
457+
0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */
458+
),
459+
CAddress(
460+
CService(CNetAddr(in6addr_loopback), 0x00f1 /* port */),
461+
NODE_NETWORK,
462+
0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */
463+
),
464+
CAddress(
465+
CService(CNetAddr(in6addr_loopback), 0xf1f2 /* port */),
466+
static_cast<ServiceFlags>(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED),
467+
0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */
468+
)
469+
});
470+
471+
// fixture_addresses should equal to this when serialized in V1 format.
472+
// When this is unserialized from V1 format it should equal to fixture_addresses.
473+
static constexpr const char* stream_addrv1_hex =
474+
"03" // number of entries
475+
476+
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
477+
"0000000000000000" // service flags, NODE_NONE
478+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
479+
"0000" // port
480+
481+
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
482+
"0100000000000000" // service flags, NODE_NETWORK
483+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
484+
"00f1" // port
485+
486+
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
487+
"4804000000000000" // service flags, NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED
488+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
489+
"f1f2"; // port
490+
491+
// fixture_addresses should equal to this when serialized in V2 format.
492+
// When this is unserialized from V2 format it should equal to fixture_addresses.
493+
static constexpr const char* stream_addrv2_hex =
494+
"03" // number of entries
495+
496+
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
497+
"00" // service flags, COMPACTSIZE(NODE_NONE)
498+
"02" // network id, IPv6
499+
"10" // address length, COMPACTSIZE(16)
500+
"00000000000000000000000000000001" // address
501+
"0000" // port
502+
503+
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
504+
"01" // service flags, COMPACTSIZE(NODE_NETWORK)
505+
"02" // network id, IPv6
506+
"10" // address length, COMPACTSIZE(16)
507+
"00000000000000000000000000000001" // address
508+
"00f1" // port
509+
510+
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
511+
"fd4804" // service flags, COMPACTSIZE(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED)
512+
"02" // network id, IPv6
513+
"10" // address length, COMPACTSIZE(16)
514+
"00000000000000000000000000000001" // address
515+
"f1f2"; // port
516+
517+
BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
518+
{
519+
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
520+
521+
s << fixture_addresses;
522+
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv1_hex);
523+
}
524+
525+
BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
526+
{
527+
CDataStream s(ParseHex(stream_addrv1_hex), SER_NETWORK, PROTOCOL_VERSION);
528+
std::vector<CAddress> addresses_unserialized;
529+
530+
s >> addresses_unserialized;
531+
BOOST_CHECK(fixture_addresses == addresses_unserialized);
532+
}
533+
534+
BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
535+
{
536+
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
537+
538+
s << fixture_addresses;
539+
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv2_hex);
540+
}
541+
542+
BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
543+
{
544+
CDataStream s(ParseHex(stream_addrv2_hex), SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
545+
std::vector<CAddress> addresses_unserialized;
546+
547+
s >> addresses_unserialized;
548+
BOOST_CHECK(fixture_addresses == addresses_unserialized);
549+
}
550+
446551
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)