Skip to content

Commit cec9567

Browse files
committed
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. Adaptation of btc@201a4596d92d640d5eb7e76cc8d959228fa09dbb
1 parent b8c1dda commit cec9567

File tree

5 files changed

+169
-15
lines changed

5 files changed

+169
-15
lines changed

doc/release-notes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ From PIVX Core 6.0 onwards, macOS versions earlier than 10.12 are no longer supp
3838

3939
PIVX Core should also work on most other Unix-like systems but is not frequently tested on them.
4040

41+
The node's known peers are persisted to disk in a file called `peers.dat`. The
42+
format of this file has been changed in a backwards-incompatible way in order to
43+
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
44+
the file is modified by v5.3 or newer then older versions will not be able to
45+
read it. Those old versions, in the event of a downgrade, will log an error
46+
message that deserialization has failed and will continue normal operation
47+
as if the file was missing, creating a new empty one. (#2411)
4148

4249
Notable Changes
4350
==============

src/addrman.h

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "random.h"
1414
#include "sync.h"
1515
#include "timedata.h"
16+
#include "tinyformat.h"
1617
#include "util/system.h"
1718

1819
#include <fs.h>
@@ -271,6 +272,14 @@ friend class CAddrManTest;
271272
void SetServices_(const CService& addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
272273

273274
public:
275+
//! Serialization versions.
276+
enum class Format : uint8_t {
277+
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
278+
V1_DETERMINISTIC = 1, //!< for pre-asmap files
279+
V2_ASMAP = 2, //!< for files including asmap version
280+
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
281+
};
282+
274283
// Compressed IP->ASN mapping, loaded from a file when a node starts.
275284
// Should be always empty if no file was provided.
276285
// This mapping is then used for bucketing nodes in Addrman.
@@ -292,8 +301,8 @@ friend class CAddrManTest;
292301

293302

294303
/**
295-
* serialized format:
296-
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
304+
* Serialized format.
305+
* * version byte (@see `Format`)
297306
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
298307
* * nNew
299308
* * nTried
@@ -321,12 +330,15 @@ friend class CAddrManTest;
321330
* very little in common.
322331
*/
323332
template <typename Stream>
324-
void Serialize(Stream& s) const
333+
void Serialize(Stream& s_) const
325334
{
326335
LOCK(cs);
327336

328-
unsigned char nVersion = 2;
329-
s << nVersion;
337+
// Always serialize in the latest version (currently Format::V3_BIP155).
338+
339+
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
340+
341+
s << static_cast<uint8_t>(Format::V3_BIP155);
330342
s << ((unsigned char)32);
331343
s << nKey;
332344
s << nNew;
@@ -378,13 +390,33 @@ friend class CAddrManTest;
378390
}
379391

380392
template <typename Stream>
381-
void Unserialize(Stream& s)
393+
void Unserialize(Stream& s_)
382394
{
383395
LOCK(cs);
384396

385397
Clear();
386-
unsigned char nVersion;
387-
s >> nVersion;
398+
399+
Format format;
400+
s_ >> Using<CustomUintFormatter<1>>(format);
401+
402+
static constexpr Format maximum_supported_format = Format::V3_BIP155;
403+
if (format > maximum_supported_format) {
404+
throw std::ios_base::failure(strprintf(
405+
"Unsupported format of addrman database: %u. Maximum supported is %u. "
406+
"Continuing operation without using the saved list of peers.",
407+
static_cast<uint8_t>(format),
408+
static_cast<uint8_t>(maximum_supported_format)));
409+
}
410+
411+
int stream_version = s_.GetVersion();
412+
if (format >= Format::V3_BIP155) {
413+
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
414+
// unserialize methods know that an address in addrv2 format is coming.
415+
stream_version |= ADDRV2_FORMAT;
416+
}
417+
418+
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
419+
388420
unsigned char nKeySize;
389421
s >> nKeySize;
390422
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
@@ -393,7 +425,7 @@ friend class CAddrManTest;
393425
s >> nTried;
394426
int nUBuckets = 0;
395427
s >> nUBuckets;
396-
if (nVersion != 0) {
428+
if (format >= Format::V1_DETERMINISTIC) {
397429
nUBuckets ^= (1 << 30);
398430
}
399431

@@ -456,21 +488,21 @@ friend class CAddrManTest;
456488
supplied_asmap_version = SerializeHash(m_asmap);
457489
}
458490
uint256 serialized_asmap_version;
459-
if (nVersion > 1) {
491+
if (format >= Format::V2_ASMAP) {
460492
s >> serialized_asmap_version;
461493
}
462494

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

src/protocol.h

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

302302
public:
303303
CAddress() : CService{} {};
304-
explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
304+
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
305+
CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nServices{nServicesIn}, nTime{nTimeIn} {};
305306

306307
SERIALIZE_METHODS(CAddress, obj)
307308
{
@@ -314,7 +315,14 @@ class CAddress : public CService
314315
(nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH))) {
315316
READWRITE(obj.nTime);
316317
}
317-
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
318+
if (nVersion & ADDRV2_FORMAT) {
319+
uint64_t services_tmp;
320+
SER_WRITE(obj, services_tmp = obj.nServices);
321+
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
322+
SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
323+
} else {
324+
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
325+
}
318326
READWRITEAS(CService, obj);
319327
}
320328

src/streams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class OverrideStream
6363
int GetVersion() const { return nVersion; }
6464
int GetType() const { return nType; }
6565
size_t size() const { return stream->size(); }
66+
void ignore(size_t size) { return stream->ignore(size); }
6667
};
6768

6869
/** Double ended buffer combining vector and stream-like interfaces.

src/test/netbase_tests.cpp

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
// Distributed under the MIT/X11 software license, see the accompanying
55
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
66

7+
#include "test/test_pivx.h"
8+
79
#include "net.h" // validateMasternodeIP
810
#include "netbase.h"
9-
#include "test/test_pivx.h"
11+
#include "protocol.h"
12+
#include "serialize.h"
13+
#include "streams.h"
14+
#include "version.h"
1015

1116
#include <string>
1217

@@ -385,4 +390,105 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
385390
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
386391
}
387392

393+
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
394+
// try a few edge cases for port, service flags and time.
395+
396+
static const std::vector<CAddress> fixture_addresses({
397+
CAddress(
398+
CService(CNetAddr(in6addr_loopback), 0 /* port */),
399+
NODE_NONE,
400+
0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */
401+
),
402+
CAddress(
403+
CService(CNetAddr(in6addr_loopback), 0x00f1 /* port */),
404+
NODE_NETWORK,
405+
0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */
406+
),
407+
CAddress(
408+
CService(CNetAddr(in6addr_loopback), 0xf1f2 /* port */),
409+
static_cast<ServiceFlags>(NODE_BLOOM),
410+
0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */
411+
)
412+
});
413+
414+
// fixture_addresses should equal to this when serialized in V1 format.
415+
// When this is unserialized from V1 format it should equal to fixture_addresses.
416+
static constexpr const char* stream_addrv1_hex =
417+
"03" // number of entries
418+
419+
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
420+
"0000000000000000" // service flags, NODE_NONE
421+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
422+
"0000" // port
423+
424+
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
425+
"0100000000000000" // service flags, NODE_NETWORK
426+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
427+
"00f1" // port
428+
429+
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
430+
"0400000000000000" // service flags, NODE_BLOOM
431+
"00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
432+
"f1f2"; // port
433+
434+
// fixture_addresses should equal to this when serialized in V2 format.
435+
// When this is unserialized from V2 format it should equal to fixture_addresses.
436+
static constexpr const char* stream_addrv2_hex =
437+
"03" // number of entries
438+
439+
"61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
440+
"00" // service flags, COMPACTSIZE(NODE_NONE)
441+
"02" // network id, IPv6
442+
"10" // address length, COMPACTSIZE(16)
443+
"00000000000000000000000000000001" // address
444+
"0000" // port
445+
446+
"79627683" // time, Tue Nov 22 11:22:33 UTC 2039
447+
"01" // service flags, COMPACTSIZE(NODE_NETWORK)
448+
"02" // network id, IPv6
449+
"10" // address length, COMPACTSIZE(16)
450+
"00000000000000000000000000000001" // address
451+
"00f1" // port
452+
453+
"ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
454+
"04" // service flags, COMPACTSIZE(NODE_BLOOM)
455+
"02" // network id, IPv6
456+
"10" // address length, COMPACTSIZE(16)
457+
"00000000000000000000000000000001" // address
458+
"f1f2"; // port
459+
460+
BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
461+
{
462+
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
463+
464+
s << fixture_addresses;
465+
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv1_hex);
466+
}
467+
468+
BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
469+
{
470+
CDataStream s(ParseHex(stream_addrv1_hex), SER_NETWORK, PROTOCOL_VERSION);
471+
std::vector<CAddress> addresses_unserialized;
472+
473+
s >> addresses_unserialized;
474+
BOOST_CHECK(fixture_addresses == addresses_unserialized);
475+
}
476+
477+
BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
478+
{
479+
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
480+
481+
s << fixture_addresses;
482+
BOOST_CHECK_EQUAL(HexStr(s), stream_addrv2_hex);
483+
}
484+
485+
BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
486+
{
487+
CDataStream s(ParseHex(stream_addrv2_hex), SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
488+
std::vector<CAddress> addresses_unserialized;
489+
490+
s >> addresses_unserialized;
491+
BOOST_CHECK(fixture_addresses == addresses_unserialized);
492+
}
493+
388494
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)