mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-19 05:45:05 +01:00
net: advertise support for ADDRv2 via new message
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. Co-authored-by: Carl Dong <contact@carldong.me>
This commit is contained in:
parent
201a4596d9
commit
353a3fdaad
12
src/net.h
12
src/net.h
@ -868,6 +868,11 @@ public:
|
||||
bool m_legacyWhitelisted{false};
|
||||
bool fClient{false}; // set by version message
|
||||
bool m_limited_node{false}; //after BIP159, set by version message
|
||||
/**
|
||||
* 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
|
||||
@ -1115,11 +1120,16 @@ public:
|
||||
|
||||
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;
|
||||
} else {
|
||||
|
@ -23,6 +23,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
|
||||
@ -2408,11 +2409,16 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
|
||||
pfrom.SetCommonVersion(greatest_common_version);
|
||||
pfrom.nVersion = nVersion;
|
||||
|
||||
const CNetMsgMaker msg_maker(greatest_common_version);
|
||||
|
||||
if (greatest_common_version >= WTXID_RELAY_VERSION) {
|
||||
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::WTXIDRELAY));
|
||||
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY));
|
||||
}
|
||||
|
||||
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::VERACK));
|
||||
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);
|
||||
@ -2582,16 +2588,25 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
|
||||
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.RelayAddrsWithConn()) {
|
||||
return;
|
||||
}
|
||||
if (vAddr.size() > MAX_ADDR_TO_SEND)
|
||||
{
|
||||
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
|
||||
Misbehaving(pfrom.GetId(), 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2635,6 +2650,11 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
|
||||
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;
|
||||
@ -4095,6 +4115,17 @@ bool PeerManager::SendMessages(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()))
|
||||
@ -4104,14 +4135,14 @@ bool PeerManager::SendMessages(CNode* pto)
|
||||
// receiver rejects addr messages larger than MAX_ADDR_TO_SEND
|
||||
if (vAddr.size() >= MAX_ADDR_TO_SEND)
|
||||
{
|
||||
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
|
||||
m_connman.PushMessage(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
|
||||
if (pto->vAddrToSend.capacity() > 40)
|
||||
pto->vAddrToSend.shrink_to_fit();
|
||||
|
@ -474,6 +474,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())
|
||||
@ -744,9 +764,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
|
||||
|
@ -173,6 +173,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;
|
||||
|
@ -14,6 +14,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";
|
||||
@ -52,6 +54,8 @@ const static std::string allNetMessageTypes[] = {
|
||||
NetMsgType::VERSION,
|
||||
NetMsgType::VERACK,
|
||||
NetMsgType::ADDR,
|
||||
NetMsgType::ADDRV2,
|
||||
NetMsgType::SENDADDRV2,
|
||||
NetMsgType::INV,
|
||||
NetMsgType::GETDATA,
|
||||
NetMsgType::MERKLEBLOCK,
|
||||
|
@ -76,6 +76,18 @@ extern const char* VERACK;
|
||||
* network.
|
||||
*/
|
||||
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.
|
||||
|
@ -212,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(), "0.0.0.0");
|
||||
|
||||
// IPv4, INADDR_NONE
|
||||
@ -220,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(), "255.255.255.255");
|
||||
|
||||
// IPv4, casual
|
||||
@ -228,6 +230,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
|
||||
@ -236,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(), "::");
|
||||
|
||||
// IPv6, casual
|
||||
@ -244,6 +248,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
|
||||
@ -252,6 +257,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
|
||||
@ -261,6 +267,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
|
||||
@ -285,6 +292,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
|
||||
@ -379,6 +387,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());
|
||||
|
||||
@ -415,6 +424,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());
|
||||
|
||||
@ -426,6 +436,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());
|
||||
|
||||
@ -461,6 +472,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());
|
||||
|
||||
@ -482,6 +494,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());
|
||||
@ -503,6 +516,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());
|
||||
@ -524,6 +539,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());
|
||||
|
||||
|
79
test/functional/p2p_addrv2_relay.py
Executable file
79
test/functional/p2p_addrv2_relay.py
Executable file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""
|
||||
Test addrv2 relay
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from test_framework.messages import (
|
||||
CAddress,
|
||||
msg_addrv2,
|
||||
NODE_NETWORK,
|
||||
NODE_WITNESS,
|
||||
)
|
||||
from test_framework.p2p import P2PInterface
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
ADDRS = []
|
||||
for i in range(10):
|
||||
addr = CAddress()
|
||||
addr.time = int(time.time()) + i
|
||||
addr.nServices = NODE_NETWORK | NODE_WITNESS
|
||||
addr.ip = "123.123.123.{}".format(i % 256)
|
||||
addr.port = 8333 + i
|
||||
ADDRS.append(addr)
|
||||
|
||||
|
||||
class AddrReceiver(P2PInterface):
|
||||
addrv2_received_and_checked = False
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(support_addrv2 = True)
|
||||
|
||||
def on_addrv2(self, message):
|
||||
for addr in message.addrs:
|
||||
assert_equal(addr.nServices, 9)
|
||||
assert addr.ip.startswith('123.123.123.')
|
||||
assert (8333 <= addr.port < 8343)
|
||||
self.addrv2_received_and_checked = True
|
||||
|
||||
def wait_for_addrv2(self):
|
||||
self.wait_until(lambda: "addrv2" in self.last_message)
|
||||
|
||||
|
||||
class AddrTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def run_test(self):
|
||||
self.log.info('Create connection that sends addrv2 messages')
|
||||
addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
msg = msg_addrv2()
|
||||
|
||||
self.log.info('Send too-large addrv2 message')
|
||||
msg.addrs = ADDRS * 101
|
||||
with self.nodes[0].assert_debug_log(['addrv2 message size = 1010']):
|
||||
addr_source.send_and_ping(msg)
|
||||
|
||||
self.log.info('Check that addrv2 message content is relayed and added to addrman')
|
||||
addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
|
||||
msg.addrs = ADDRS
|
||||
with self.nodes[0].assert_debug_log([
|
||||
'Added 10 addresses from 127.0.0.1: 0 tried',
|
||||
'received: addrv2 (131 bytes) peer=0',
|
||||
'sending addrv2 (131 bytes) peer=1',
|
||||
]):
|
||||
addr_source.send_and_ping(msg)
|
||||
self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
|
||||
addr_receiver.wait_for_addrv2()
|
||||
|
||||
assert addr_receiver.addrv2_received_and_checked
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AddrTest().main()
|
@ -4,6 +4,9 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test node responses to invalid network messages."""
|
||||
|
||||
import struct
|
||||
import time
|
||||
|
||||
from test_framework.messages import (
|
||||
CBlockHeader,
|
||||
CInv,
|
||||
@ -22,7 +25,10 @@ from test_framework.p2p import (
|
||||
P2PInterface,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
hex_str_to_bytes,
|
||||
)
|
||||
|
||||
VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix
|
||||
|
||||
@ -42,6 +48,11 @@ class msg_unrecognized:
|
||||
return "{}(data={})".format(self.msgtype, self.str_data)
|
||||
|
||||
|
||||
class SenderOfAddrV2(P2PInterface):
|
||||
def wait_for_sendaddrv2(self):
|
||||
self.wait_until(lambda: 'sendaddrv2' in self.last_message)
|
||||
|
||||
|
||||
class InvalidMessagesTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
@ -53,6 +64,10 @@ class InvalidMessagesTest(BitcoinTestFramework):
|
||||
self.test_checksum()
|
||||
self.test_size()
|
||||
self.test_msgtype()
|
||||
self.test_addrv2_empty()
|
||||
self.test_addrv2_no_addresses()
|
||||
self.test_addrv2_too_long_address()
|
||||
self.test_addrv2_unrecognized_network()
|
||||
self.test_oversized_inv_msg()
|
||||
self.test_oversized_getdata_msg()
|
||||
self.test_oversized_headers_msg()
|
||||
@ -127,6 +142,84 @@ class InvalidMessagesTest(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
|
||||
self.nodes[0].disconnect_p2ps()
|
||||
|
||||
def test_addrv2(self, label, required_log_messages, raw_addrv2):
|
||||
node = self.nodes[0]
|
||||
conn = node.add_p2p_connection(SenderOfAddrV2())
|
||||
|
||||
# Make sure bitcoind signals support for ADDRv2, otherwise this test
|
||||
# will bombard an old node with messages it does not recognize which
|
||||
# will produce unexpected results.
|
||||
conn.wait_for_sendaddrv2()
|
||||
|
||||
self.log.info('Test addrv2: ' + label)
|
||||
|
||||
msg = msg_unrecognized(str_data=b'')
|
||||
msg.msgtype = b'addrv2'
|
||||
with node.assert_debug_log(required_log_messages):
|
||||
# override serialize() which would include the length of the data
|
||||
msg.serialize = lambda: raw_addrv2
|
||||
conn.send_raw_message(conn.build_message(msg))
|
||||
conn.sync_with_ping()
|
||||
|
||||
node.disconnect_p2ps()
|
||||
|
||||
def test_addrv2_empty(self):
|
||||
self.test_addrv2('empty',
|
||||
[
|
||||
'received: addrv2 (0 bytes)',
|
||||
'ProcessMessages(addrv2, 0 bytes): Exception',
|
||||
'end of data',
|
||||
],
|
||||
b'')
|
||||
|
||||
def test_addrv2_no_addresses(self):
|
||||
self.test_addrv2('no addresses',
|
||||
[
|
||||
'received: addrv2 (1 bytes)',
|
||||
],
|
||||
hex_str_to_bytes('00'))
|
||||
|
||||
def test_addrv2_too_long_address(self):
|
||||
self.test_addrv2('too long address',
|
||||
[
|
||||
'received: addrv2 (525 bytes)',
|
||||
'ProcessMessages(addrv2, 525 bytes): Exception',
|
||||
'Address too long: 513 > 512',
|
||||
],
|
||||
hex_str_to_bytes(
|
||||
'01' + # number of entries
|
||||
'61bc6649' + # time, Fri Jan 9 02:54:25 UTC 2009
|
||||
'00' + # service flags, COMPACTSIZE(NODE_NONE)
|
||||
'01' + # network type (IPv4)
|
||||
'fd0102' + # address length (COMPACTSIZE(513))
|
||||
'ab' * 513 + # address
|
||||
'208d')) # port
|
||||
|
||||
def test_addrv2_unrecognized_network(self):
|
||||
now_hex = struct.pack('<I', int(time.time())).hex()
|
||||
self.test_addrv2('unrecognized network',
|
||||
[
|
||||
'received: addrv2 (25 bytes)',
|
||||
'IP 9.9.9.9 mapped',
|
||||
'Added 1 addresses',
|
||||
],
|
||||
hex_str_to_bytes(
|
||||
'02' + # number of entries
|
||||
# this should be ignored without impeding acceptance of subsequent ones
|
||||
now_hex + # time
|
||||
'01' + # service flags, COMPACTSIZE(NODE_NETWORK)
|
||||
'99' + # network type (unrecognized)
|
||||
'02' + # address length (COMPACTSIZE(2))
|
||||
'ab' * 2 + # address
|
||||
'208d' + # port
|
||||
# this should be added:
|
||||
now_hex + # time
|
||||
'01' + # service flags, COMPACTSIZE(NODE_NETWORK)
|
||||
'01' + # network type (IPv4)
|
||||
'04' + # address length (COMPACTSIZE(4))
|
||||
'09' * 4 + # address
|
||||
'208d')) # port
|
||||
|
||||
def test_oversized_msg(self, msg, size):
|
||||
msg_type = msg.msgtype.decode('ascii')
|
||||
self.log.info("Test {} message of size {} is logged as misbehaving".format(msg_type, size))
|
||||
|
@ -136,12 +136,17 @@ def uint256_from_compact(c):
|
||||
return v
|
||||
|
||||
|
||||
def deser_vector(f, c):
|
||||
# deser_function_name: Allow for an alternate deserialization function on the
|
||||
# entries in the vector.
|
||||
def deser_vector(f, c, deser_function_name=None):
|
||||
nit = deser_compact_size(f)
|
||||
r = []
|
||||
for _ in range(nit):
|
||||
t = c()
|
||||
t.deserialize(f)
|
||||
if deser_function_name:
|
||||
getattr(t, deser_function_name)(f)
|
||||
else:
|
||||
t.deserialize(f)
|
||||
r.append(t)
|
||||
return r
|
||||
|
||||
@ -204,38 +209,82 @@ def ToHex(obj):
|
||||
|
||||
|
||||
class CAddress:
|
||||
__slots__ = ("ip", "nServices", "pchReserved", "port", "time")
|
||||
__slots__ = ("net", "ip", "nServices", "port", "time")
|
||||
|
||||
# see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
|
||||
NET_IPV4 = 1
|
||||
|
||||
ADDRV2_NET_NAME = {
|
||||
NET_IPV4: "IPv4"
|
||||
}
|
||||
|
||||
ADDRV2_ADDRESS_LENGTH = {
|
||||
NET_IPV4: 4
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.time = 0
|
||||
self.nServices = 1
|
||||
self.pchReserved = b"\x00" * 10 + b"\xff" * 2
|
||||
self.net = self.NET_IPV4
|
||||
self.ip = "0.0.0.0"
|
||||
self.port = 0
|
||||
|
||||
def deserialize(self, f, *, with_time=True):
|
||||
"""Deserialize from addrv1 format (pre-BIP155)"""
|
||||
if with_time:
|
||||
# VERSION messages serialize CAddress objects without time
|
||||
self.time = struct.unpack("<i", f.read(4))[0]
|
||||
self.time = struct.unpack("<I", f.read(4))[0]
|
||||
self.nServices = struct.unpack("<Q", f.read(8))[0]
|
||||
self.pchReserved = f.read(12)
|
||||
# We only support IPv4 which means skip 12 bytes and read the next 4 as IPv4 address.
|
||||
f.read(12)
|
||||
self.net = self.NET_IPV4
|
||||
self.ip = socket.inet_ntoa(f.read(4))
|
||||
self.port = struct.unpack(">H", f.read(2))[0]
|
||||
|
||||
def serialize(self, *, with_time=True):
|
||||
"""Serialize in addrv1 format (pre-BIP155)"""
|
||||
assert self.net == self.NET_IPV4
|
||||
r = b""
|
||||
if with_time:
|
||||
# VERSION messages serialize CAddress objects without time
|
||||
r += struct.pack("<i", self.time)
|
||||
r += struct.pack("<I", self.time)
|
||||
r += struct.pack("<Q", self.nServices)
|
||||
r += self.pchReserved
|
||||
r += b"\x00" * 10 + b"\xff" * 2
|
||||
r += socket.inet_aton(self.ip)
|
||||
r += struct.pack(">H", self.port)
|
||||
return r
|
||||
|
||||
def deserialize_v2(self, f):
|
||||
"""Deserialize from addrv2 format (BIP155)"""
|
||||
self.time = struct.unpack("<I", f.read(4))[0]
|
||||
|
||||
self.nServices = deser_compact_size(f)
|
||||
|
||||
self.net = struct.unpack("B", f.read(1))[0]
|
||||
assert self.net == self.NET_IPV4
|
||||
|
||||
address_length = deser_compact_size(f)
|
||||
assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]
|
||||
|
||||
self.ip = socket.inet_ntoa(f.read(4))
|
||||
|
||||
self.port = struct.unpack(">H", f.read(2))[0]
|
||||
|
||||
def serialize_v2(self):
|
||||
"""Serialize in addrv2 format (BIP155)"""
|
||||
assert self.net == self.NET_IPV4
|
||||
r = b""
|
||||
r += struct.pack("<I", self.time)
|
||||
r += ser_compact_size(self.nServices)
|
||||
r += struct.pack("B", self.net)
|
||||
r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])
|
||||
r += socket.inet_aton(self.ip)
|
||||
r += struct.pack(">H", self.port)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
|
||||
self.ip, self.port)
|
||||
return ("CAddress(nServices=%i net=%s addr=%s port=%i)"
|
||||
% (self.nServices, self.ADDRV2_NET_NAME[self.net], self.ip, self.port))
|
||||
|
||||
|
||||
class CInv:
|
||||
@ -1064,6 +1113,40 @@ class msg_addr:
|
||||
return "msg_addr(addrs=%s)" % (repr(self.addrs))
|
||||
|
||||
|
||||
class msg_addrv2:
|
||||
__slots__ = ("addrs",)
|
||||
msgtype = b"addrv2"
|
||||
|
||||
def __init__(self):
|
||||
self.addrs = []
|
||||
|
||||
def deserialize(self, f):
|
||||
self.addrs = deser_vector(f, CAddress, "deserialize_v2")
|
||||
|
||||
def serialize(self):
|
||||
return ser_vector(self.addrs, "serialize_v2")
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_addrv2(addrs=%s)" % (repr(self.addrs))
|
||||
|
||||
|
||||
class msg_sendaddrv2:
|
||||
__slots__ = ()
|
||||
msgtype = b"sendaddrv2"
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def deserialize(self, f):
|
||||
pass
|
||||
|
||||
def serialize(self):
|
||||
return b""
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_sendaddrv2()"
|
||||
|
||||
|
||||
class msg_inv:
|
||||
__slots__ = ("inv",)
|
||||
msgtype = b"inv"
|
||||
|
@ -33,6 +33,7 @@ from test_framework.messages import (
|
||||
MAX_HEADERS_RESULTS,
|
||||
MIN_VERSION_SUPPORTED,
|
||||
msg_addr,
|
||||
msg_addrv2,
|
||||
msg_block,
|
||||
MSG_BLOCK,
|
||||
msg_blocktxn,
|
||||
@ -56,6 +57,7 @@ from test_framework.messages import (
|
||||
msg_notfound,
|
||||
msg_ping,
|
||||
msg_pong,
|
||||
msg_sendaddrv2,
|
||||
msg_sendcmpct,
|
||||
msg_sendheaders,
|
||||
msg_tx,
|
||||
@ -75,6 +77,7 @@ logger = logging.getLogger("TestFramework.p2p")
|
||||
|
||||
MESSAGEMAP = {
|
||||
b"addr": msg_addr,
|
||||
b"addrv2": msg_addrv2,
|
||||
b"block": msg_block,
|
||||
b"blocktxn": msg_blocktxn,
|
||||
b"cfcheckpt": msg_cfcheckpt,
|
||||
@ -97,6 +100,7 @@ MESSAGEMAP = {
|
||||
b"notfound": msg_notfound,
|
||||
b"ping": msg_ping,
|
||||
b"pong": msg_pong,
|
||||
b"sendaddrv2": msg_sendaddrv2,
|
||||
b"sendcmpct": msg_sendcmpct,
|
||||
b"sendheaders": msg_sendheaders,
|
||||
b"tx": msg_tx,
|
||||
@ -285,7 +289,7 @@ class P2PInterface(P2PConnection):
|
||||
|
||||
Individual testcases should subclass this and override the on_* methods
|
||||
if they want to alter message handling behaviour."""
|
||||
def __init__(self):
|
||||
def __init__(self, support_addrv2=False):
|
||||
super().__init__()
|
||||
|
||||
# Track number of messages of each type received.
|
||||
@ -303,6 +307,8 @@ class P2PInterface(P2PConnection):
|
||||
# The network services received from the peer
|
||||
self.nServices = 0
|
||||
|
||||
self.support_addrv2 = support_addrv2
|
||||
|
||||
def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs):
|
||||
create_conn = super().peer_connect(*args, **kwargs)
|
||||
|
||||
@ -345,6 +351,7 @@ class P2PInterface(P2PConnection):
|
||||
pass
|
||||
|
||||
def on_addr(self, message): pass
|
||||
def on_addrv2(self, message): pass
|
||||
def on_block(self, message): pass
|
||||
def on_blocktxn(self, message): pass
|
||||
def on_cfcheckpt(self, message): pass
|
||||
@ -365,6 +372,7 @@ class P2PInterface(P2PConnection):
|
||||
def on_merkleblock(self, message): pass
|
||||
def on_notfound(self, message): pass
|
||||
def on_pong(self, message): pass
|
||||
def on_sendaddrv2(self, message): pass
|
||||
def on_sendcmpct(self, message): pass
|
||||
def on_sendheaders(self, message): pass
|
||||
def on_tx(self, message): pass
|
||||
@ -389,6 +397,8 @@ class P2PInterface(P2PConnection):
|
||||
if message.nVersion >= 70016:
|
||||
self.send_message(msg_wtxidrelay())
|
||||
self.send_message(msg_verack())
|
||||
if self.support_addrv2:
|
||||
self.send_message(msg_sendaddrv2())
|
||||
self.nServices = message.nServices
|
||||
|
||||
# Connection helper methods
|
||||
|
@ -154,6 +154,7 @@ BASE_SCRIPTS = [
|
||||
'feature_proxy.py',
|
||||
'rpc_signrawtransaction.py',
|
||||
'wallet_groups.py',
|
||||
'p2p_addrv2_relay.py',
|
||||
'p2p_disconnect_ban.py',
|
||||
'rpc_decodescript.py',
|
||||
'rpc_blockchain.py',
|
||||
|
Loading…
Reference in New Issue
Block a user