Skip to content

Commit 896a5af

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 b9c46e0 commit 896a5af

File tree

6 files changed

+193
-15
lines changed

6 files changed

+193
-15
lines changed

src/addrman.h

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ friend class CAddrManTest;
264264
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
265265

266266
public:
267+
//! Serialization versions.
268+
enum class Format : uint8_t {
269+
V0 = 0, //!< historic format, before commit e6b343d88
270+
V1 = 1, //!< for pre-asmap files
271+
V2 = 2, //!< for files including asmap version
272+
V3 = 3, //!< same as V2 plus addresses are in BIP155 format
273+
};
274+
275+
//! Serialization / unserialization format.
276+
Format m_format = Format::V3;
277+
267278
// Compressed IP->ASN mapping, loaded from a file when a node starts.
268279
// Should be always empty if no file was provided.
269280
// This mapping is then used for bucketing nodes in Addrman.
@@ -285,8 +296,8 @@ friend class CAddrManTest;
285296

286297

287298
/**
288-
* serialized format:
289-
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
299+
* Serialized format.
300+
* * version byte (@see `Format`)
290301
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
291302
* * nNew
292303
* * nTried
@@ -318,8 +329,15 @@ friend class CAddrManTest;
318329
{
319330
LOCK(cs);
320331

321-
unsigned char nVersion = 2;
322-
s << nVersion;
332+
const auto stream_version_orig = s.GetVersion();
333+
334+
if (m_format >= Format::V3) {
335+
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
336+
// serialize methods produce an address in v2 format.
337+
s.SetVersion(stream_version_orig | ADDRV2_FORMAT);
338+
}
339+
340+
s << (uint8_t)m_format;
323341
s << ((unsigned char)32);
324342
s << nKey;
325343
s << nNew;
@@ -368,6 +386,8 @@ friend class CAddrManTest;
368386
asmap_version = SerializeHash(m_asmap);
369387
}
370388
s << asmap_version;
389+
390+
s.SetVersion(stream_version_orig);
371391
}
372392

373393
template<typename Stream>
@@ -376,8 +396,17 @@ friend class CAddrManTest;
376396
LOCK(cs);
377397

378398
Clear();
379-
unsigned char nVersion;
380-
s >> nVersion;
399+
400+
const auto stream_version_orig = s.GetVersion();
401+
402+
uint8_t format_tmp;
403+
s >> format_tmp;
404+
m_format = (Format)format_tmp;
405+
if (m_format >= Format::V3) {
406+
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
407+
// unserialize methods know that an address in v2 format is coming.
408+
s.SetVersion(stream_version_orig | ADDRV2_FORMAT);
409+
}
381410
unsigned char nKeySize;
382411
s >> nKeySize;
383412
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
@@ -386,15 +415,17 @@ friend class CAddrManTest;
386415
s >> nTried;
387416
int nUBuckets = 0;
388417
s >> nUBuckets;
389-
if (nVersion != 0) {
418+
if (m_format > Format::V0) {
390419
nUBuckets ^= (1 << 30);
391420
}
392421

393422
if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
423+
s.SetVersion(stream_version_orig);
394424
throw std::ios_base::failure("Corrupt CAddrMan serialization, nNew exceeds limit.");
395425
}
396426

397427
if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
428+
s.SetVersion(stream_version_orig);
398429
throw std::ios_base::failure("Corrupt CAddrMan serialization, nTried exceeds limit.");
399430
}
400431

@@ -449,21 +480,21 @@ friend class CAddrManTest;
449480
supplied_asmap_version = SerializeHash(m_asmap);
450481
}
451482
uint256 serialized_asmap_version;
452-
if (nVersion > 1) {
483+
if (m_format > Format::V1) {
453484
s >> serialized_asmap_version;
454485
}
455486

456487
for (int n = 0; n < nNew; n++) {
457488
CAddrInfo &info = mapInfo[n];
458489
int bucket = entryToBucket[n];
459490
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
460-
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
491+
if (m_format >= Format::V2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
461492
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
462493
// Bucketing has not changed, using existing bucket positions for the new table
463494
vvNew[bucket][nUBucketPos] = n;
464495
info.nRefCount++;
465496
} else {
466-
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
497+
// In case the new table data cannot be used (m_format unknown, bucket count wrong or new asmap),
467498
// try to give them a reference based on their primary source address.
468499
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
469500
bucket = info.GetNewBucket(nKey, m_asmap);
@@ -491,6 +522,8 @@ friend class CAddrManTest;
491522
}
492523

493524
Check();
525+
526+
s.SetVersion(stream_version_orig);
494527
}
495528

496529
void Clear()

src/hash.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,14 @@ class CHashWriter
102102
CSHA256 ctx;
103103

104104
const int nType;
105-
const int nVersion;
105+
int nVersion;
106106
public:
107107

108108
CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {}
109109

110110
int GetType() const { return nType; }
111111
int GetVersion() const { return nVersion; }
112+
void SetVersion(int n) { nVersion = n; }
112113

113114
void write(const char *pch, size_t size) {
114115
ctx.Write((const unsigned char*)pch, size);

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(COMPACTSIZE(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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ class CAutoFile
602602
{
603603
private:
604604
const int nType;
605-
const int nVersion;
605+
int nVersion;
606606

607607
FILE* file;
608608

@@ -650,6 +650,7 @@ class CAutoFile
650650
//
651651
int GetType() const { return nType; }
652652
int GetVersion() const { return nVersion; }
653+
void SetVersion(int n) { nVersion = n; }
653654

654655
void read(char* pch, size_t nSize)
655656
{

src/test/net_tests.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
8080
ssPeersIn << _addrman;
8181
std::string str = ssPeersIn.str();
8282
std::vector<unsigned char> vchData(str.begin(), str.end());
83-
return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
83+
return CDataStream(vchData, ssPeersIn.GetType(), ssPeersIn.GetVersion());
8484
}
8585

8686
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
@@ -142,6 +142,36 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
142142
BOOST_CHECK(addrman2.size() == 3);
143143
}
144144

145+
BOOST_AUTO_TEST_CASE(caddrdb_read_v1_ser)
146+
{
147+
// Construct an AddrMan that serializes addresses in pre-BIP155 format.
148+
CAddrManUncorrupted addrmanUncorrupted;
149+
addrmanUncorrupted.m_format = CAddrMan::Format::V2;
150+
addrmanUncorrupted.MakeDeterministic();
151+
152+
CService addr1, addr2, addr3;
153+
BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
154+
BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
155+
BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));
156+
157+
// Add three addresses to new table.
158+
CService source;
159+
BOOST_CHECK(Lookup("252.5.1.1", source, 8333, false));
160+
BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source));
161+
BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source));
162+
BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source));
163+
164+
// Test that the de-serialization does not throw an exception.
165+
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
166+
CAddrMan addrman1;
167+
168+
BOOST_CHECK(addrman1.size() == 0);
169+
unsigned char message_start[4];
170+
ssPeers1 >> message_start;
171+
ssPeers1 >> addrman1;
172+
BOOST_CHECK_EQUAL(addrman1.m_format, CAddrMan::Format::V2);
173+
BOOST_CHECK_EQUAL(addrman1.size(), 3U);
174+
}
145175

146176
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
147177
{

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)