aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorW. J. van der Laan <laanwj@protonmail.com>2021-07-15 16:53:30 +0200
committerW. J. van der Laan <laanwj@protonmail.com>2021-07-15 16:53:34 +0200
commita88fa1a555195af6543cdc802e8e1c2e1668dd38 (patch)
treec49952c0f17979c2b4bf3a7787e304b9ea6ea07d
parent853ac47705c8abd9b8bda2da59eb043d9591491c (diff)
parent7593b06bd1262f438bf34769ecc00e9c22328e56 (diff)
downloadbitcoin-a88fa1a555195af6543cdc802e8e1c2e1668dd38.tar.xz
Merge bitcoin/bitcoin#22211: net: relay I2P addresses even if not reachable (by us)
7593b06bd1262f438bf34769ecc00e9c22328e56 test: ensure I2P addresses are relayed (Vasil Dimov) e7468139a1dddd4946bc70697ec38816b3fa8f9b test: make CAddress in functional tests comparable (Vasil Dimov) 33e211d2a442e4555ff3401f92af4ee2f7552568 test: implement ser/unser of I2P addresses in functional tests (Vasil Dimov) 86742811ce3662789ac85334008090a3b54babe3 test: use NODE_* constants instead of magic numbers (Vasil Dimov) ba45f0270815d54ae3290efc16324c2ff1984565 net: relay I2P addresses even if not reachable (by us) (Vasil Dimov) Pull request description: Nodes that can reach the I2P network (have set `-i2psam=`) will relay I2P addresses even without this patch. However, nodes that can't reach the I2P network will not. This was done as a precaution in https://github.com/bitcoin/bitcoin/pull/20119 before anybody could connect to I2P because then, for sure, it would have been useless. Now, however, we have I2P support and a bunch of I2P nodes, so get all nodes on the network to relay I2P addresses to help with propagation, similarly to what we do with Tor addresses. ACKs for top commit: jonatack: ACK 7593b06bd1262f438bf34769ecc00e9c22328e56 naumenkogs: ACK 7593b06bd1262f438bf34769ecc00e9c22328e56. laanwj: Code review ACK 7593b06bd1262f438bf34769ecc00e9c22328e56 kristapsk: ACK 7593b06bd1262f438bf34769ecc00e9c22328e56. Code looks correct, tested that functional test suite passes and also that `test/functional/p2p_addrv2_replay.py` fails if I undo changes in `IsRelayable()`. Tree-SHA512: c9feec4a9546cc06bc2fec6d74f999a3c0abd3d15b7c421c21fcf2d610eb94611489e33d61bdcd5a4f42041a6d84aa892f7ae293b0d4f755309a8560b113b735
-rw-r--r--src/netaddress.h2
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py25
-rwxr-xr-xtest/functional/test_framework/messages.py30
3 files changed, 41 insertions, 16 deletions
diff --git a/src/netaddress.h b/src/netaddress.h
index d504996347..5e1d9d2a6f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -227,7 +227,7 @@ class CNetAddr
*/
bool IsRelayable() const
{
- return IsIPv4() || IsIPv6() || IsTor();
+ return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
}
/**
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
index 23ce3e5d04..c006a6c24f 100755
--- a/test/functional/p2p_addrv2_relay.py
+++ b/test/functional/p2p_addrv2_relay.py
@@ -18,12 +18,19 @@ from test_framework.p2p import P2PInterface
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)
@@ -35,11 +42,8 @@ class AddrReceiver(P2PInterface):
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)
@@ -64,15 +68,18 @@ class AddrTest(BitcoinTestFramework):
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',
]):
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__':
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 504c8c70d4..065e8961ae 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -18,6 +18,7 @@ ser_*, deser_*: functions that handle serialization/deserialization.
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
@@ -213,15 +214,20 @@ 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
@@ -229,6 +235,9 @@ class CAddress:
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:
@@ -261,24 +270,33 @@ class CAddress:
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