Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: relay I2P addresses even if not reachable (by us) #22211

Merged
merged 5 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/netaddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class CNetAddr
*/
bool IsRelayable() const
{
return IsIPv4() || IsIPv6() || IsTor();
return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
}

/**
Expand Down
25 changes: 16 additions & 9 deletions test/functional/p2p_addrv2_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal

I2P_ADDR = "c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p"

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)
# Add one I2P address at an arbitrary position.
if i == 5:
addr.net = addr.NET_I2P
addr.ip = I2P_ADDR
else:
addr.ip = f"123.123.123.{i % 256}"
addr.port = 8333 + i
ADDRS.append(addr)

Expand All @@ -35,11 +42,8 @@ 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
if ADDRS == message.addrs:
self.addrv2_received_and_checked = True

def wait_for_addrv2(self):
self.wait_until(lambda: "addrv2" in self.last_message)
Expand All @@ -64,15 +68,18 @@ def run_test(self):
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',
# The I2P address is not added to node's own addrman because it has no
# I2P reachability (thus 10 - 1 = 9).
'Added 9 addresses from 127.0.0.1: 0 tried',
'received: addrv2 (159 bytes) peer=0',
'sending addrv2 (159 bytes) peer=1',
vasild marked this conversation as resolved.
Show resolved Hide resolved
]):
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
assert_equal(len(self.nodes[0].getnodeaddresses(count=0, network="i2p")), 0)


if __name__ == '__main__':
Expand Down
30 changes: 24 additions & 6 deletions test/functional/test_framework/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Classes use __slots__ to ensure extraneous attributes aren't accidentally added
by tests, compromising their intended effect.
"""
from base64 import b32decode, b32encode
from codecs import encode
import copy
import hashlib
Expand Down Expand Up @@ -207,22 +208,30 @@ class CAddress:

# see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
NET_IPV4 = 1
NET_I2P = 5

ADDRV2_NET_NAME = {
NET_IPV4: "IPv4"
NET_IPV4: "IPv4",
NET_I2P: "I2P"
}

ADDRV2_ADDRESS_LENGTH = {
NET_IPV4: 4
NET_IPV4: 4,
NET_I2P: 32
}

I2P_PAD = "===="

def __init__(self):
self.time = 0
self.nServices = 1
self.net = self.NET_IPV4
self.ip = "0.0.0.0"
self.port = 0

def __eq__(self, other):
return self.net == other.net and self.ip == other.ip and self.nServices == other.nServices and self.port == other.port and self.time == other.time

def deserialize(self, f, *, with_time=True):
"""Deserialize from addrv1 format (pre-BIP155)"""
if with_time:
Expand Down Expand Up @@ -255,24 +264,33 @@ def deserialize_v2(self, f):
self.nServices = deser_compact_size(f)

self.net = struct.unpack("B", f.read(1))[0]
assert self.net == self.NET_IPV4
assert self.net in (self.NET_IPV4, self.NET_I2P)

address_length = deser_compact_size(f)
assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]

self.ip = socket.inet_ntoa(f.read(4))
addr_bytes = f.read(address_length)
if self.net == self.NET_IPV4:
self.ip = socket.inet_ntoa(addr_bytes)
else:
self.ip = b32encode(addr_bytes)[0:-len(self.I2P_PAD)].decode("ascii").lower() + ".b32.i2p"

self.port = struct.unpack(">H", f.read(2))[0]

def serialize_v2(self):
"""Serialize in addrv2 format (BIP155)"""
assert self.net == self.NET_IPV4
assert self.net in (self.NET_IPV4, self.NET_I2P)
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)
if self.net == self.NET_IPV4:
r += socket.inet_aton(self.ip)
else:
sfx = ".b32.i2p"
assert self.ip.endswith(sfx)
r += b32decode(self.ip[0:-len(sfx)] + self.I2P_PAD, True)
r += struct.pack(">H", self.port)
return r

Expand Down