Skip to content

Commit

Permalink
net: advertise support for ADDRv2 via new message
Browse files Browse the repository at this point in the history
Summary:
```
Introduce a new message `sendaddrv2` to signal support for ADDRv2.
Send the new message immediately after sending the `VERACK` message.

Add support for receiving and parsing ADDRv2 messages.

Send ADDRv2 messages (instead of ADDR) to a peer if he has
advertised support for it.
```

Partial backport (3/4) of core [[bitcoin/bitcoin#19954 | PR19954]]:
bitcoin/bitcoin@353a3fd

Depends on D9199.

Some functional tests required a debug log message update since we where
using a slightly different form from core. The new code is in sync with
core's message to make later backports easier.

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Subscribers: majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9200
  • Loading branch information
vasild authored and Fabcien committed Feb 11, 2021
1 parent e9b2955 commit 15f4d60
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 41 deletions.
14 changes: 13 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,11 @@ class CNode {
bool fClient{false};
// after BIP159, set by version message
bool m_limited_node{false};
/**
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
*/
std::atomic_bool m_wants_addrv2{false};
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
Expand Down Expand Up @@ -1103,11 +1108,18 @@ class CNode {
}

void PushAddress(const CAddress &_addr, FastRandomContext &insecure_rand) {
// Whether the peer supports the address in `_addr`. For example,
// nodes that do not implement BIP155 cannot receive Tor v3 addresses
// because they require ADDRv2 (BIP155) encoding.
const bool addr_format_supported =
m_wants_addrv2 || _addr.IsAddrV1Compatible();

// Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added
// after addresses were pushed.
assert(m_addr_known);
if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey())) {
if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) &&
addr_format_supported) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] =
_addr;
Expand Down
52 changes: 41 additions & 11 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <random.h>
#include <reverse_iterator.h>
#include <scheduler.h>
#include <streams.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <util/check.h> // For NDEBUG compile time check
Expand Down Expand Up @@ -2762,9 +2763,12 @@ void PeerManager::ProcessMessage(const Config &config, CNode &pfrom,
pfrom.SetCommonVersion(greatest_common_version);
pfrom.nVersion = nVersion;

m_connman.PushMessage(
&pfrom,
CNetMsgMaker(greatest_common_version).Make(NetMsgType::VERACK));
const CNetMsgMaker msg_maker(greatest_common_version);

m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));

// Signal ADDRv2 support (BIP155).
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));

pfrom.nServices = nServices;
pfrom.SetAddrLocal(addrMe);
Expand Down Expand Up @@ -2921,17 +2925,27 @@ void PeerManager::ProcessMessage(const Config &config, CNode &pfrom,
return;
}

if (msg_type == NetMsgType::ADDR) {
if (msg_type == NetMsgType::ADDR || msg_type == NetMsgType::ADDRV2) {
int stream_version = vRecv.GetVersion();
if (msg_type == NetMsgType::ADDRV2) {
// Add ADDRV2_FORMAT to the version so that the CNetAddr and
// CAddress unserialize methods know that an address in v2 format is
// coming.
stream_version |= ADDRV2_FORMAT;
}

OverrideStream<CDataStream> s(&vRecv, vRecv.GetType(), stream_version);
std::vector<CAddress> vAddr;
vRecv >> vAddr;

s >> vAddr;

if (!pfrom.IsAddrRelayPeer()) {
return;
}
if (vAddr.size() > 1000) {
Misbehaving(pfrom, 20,
strprintf("oversized-addr: message addr size() = %u",
vAddr.size()));
Misbehaving(
pfrom, 20,
strprintf("%s message size = %u", msg_type, vAddr.size()));
return;
}

Expand Down Expand Up @@ -2984,6 +2998,11 @@ void PeerManager::ProcessMessage(const Config &config, CNode &pfrom,
return;
}

if (msg_type == NetMsgType::SENDADDRV2) {
pfrom.m_wants_addrv2 = true;
return;
}

if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom.GetId())->fPreferHeaders = true;
Expand Down Expand Up @@ -4855,22 +4874,33 @@ bool PeerManager::SendMessages(const Config &config, CNode *pto,
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
assert(pto->m_addr_known);

const char *msg_type;
int make_flags;
if (pto->m_wants_addrv2) {
msg_type = NetMsgType::ADDRV2;
make_flags = ADDRV2_FORMAT;
} else {
msg_type = NetMsgType::ADDR;
make_flags = 0;
}

for (const CAddress &addr : pto->vAddrToSend) {
if (!pto->m_addr_known->contains(addr.GetKey())) {
pto->m_addr_known->insert(addr.GetKey());
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000) {
m_connman.PushMessage(
pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
pto, msgMaker.Make(make_flags, msg_type, vAddr));
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty()) {
m_connman.PushMessage(pto,
msgMaker.Make(NetMsgType::ADDR, vAddr));
m_connman.PushMessage(
pto, msgMaker.Make(make_flags, msg_type, vAddr));
}

// we only send the big addr message once
Expand Down
30 changes: 27 additions & 3 deletions src/netaddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,27 @@ bool CNetAddr::IsInternal() const {
return m_net == NET_INTERNAL;
}

bool CNetAddr::IsAddrV1Compatible() const {
switch (m_net) {
case NET_IPV4:
case NET_IPV6:
case NET_INTERNAL:
return true;
case NET_ONION:
return m_addr.size() == ADDR_TORV2_SIZE;
case NET_I2P:
case NET_CJDNS:
return false;
case NET_UNROUTABLE:
// m_net is never and should not be set to NET_UNROUTABLE
case NET_MAX:
// m_net is never and should not be set to NET_MAX
assert(false);
} // no default case, so the compiler can warn about missing cases

assert(false);
}

enum Network CNetAddr::GetNetwork() const {
if (IsInternal()) {
return NET_INTERNAL;
Expand Down Expand Up @@ -775,9 +796,12 @@ std::vector<uint8_t> CNetAddr::GetGroup(const std::vector<bool> &asmap) const {
}

std::vector<uint8_t> CNetAddr::GetAddrBytes() const {
uint8_t serialized[V1_SERIALIZATION_SIZE];
SerializeV1Array(serialized);
return {std::begin(serialized), std::end(serialized)};
if (IsAddrV1Compatible()) {
uint8_t serialized[V1_SERIALIZATION_SIZE];
SerializeV1Array(serialized);
return {std::begin(serialized), std::end(serialized)};
}
return std::vector<uint8_t>(m_addr.begin(), m_addr.end());
}

uint64_t CNetAddr::GetHash() const {
Expand Down
7 changes: 7 additions & 0 deletions src/netaddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ class CNetAddr {
bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const;

/**
* Check if the current object can be serialized in pre-ADDRv2/BIP155
* format.
*/
bool IsAddrV1Compatible() const;

enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
Expand Down
24 changes: 13 additions & 11 deletions src/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace NetMsgType {
const char *VERSION = "version";
const char *VERACK = "verack";
const char *ADDR = "addr";
const char *ADDRV2 = "addrv2";
const char *SENDADDRV2 = "sendaddrv2";
const char *INV = "inv";
const char *GETDATA = "getdata";
const char *MERKLEBLOCK = "merkleblock";
Expand Down Expand Up @@ -65,17 +67,17 @@ bool IsBlockLike(const std::string &strCommand) {
* above and in protocol.h.
*/
static const std::string allNetMessageTypes[] = {
NetMsgType::VERSION, NetMsgType::VERACK, NetMsgType::ADDR,
NetMsgType::INV, NetMsgType::GETDATA, NetMsgType::MERKLEBLOCK,
NetMsgType::GETBLOCKS, NetMsgType::GETHEADERS, NetMsgType::TX,
NetMsgType::HEADERS, NetMsgType::BLOCK, NetMsgType::GETADDR,
NetMsgType::MEMPOOL, NetMsgType::PING, NetMsgType::PONG,
NetMsgType::NOTFOUND, NetMsgType::FILTERLOAD, NetMsgType::FILTERADD,
NetMsgType::FILTERCLEAR, NetMsgType::SENDHEADERS, NetMsgType::FEEFILTER,
NetMsgType::SENDCMPCT, NetMsgType::CMPCTBLOCK, NetMsgType::GETBLOCKTXN,
NetMsgType::BLOCKTXN, NetMsgType::GETCFILTERS, NetMsgType::CFILTER,
NetMsgType::GETCFHEADERS, NetMsgType::CFHEADERS, NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
NetMsgType::VERSION, NetMsgType::VERACK, NetMsgType::ADDR,
NetMsgType::ADDRV2, NetMsgType::SENDADDRV2, NetMsgType::INV,
NetMsgType::GETDATA, NetMsgType::MERKLEBLOCK, NetMsgType::GETBLOCKS,
NetMsgType::GETHEADERS, NetMsgType::TX, NetMsgType::HEADERS,
NetMsgType::BLOCK, NetMsgType::GETADDR, NetMsgType::MEMPOOL,
NetMsgType::PING, NetMsgType::PONG, NetMsgType::NOTFOUND,
NetMsgType::FILTERLOAD, NetMsgType::FILTERADD, NetMsgType::FILTERCLEAR,
NetMsgType::SENDHEADERS, NetMsgType::FEEFILTER, NetMsgType::SENDCMPCT,
NetMsgType::CMPCTBLOCK, NetMsgType::GETBLOCKTXN, NetMsgType::BLOCKTXN,
NetMsgType::GETCFILTERS, NetMsgType::CFILTER, NetMsgType::GETCFHEADERS,
NetMsgType::CFHEADERS, NetMsgType::GETCFCHECKPT, NetMsgType::CFCHECKPT,
};
static const std::vector<std::string>
allNetMessageTypesVec(allNetMessageTypes,
Expand Down
13 changes: 13 additions & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ extern const char *VERACK;
* @see https://bitcoin.org/en/developer-reference#addr
*/
extern const char *ADDR;
/**
* The addrv2 message relays connection information for peers on the network
* just like the addr message, but is extended to allow gossiping of longer node
* addresses (see BIP155).
*/
extern const char *ADDRV2;
/**
* The sendaddrv2 message signals support for receiving ADDRV2 messages
* (BIP155). It also implies that its sender can encode as ADDRV2 and would send
* ADDRV2 instead of ADDR to a peer that has signaled ADDRV2 support by sending
* SENDADDRV2.
*/
extern const char *SENDADDRV2;
/**
* The inv message (inventory message) transmits one or more inventories of
* objects known to the transmitting peer.
Expand Down
17 changes: 17 additions & 0 deletions src/test/net_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsIPv4());

BOOST_CHECK(addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0");

// IPv4, INADDR_NONE
Expand All @@ -317,6 +318,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsIPv4());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255");

// IPv4, casual
Expand All @@ -325,6 +327,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsIPv4());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78");

// IPv6, in6addr_any
Expand All @@ -333,6 +336,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsIPv6());

BOOST_CHECK(addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "::");

// IPv6, casual
Expand All @@ -342,6 +346,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsIPv6());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(),
"1122:3344:5566:7788:9900:aabb:ccdd:eeff");

Expand All @@ -351,6 +356,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsTor());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");

// TORv3
Expand All @@ -361,6 +367,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsTor());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);

// TORv3, broken, with wrong checksum
Expand Down Expand Up @@ -389,6 +396,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) {
BOOST_REQUIRE(addr.IsInternal());

BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");

// Totally bogus
Expand Down Expand Up @@ -486,6 +494,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv4());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "1.2.3.4");
BOOST_REQUIRE(s.empty());

Expand Down Expand Up @@ -524,6 +533,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv6());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "102:304:506:708:90a:b0c:d0e:f10");
BOOST_REQUIRE(s.empty());

Expand All @@ -536,6 +546,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
// + sha256(name)[0:10]
s >> addr;
BOOST_CHECK(addr.IsInternal());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "zklycewkdo64v6wc.internal");
BOOST_REQUIRE(s.empty());

Expand Down Expand Up @@ -572,6 +583,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsTor());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
BOOST_REQUIRE(s.empty());

Expand All @@ -593,6 +605,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsTor());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(
addr.ToString(),
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion");
Expand All @@ -616,6 +629,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
"f98232ae42d4b6fd2fa81952dfe36a87"));
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsI2P());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(
addr.ToString(),
"ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p");
Expand All @@ -639,6 +654,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) {
));
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsCJDNS());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7");
BOOST_REQUIRE(s.empty());

Expand Down
2 changes: 1 addition & 1 deletion test/functional/p2p_addr_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def run_test(self):

self.log.info('Send too large addr message')
msg.addrs = ADDRS * 101
with self.nodes[0].assert_debug_log(['message addr size() = 1010']):
with self.nodes[0].assert_debug_log(['addr message size = 1010']):
addr_source.send_and_ping(msg)

self.log.info(
Expand Down
Loading

0 comments on commit 15f4d60

Please sign in to comment.