diff options
author | Vasil Dimov <vd@FreeBSD.org> | 2020-05-20 12:05:18 +0200 |
---|---|---|
committer | Vasil Dimov <vd@FreeBSD.org> | 2020-10-09 16:42:50 +0200 |
commit | 353a3fdaad055eea42a0baf7326bdd591f541170 (patch) | |
tree | ab25e783cbf347df239d98060f83dcf2ea72209a /test/functional/test_framework | |
parent | 201a4596d92d640d5eb7e76cc8d959228fa09dbb (diff) | |
download | bitcoin-353a3fdaad055eea42a0baf7326bdd591f541170.tar.xz |
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>
Diffstat (limited to 'test/functional/test_framework')
-rwxr-xr-x | test/functional/test_framework/messages.py | 103 | ||||
-rwxr-xr-x | test/functional/test_framework/p2p.py | 12 |
2 files changed, 104 insertions, 11 deletions
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 00cf1ef66d..ff7f73bdf4 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -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" diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 5f9b316b18..6846d31221 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -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 |