Skip to content

Commit

Permalink
net: advertise support for ADDRv2 via new message
Browse files Browse the repository at this point in the history
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.

Adaptation of btc@353a3fdaad055eea42a0baf7326bdd591f541170 with some further changes
  • Loading branch information
furszy committed Aug 10, 2021
1 parent e58d5d0 commit 6da9a14
Show file tree
Hide file tree
Showing 13 changed files with 430 additions and 66 deletions.
2 changes: 0 additions & 2 deletions src/masternode-sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,6 @@ void CMasternodeSync::Process()
return;
}

LogPrint(BCLog::MASTERNODE, "CMasternodeSync::Process() - tick %d RequestedMasternodeAssets %d\n", tick, RequestedMasternodeAssets);

if (RequestedMasternodeAssets == MASTERNODE_SYNC_INITIAL) SwitchToNextAsset();

// sporks synced but blockchain is not, wait until we're almost at a recent block to continue
Expand Down
12 changes: 11 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@ class CNode
bool fClient;
const bool fInbound;
bool fNetworkNode;
/**
* 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;
std::atomic_bool fDisconnect;
// We use fRelayTxes for two purposes -
Expand Down Expand Up @@ -721,10 +726,15 @@ 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.
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey()) && addr_format_supported) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
Expand Down
42 changes: 35 additions & 7 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "sporkdb.h"
#include "streams.h"

int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last received a block

Expand Down Expand Up @@ -1146,8 +1147,6 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
if (pfrom->fInbound)
PushNodeVersion(pfrom, connman, GetAdjustedTime());

connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));

pfrom->nServices = nServices;
pfrom->SetAddrLocal(addrMe);
{
Expand All @@ -1171,6 +1170,12 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
pfrom->SetSendVersion(nSendVersion);
pfrom->nVersion = nVersion;

CNetMsgMaker msg_maker(INIT_PROTO_VERSION);
connman->PushMessage(pfrom, msg_maker.Make(NetMsgType::VERACK));

// Signal ADDRv2 support (BIP155).
connman->PushMessage(pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));

{
LOCK(cs_main);
// Potentially mark this peer as a preferred download peer.
Expand Down Expand Up @@ -1270,16 +1275,24 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
}


else if (strCommand == NetMsgType::ADDR) {
else if (strCommand == NetMsgType::ADDR || strCommand == NetMsgType::ADDRV2) {
int stream_version = vRecv.GetVersion();
if (strCommand == 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;

// Don't want addr from older versions unless seeding
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
return true;
if (vAddr.size() > MAX_ADDR_TO_SEND) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
Misbehaving(pfrom->GetId(), 20, strprintf("%s message size = %u", strCommand, vAddr.size()));
return false;
}

Expand Down Expand Up @@ -1313,6 +1326,10 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
pfrom->fDisconnect = true;
}

else if (strCommand == NetMsgType::SENDADDRV2) {
pfrom->m_wants_addrv2 = true;
return true;
}

else if (strCommand == NetMsgType::INV) {
std::vector<CInv> vInv;
Expand Down Expand Up @@ -2089,20 +2106,31 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());

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->addrKnown.contains(addr.GetKey())) {
pto->addrKnown.insert(addr.GetKey());
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
connman->PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
connman->PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
}

// Start block sync
Expand Down
29 changes: 26 additions & 3 deletions src/netaddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,26 @@ 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())
Expand Down Expand Up @@ -731,9 +751,12 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co

std::vector<unsigned char> 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<unsigned char>(m_addr.begin(), m_addr.end());
}

uint64_t CNetAddr::GetHash() const
Expand Down
6 changes: 6 additions & 0 deletions src/netaddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ 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
4 changes: 4 additions & 0 deletions src/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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 @@ -59,6 +61,8 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::VERSION,
NetMsgType::VERACK,
NetMsgType::ADDR,
NetMsgType::ADDRV2,
NetMsgType::SENDADDRV2,
NetMsgType::INV,
NetMsgType::GETDATA,
NetMsgType::MERKLEBLOCK,
Expand Down
12 changes: 12 additions & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ 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 @@ -203,6 +203,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 @@ -211,6 +212,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 @@ -219,6 +221,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 @@ -227,6 +230,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 @@ -235,6 +239,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");

// TORv2
Expand All @@ -243,6 +248,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 @@ -252,6 +258,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 All @@ -276,6 +283,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 @@ -370,6 +378,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 @@ -406,6 +415,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 @@ -417,6 +427,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 @@ -452,6 +463,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 @@ -473,6 +485,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");
BOOST_REQUIRE(s.empty());
Expand All @@ -494,6 +507,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");
BOOST_REQUIRE(s.empty());
Expand All @@ -515,6 +530,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
Loading

0 comments on commit 6da9a14

Please sign in to comment.