Skip to content

Commit 0b2abaa

Browse files
committed
Merge #19954: Complete the BIP155 implementation and upgrade to TORv3
dcf0cb4 tor: make a TORv3 hidden service instead of TORv2 (Vasil Dimov) 353a3fd net: advertise support for ADDRv2 via new message (Vasil Dimov) 201a459 net: CAddress & CAddrMan: (un)serialize as ADDRv2 (Vasil Dimov) 1d3ec2a Support bypassing range check in ReadCompactSize (Pieter Wuille) Pull request description: This PR contains the two remaining commits from #19031 to complete the [BIP155](https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki) implementation: `net: CAddress & CAddrMan: (un)serialize as ADDRv2` `net: advertise support for ADDRv2 via new message` plus one more commit: `tor: make a TORv3 hidden service instead of TORv2` ACKs for top commit: jonatack: re-ACK dcf0cb4 per `git diff 9b56a68 dcf0cb4` only change since last review is an update to the release notes which partially picked up the suggested text. Running a node on this branch and addnode-ing to 6 other Tor v3 nodes, I see "addrv2" and "sendaddrv2" messages in getpeerinfo in both the "bytesrecv_per_msg" and "bytessent_per_msg" JSON objects. sipa: ACK dcf0cb4 hebasto: re-ACK dcf0cb4, the node works flawlessly in all of the modes: Tor-only, clearnet-only, mixed. laanwj: Edit: I have to retract this ACK for now, I'm having some problems with this PR on a FreeBSD node. It drops all outgoing connections with this dcf0cb4 merged on master (12a1c3a). ariard: Code Review ACK dcf0cb4 Tree-SHA512: 28d4d0d817b8664d2f4b18c0e0f31579b2f0f2d23310ed213f1f436a4242afea14dfbf99e07e15889bc5c5c71ad50056797e9307ff8a90e96704f588a6171308
2 parents 12a1c3a + dcf0cb4 commit 0b2abaa

19 files changed

+596
-48
lines changed

doc/files.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Subdirectory | File(s) | Description
5858
`./` | `guisettings.ini.bak` | Backup of former [GUI settings](#gui-settings) after `-resetguisettings` option is used
5959
`./` | `ip_asn.map` | IP addresses to Autonomous System Numbers (ASNs) mapping used for bucketing of the peers; path can be specified with the `-asmap` option
6060
`./` | `mempool.dat` | Dump of the mempool's transactions
61-
`./` | `onion_private_key` | Cached Tor onion service private key for `-listenonion` option
61+
`./` | `onion_v3_private_key` | Cached Tor onion service private key for `-listenonion` option
6262
`./` | `peers.dat` | Peer IP address database (custom format)
6363
`./` | `settings.json` | Read-write settings set through GUI or RPC interfaces, augmenting manual settings from [bitcoin.conf](bitcoin-conf.md). File is created automatically if read-write settings storage is not disabled with `-nosettings` option. Path can be specified with `-settings` option
6464
`./` | `.cookie` | Session RPC authentication cookie; if used, created at start and deleted on shutdown; can be specified by `-rpccookiefile` option
@@ -100,6 +100,7 @@ Path | Description | Repository notes
100100
`blkindex.dat` | Blockchain index BDB database; replaced by {`chainstate/`, `blocks/index/`, `blocks/revNNNNN.dat`<sup>[\[2\]](#note2)</sup>} in 0.8.0 | [PR #1677](https://github.com/bitcoin/bitcoin/pull/1677)
101101
`blk000?.dat` | Block data (custom format, 2 GiB per file); replaced by `blocks/blkNNNNN.dat`<sup>[\[2\]](#note2)</sup> in 0.8.0 | [PR #1677](https://github.com/bitcoin/bitcoin/pull/1677)
102102
`addr.dat` | Peer IP address BDB database; replaced by `peers.dat` in [0.7.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.7.0.md) | [PR #1198](https://github.com/bitcoin/bitcoin/pull/1198), [`928d3a01`](https://github.com/bitcoin/bitcoin/commit/928d3a011cc66c7f907c4d053f674ea77dc611cc)
103+
`onion_private_key` | Cached Tor onion service private key for `-listenonion` option. Was used for Tor v2 services; replaced by `onion_v3_private_key` in [0.21.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md) | [PR #19954](https://github.com/bitcoin/bitcoin/pull/19954)
103104

104105
## Notes
105106

doc/release-notes.md

Lines changed: 19 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

@@ -74,6 +82,17 @@ P2P and network changes
7482
node using P2P relay. This version reduces the initial broadcast guarantees
7583
for wallet transactions submitted via P2P to a node running the wallet. (#18038)
7684

85+
- The Tor onion service that is automatically created by setting the
86+
`-listenonion` configuration parameter will now be created as a Tor v3 service
87+
instead of Tor v2. The private key that was used for Tor v2 (if any) will be
88+
left untouched in the `onion_private_key` file in the data directory (see
89+
`-datadir`) and can be removed if not needed. Bitcoin Core will no longer
90+
attempt to read it. The private key for the Tor v3 service will be saved in a
91+
file named `onion_v3_private_key`. To use the deprecated Tor v2 service (not
92+
recommended), then `onion_private_key` can be copied over
93+
`onion_v3_private_key`, e.g.
94+
`cp -f onion_private_key onion_v3_private_key`. (#19954)
95+
7796
Updated RPCs
7897
------------
7998

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/net.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,11 @@ class CNode
867867
bool m_legacyWhitelisted{false};
868868
bool fClient{false}; // set by version message
869869
bool m_limited_node{false}; //after BIP159, set by version message
870+
/**
871+
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
872+
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
873+
*/
874+
std::atomic_bool m_wants_addrv2{false};
870875
std::atomic_bool fSuccessfullyConnected{false};
871876
// Setting fDisconnect to true will cause the node to be disconnected the
872877
// next time DisconnectNodes() runs
@@ -1114,11 +1119,16 @@ class CNode
11141119

11151120
void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
11161121
{
1122+
// Whether the peer supports the address in `_addr`. For example,
1123+
// nodes that do not implement BIP155 cannot receive Tor v3 addresses
1124+
// because they require ADDRv2 (BIP155) encoding.
1125+
const bool addr_format_supported = m_wants_addrv2 || _addr.IsAddrV1Compatible();
1126+
11171127
// Known checking here is only to save space from duplicates.
11181128
// SendMessages will filter it again for knowns that were added
11191129
// after addresses were pushed.
11201130
assert(m_addr_known);
1121-
if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey())) {
1131+
if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && addr_format_supported) {
11221132
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
11231133
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
11241134
} else {

src/net_processing.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <random.h>
2424
#include <reverse_iterator.h>
2525
#include <scheduler.h>
26+
#include <streams.h>
2627
#include <tinyformat.h>
2728
#include <txmempool.h>
2829
#include <util/check.h> // For NDEBUG compile time check
@@ -2435,11 +2436,16 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
24352436
pfrom.SetCommonVersion(greatest_common_version);
24362437
pfrom.nVersion = nVersion;
24372438

2439+
const CNetMsgMaker msg_maker(greatest_common_version);
2440+
24382441
if (greatest_common_version >= WTXID_RELAY_VERSION) {
2439-
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::WTXIDRELAY));
2442+
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY));
24402443
}
24412444

2442-
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::VERACK));
2445+
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
2446+
2447+
// Signal ADDRv2 support (BIP155).
2448+
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
24432449

24442450
pfrom.nServices = nServices;
24452451
pfrom.SetAddrLocal(addrMe);
@@ -2608,16 +2614,25 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
26082614
return;
26092615
}
26102616

2611-
if (msg_type == NetMsgType::ADDR) {
2617+
if (msg_type == NetMsgType::ADDR || msg_type == NetMsgType::ADDRV2) {
2618+
int stream_version = vRecv.GetVersion();
2619+
if (msg_type == NetMsgType::ADDRV2) {
2620+
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
2621+
// unserialize methods know that an address in v2 format is coming.
2622+
stream_version |= ADDRV2_FORMAT;
2623+
}
2624+
2625+
OverrideStream<CDataStream> s(&vRecv, vRecv.GetType(), stream_version);
26122626
std::vector<CAddress> vAddr;
2613-
vRecv >> vAddr;
2627+
2628+
s >> vAddr;
26142629

26152630
if (!pfrom.RelayAddrsWithConn()) {
26162631
return;
26172632
}
26182633
if (vAddr.size() > MAX_ADDR_TO_SEND)
26192634
{
2620-
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
2635+
Misbehaving(pfrom.GetId(), 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
26212636
return;
26222637
}
26232638

@@ -2661,6 +2676,11 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
26612676
return;
26622677
}
26632678

2679+
if (msg_type == NetMsgType::SENDADDRV2) {
2680+
pfrom.m_wants_addrv2 = true;
2681+
return;
2682+
}
2683+
26642684
if (msg_type == NetMsgType::SENDHEADERS) {
26652685
LOCK(cs_main);
26662686
State(pfrom.GetId())->fPreferHeaders = true;
@@ -4117,6 +4137,17 @@ bool PeerManager::SendMessages(CNode* pto)
41174137
std::vector<CAddress> vAddr;
41184138
vAddr.reserve(pto->vAddrToSend.size());
41194139
assert(pto->m_addr_known);
4140+
4141+
const char* msg_type;
4142+
int make_flags;
4143+
if (pto->m_wants_addrv2) {
4144+
msg_type = NetMsgType::ADDRV2;
4145+
make_flags = ADDRV2_FORMAT;
4146+
} else {
4147+
msg_type = NetMsgType::ADDR;
4148+
make_flags = 0;
4149+
}
4150+
41204151
for (const CAddress& addr : pto->vAddrToSend)
41214152
{
41224153
if (!pto->m_addr_known->contains(addr.GetKey()))
@@ -4126,14 +4157,14 @@ bool PeerManager::SendMessages(CNode* pto)
41264157
// receiver rejects addr messages larger than MAX_ADDR_TO_SEND
41274158
if (vAddr.size() >= MAX_ADDR_TO_SEND)
41284159
{
4129-
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
4160+
m_connman.PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
41304161
vAddr.clear();
41314162
}
41324163
}
41334164
}
41344165
pto->vAddrToSend.clear();
41354166
if (!vAddr.empty())
4136-
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
4167+
m_connman.PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
41374168
// we only send the big addr message once
41384169
if (pto->vAddrToSend.capacity() > 40)
41394170
pto->vAddrToSend.shrink_to_fit();

src/netaddress.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,26 @@ bool CNetAddr::IsInternal() const
474474
return m_net == NET_INTERNAL;
475475
}
476476

477+
bool CNetAddr::IsAddrV1Compatible() const
478+
{
479+
switch (m_net) {
480+
case NET_IPV4:
481+
case NET_IPV6:
482+
case NET_INTERNAL:
483+
return true;
484+
case NET_ONION:
485+
return m_addr.size() == ADDR_TORV2_SIZE;
486+
case NET_I2P:
487+
case NET_CJDNS:
488+
return false;
489+
case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
490+
case NET_MAX: // m_net is never and should not be set to NET_MAX
491+
assert(false);
492+
} // no default case, so the compiler can warn about missing cases
493+
494+
assert(false);
495+
}
496+
477497
enum Network CNetAddr::GetNetwork() const
478498
{
479499
if (IsInternal())
@@ -744,9 +764,12 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
744764

745765
std::vector<unsigned char> CNetAddr::GetAddrBytes() const
746766
{
747-
uint8_t serialized[V1_SERIALIZATION_SIZE];
748-
SerializeV1Array(serialized);
749-
return {std::begin(serialized), std::end(serialized)};
767+
if (IsAddrV1Compatible()) {
768+
uint8_t serialized[V1_SERIALIZATION_SIZE];
769+
SerializeV1Array(serialized);
770+
return {std::begin(serialized), std::end(serialized)};
771+
}
772+
return std::vector<unsigned char>(m_addr.begin(), m_addr.end());
750773
}
751774

752775
uint64_t CNetAddr::GetHash() const

src/netaddress.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ class CNetAddr
177177
bool IsRoutable() const;
178178
bool IsInternal() const;
179179
bool IsValid() const;
180+
181+
/**
182+
* Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
183+
*/
184+
bool IsAddrV1Compatible() const;
185+
180186
enum Network GetNetwork() const;
181187
std::string ToString() const;
182188
std::string ToStringIP() const;

src/protocol.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace NetMsgType {
1414
const char *VERSION="version";
1515
const char *VERACK="verack";
1616
const char *ADDR="addr";
17+
const char *ADDRV2="addrv2";
18+
const char *SENDADDRV2="sendaddrv2";
1719
const char *INV="inv";
1820
const char *GETDATA="getdata";
1921
const char *MERKLEBLOCK="merkleblock";
@@ -52,6 +54,8 @@ const static std::string allNetMessageTypes[] = {
5254
NetMsgType::VERSION,
5355
NetMsgType::VERACK,
5456
NetMsgType::ADDR,
57+
NetMsgType::ADDRV2,
58+
NetMsgType::SENDADDRV2,
5559
NetMsgType::INV,
5660
NetMsgType::GETDATA,
5761
NetMsgType::MERKLEBLOCK,

0 commit comments

Comments
 (0)