|
27 | 27 | import socket
|
28 | 28 | import struct
|
29 | 29 | import time
|
| 30 | +import unittest |
30 | 31 |
|
31 | 32 | from test_framework.siphash import siphash256
|
32 | 33 | from test_framework.util import assert_equal
|
@@ -77,6 +78,10 @@ def sha256(s):
|
77 | 78 | return hashlib.sha256(s).digest()
|
78 | 79 |
|
79 | 80 |
|
| 81 | +def sha3(s): |
| 82 | + return hashlib.sha3_256(s).digest() |
| 83 | + |
| 84 | + |
80 | 85 | def hash256(s):
|
81 | 86 | return sha256(sha256(s))
|
82 | 87 |
|
@@ -229,16 +234,25 @@ class CAddress:
|
229 | 234 |
|
230 | 235 | # see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
|
231 | 236 | NET_IPV4 = 1
|
| 237 | + NET_IPV6 = 2 |
| 238 | + NET_TORV3 = 4 |
232 | 239 | NET_I2P = 5
|
| 240 | + NET_CJDNS = 6 |
233 | 241 |
|
234 | 242 | ADDRV2_NET_NAME = {
|
235 | 243 | NET_IPV4: "IPv4",
|
236 |
| - NET_I2P: "I2P" |
| 244 | + NET_IPV6: "IPv6", |
| 245 | + NET_TORV3: "TorV3", |
| 246 | + NET_I2P: "I2P", |
| 247 | + NET_CJDNS: "CJDNS" |
237 | 248 | }
|
238 | 249 |
|
239 | 250 | ADDRV2_ADDRESS_LENGTH = {
|
240 | 251 | NET_IPV4: 4,
|
241 |
| - NET_I2P: 32 |
| 252 | + NET_IPV6: 16, |
| 253 | + NET_TORV3: 32, |
| 254 | + NET_I2P: 32, |
| 255 | + NET_CJDNS: 16 |
242 | 256 | }
|
243 | 257 |
|
244 | 258 | I2P_PAD = "===="
|
@@ -285,33 +299,54 @@ def deserialize_v2(self, f):
|
285 | 299 | self.nServices = deser_compact_size(f)
|
286 | 300 |
|
287 | 301 | self.net = struct.unpack("B", f.read(1))[0]
|
288 |
| - assert self.net in (self.NET_IPV4, self.NET_I2P) |
| 302 | + assert self.net in self.ADDRV2_NET_NAME |
289 | 303 |
|
290 | 304 | address_length = deser_compact_size(f)
|
291 | 305 | assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]
|
292 | 306 |
|
293 | 307 | addr_bytes = f.read(address_length)
|
294 | 308 | if self.net == self.NET_IPV4:
|
295 | 309 | self.ip = socket.inet_ntoa(addr_bytes)
|
296 |
| - else: |
| 310 | + elif self.net == self.NET_IPV6: |
| 311 | + self.ip = socket.inet_ntop(socket.AF_INET6, addr_bytes) |
| 312 | + elif self.net == self.NET_TORV3: |
| 313 | + prefix = b".onion checksum" |
| 314 | + version = bytes([3]) |
| 315 | + checksum = sha3(prefix + addr_bytes + version)[:2] |
| 316 | + self.ip = b32encode(addr_bytes + checksum + version).decode("ascii").lower() + ".onion" |
| 317 | + elif self.net == self.NET_I2P: |
297 | 318 | self.ip = b32encode(addr_bytes)[0:-len(self.I2P_PAD)].decode("ascii").lower() + ".b32.i2p"
|
| 319 | + elif self.net == self.NET_CJDNS: |
| 320 | + self.ip = socket.inet_ntop(socket.AF_INET6, addr_bytes) |
| 321 | + else: |
| 322 | + raise Exception(f"Address type not supported") |
298 | 323 |
|
299 | 324 | self.port = struct.unpack(">H", f.read(2))[0]
|
300 | 325 |
|
301 | 326 | def serialize_v2(self):
|
302 | 327 | """Serialize in addrv2 format (BIP155)"""
|
303 |
| - assert self.net in (self.NET_IPV4, self.NET_I2P) |
| 328 | + assert self.net in self.ADDRV2_NET_NAME |
304 | 329 | r = b""
|
305 | 330 | r += struct.pack("<I", self.time)
|
306 | 331 | r += ser_compact_size(self.nServices)
|
307 | 332 | r += struct.pack("B", self.net)
|
308 | 333 | r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])
|
309 | 334 | if self.net == self.NET_IPV4:
|
310 | 335 | r += socket.inet_aton(self.ip)
|
311 |
| - else: |
| 336 | + elif self.net == self.NET_IPV6: |
| 337 | + r += socket.inet_pton(socket.AF_INET6, self.ip) |
| 338 | + elif self.net == self.NET_TORV3: |
| 339 | + sfx = ".onion" |
| 340 | + assert self.ip.endswith(sfx) |
| 341 | + r += b32decode(self.ip[0:-len(sfx)], True)[0:32] |
| 342 | + elif self.net == self.NET_I2P: |
312 | 343 | sfx = ".b32.i2p"
|
313 | 344 | assert self.ip.endswith(sfx)
|
314 | 345 | r += b32decode(self.ip[0:-len(sfx)] + self.I2P_PAD, True)
|
| 346 | + elif self.net == self.NET_CJDNS: |
| 347 | + r += socket.inet_pton(socket.AF_INET6, self.ip) |
| 348 | + else: |
| 349 | + raise Exception(f"Address type not supported") |
315 | 350 | r += struct.pack(">H", self.port)
|
316 | 351 | return r
|
317 | 352 |
|
@@ -1852,3 +1887,19 @@ def serialize(self):
|
1852 | 1887 | def __repr__(self):
|
1853 | 1888 | return "msg_sendtxrcncl(version=%lu, salt=%lu)" %\
|
1854 | 1889 | (self.version, self.salt)
|
| 1890 | + |
| 1891 | +class TestFrameworkScript(unittest.TestCase): |
| 1892 | + def test_addrv2_encode_decode(self): |
| 1893 | + def check_addrv2(ip, net): |
| 1894 | + addr = CAddress() |
| 1895 | + addr.net, addr.ip = net, ip |
| 1896 | + ser = addr.serialize_v2() |
| 1897 | + actual = CAddress() |
| 1898 | + actual.deserialize_v2(BytesIO(ser)) |
| 1899 | + self.assertEqual(actual, addr) |
| 1900 | + |
| 1901 | + check_addrv2("1.65.195.98", CAddress.NET_IPV4) |
| 1902 | + check_addrv2("2001:41f0::62:6974:636f:696e", CAddress.NET_IPV6) |
| 1903 | + check_addrv2("2bqghnldu6mcug4pikzprwhtjjnsyederctvci6klcwzepnjd46ikjyd.onion", CAddress.NET_TORV3) |
| 1904 | + check_addrv2("255fhcp6ajvftnyo7bwz3an3t4a4brhopm3bamyh2iu5r3gnr2rq.b32.i2p", CAddress.NET_I2P) |
| 1905 | + check_addrv2("fc32:17ea:e415:c3bf:9808:149d:b5a2:c9aa", CAddress.NET_CJDNS) |
0 commit comments