|
18 | 18 | Classes use __slots__ to ensure extraneous attributes aren't accidentally added
|
19 | 19 | by tests, compromising their intended effect.
|
20 | 20 | """
|
| 21 | +from base64 import b32decode, b32encode |
21 | 22 | from codecs import encode
|
22 | 23 | import copy
|
23 | 24 | import hashlib
|
@@ -213,22 +214,30 @@ class CAddress:
|
213 | 214 |
|
214 | 215 | # see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
|
215 | 216 | NET_IPV4 = 1
|
| 217 | + NET_I2P = 5 |
216 | 218 |
|
217 | 219 | ADDRV2_NET_NAME = {
|
218 |
| - NET_IPV4: "IPv4" |
| 220 | + NET_IPV4: "IPv4", |
| 221 | + NET_I2P: "I2P" |
219 | 222 | }
|
220 | 223 |
|
221 | 224 | ADDRV2_ADDRESS_LENGTH = {
|
222 |
| - NET_IPV4: 4 |
| 225 | + NET_IPV4: 4, |
| 226 | + NET_I2P: 32 |
223 | 227 | }
|
224 | 228 |
|
| 229 | + I2P_PAD = "====" |
| 230 | + |
225 | 231 | def __init__(self):
|
226 | 232 | self.time = 0
|
227 | 233 | self.nServices = 1
|
228 | 234 | self.net = self.NET_IPV4
|
229 | 235 | self.ip = "0.0.0.0"
|
230 | 236 | self.port = 0
|
231 | 237 |
|
| 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 | + |
232 | 241 | def deserialize(self, f, *, with_time=True):
|
233 | 242 | """Deserialize from addrv1 format (pre-BIP155)"""
|
234 | 243 | if with_time:
|
@@ -261,24 +270,33 @@ def deserialize_v2(self, f):
|
261 | 270 | self.nServices = deser_compact_size(f)
|
262 | 271 |
|
263 | 272 | 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) |
265 | 274 |
|
266 | 275 | address_length = deser_compact_size(f)
|
267 | 276 | assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]
|
268 | 277 |
|
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" |
270 | 283 |
|
271 | 284 | self.port = struct.unpack(">H", f.read(2))[0]
|
272 | 285 |
|
273 | 286 | def serialize_v2(self):
|
274 | 287 | """Serialize in addrv2 format (BIP155)"""
|
275 |
| - assert self.net == self.NET_IPV4 |
| 288 | + assert self.net in (self.NET_IPV4, self.NET_I2P) |
276 | 289 | r = b""
|
277 | 290 | r += struct.pack("<I", self.time)
|
278 | 291 | r += ser_compact_size(self.nServices)
|
279 | 292 | r += struct.pack("B", self.net)
|
280 | 293 | 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) |
282 | 300 | r += struct.pack(">H", self.port)
|
283 | 301 | return r
|
284 | 302 |
|
|
0 commit comments