Skip to content

Commit a88fa1a

Browse files
committed
Merge bitcoin/bitcoin#22211: net: relay I2P addresses even if not reachable (by us)
7593b06 test: ensure I2P addresses are relayed (Vasil Dimov) e746813 test: make CAddress in functional tests comparable (Vasil Dimov) 33e211d test: implement ser/unser of I2P addresses in functional tests (Vasil Dimov) 8674281 test: use NODE_* constants instead of magic numbers (Vasil Dimov) ba45f02 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 bitcoin/bitcoin#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 7593b06 naumenkogs: ACK 7593b06. laanwj: Code review ACK 7593b06 kristapsk: ACK 7593b06. 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
2 parents 853ac47 + 7593b06 commit a88fa1a

File tree

3 files changed

+41
-16
lines changed

3 files changed

+41
-16
lines changed

src/netaddress.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class CNetAddr
227227
*/
228228
bool IsRelayable() const
229229
{
230-
return IsIPv4() || IsIPv6() || IsTor();
230+
return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
231231
}
232232

233233
/**

test/functional/p2p_addrv2_relay.py

+16-9
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@
1818
from test_framework.test_framework import BitcoinTestFramework
1919
from test_framework.util import assert_equal
2020

21+
I2P_ADDR = "c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p"
22+
2123
ADDRS = []
2224
for i in range(10):
2325
addr = CAddress()
2426
addr.time = int(time.time()) + i
2527
addr.nServices = NODE_NETWORK | NODE_WITNESS
26-
addr.ip = "123.123.123.{}".format(i % 256)
28+
# Add one I2P address at an arbitrary position.
29+
if i == 5:
30+
addr.net = addr.NET_I2P
31+
addr.ip = I2P_ADDR
32+
else:
33+
addr.ip = f"123.123.123.{i % 256}"
2734
addr.port = 8333 + i
2835
ADDRS.append(addr)
2936

@@ -35,11 +42,8 @@ def __init__(self):
3542
super().__init__(support_addrv2 = True)
3643

3744
def on_addrv2(self, message):
38-
for addr in message.addrs:
39-
assert_equal(addr.nServices, 9)
40-
assert addr.ip.startswith('123.123.123.')
41-
assert (8333 <= addr.port < 8343)
42-
self.addrv2_received_and_checked = True
45+
if ADDRS == message.addrs:
46+
self.addrv2_received_and_checked = True
4347

4448
def wait_for_addrv2(self):
4549
self.wait_until(lambda: "addrv2" in self.last_message)
@@ -64,15 +68,18 @@ def run_test(self):
6468
addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
6569
msg.addrs = ADDRS
6670
with self.nodes[0].assert_debug_log([
67-
'Added 10 addresses from 127.0.0.1: 0 tried',
68-
'received: addrv2 (131 bytes) peer=0',
69-
'sending addrv2 (131 bytes) peer=1',
71+
# The I2P address is not added to node's own addrman because it has no
72+
# I2P reachability (thus 10 - 1 = 9).
73+
'Added 9 addresses from 127.0.0.1: 0 tried',
74+
'received: addrv2 (159 bytes) peer=0',
75+
'sending addrv2 (159 bytes) peer=1',
7076
]):
7177
addr_source.send_and_ping(msg)
7278
self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
7379
addr_receiver.wait_for_addrv2()
7480

7581
assert addr_receiver.addrv2_received_and_checked
82+
assert_equal(len(self.nodes[0].getnodeaddresses(count=0, network="i2p")), 0)
7683

7784

7885
if __name__ == '__main__':

test/functional/test_framework/messages.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Classes use __slots__ to ensure extraneous attributes aren't accidentally added
1919
by tests, compromising their intended effect.
2020
"""
21+
from base64 import b32decode, b32encode
2122
from codecs import encode
2223
import copy
2324
import hashlib
@@ -213,22 +214,30 @@ class CAddress:
213214

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

217219
ADDRV2_NET_NAME = {
218-
NET_IPV4: "IPv4"
220+
NET_IPV4: "IPv4",
221+
NET_I2P: "I2P"
219222
}
220223

221224
ADDRV2_ADDRESS_LENGTH = {
222-
NET_IPV4: 4
225+
NET_IPV4: 4,
226+
NET_I2P: 32
223227
}
224228

229+
I2P_PAD = "===="
230+
225231
def __init__(self):
226232
self.time = 0
227233
self.nServices = 1
228234
self.net = self.NET_IPV4
229235
self.ip = "0.0.0.0"
230236
self.port = 0
231237

238+
def __eq__(self, other):
239+
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
240+
232241
def deserialize(self, f, *, with_time=True):
233242
"""Deserialize from addrv1 format (pre-BIP155)"""
234243
if with_time:
@@ -261,24 +270,33 @@ def deserialize_v2(self, f):
261270
self.nServices = deser_compact_size(f)
262271

263272
self.net = struct.unpack("B", f.read(1))[0]
264-
assert self.net == self.NET_IPV4
273+
assert self.net in (self.NET_IPV4, self.NET_I2P)
265274

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

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

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

273286
def serialize_v2(self):
274287
"""Serialize in addrv2 format (BIP155)"""
275-
assert self.net == self.NET_IPV4
288+
assert self.net in (self.NET_IPV4, self.NET_I2P)
276289
r = b""
277290
r += struct.pack("<I", self.time)
278291
r += ser_compact_size(self.nServices)
279292
r += struct.pack("B", self.net)
280293
r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])
281-
r += socket.inet_aton(self.ip)
294+
if self.net == self.NET_IPV4:
295+
r += socket.inet_aton(self.ip)
296+
else:
297+
sfx = ".b32.i2p"
298+
assert self.ip.endswith(sfx)
299+
r += b32decode(self.ip[0:-len(sfx)] + self.I2P_PAD, True)
282300
r += struct.pack(">H", self.port)
283301
return r
284302

0 commit comments

Comments
 (0)