Skip to content

Commit e0d7357

Browse files
vasilddongcarl
andcommitted
net: CNetAddr: add support to (un)serialize as ADDRv2
Co-authored-by: Carl Dong <[email protected]>
1 parent fe42411 commit e0d7357

File tree

5 files changed

+389
-3
lines changed

5 files changed

+389
-3
lines changed

src/netaddress.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,65 @@
1414
#include <algorithm>
1515
#include <array>
1616
#include <cstdint>
17+
#include <ios>
1718
#include <iterator>
1819
#include <tuple>
1920

2021
constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE;
22+
constexpr size_t CNetAddr::MAX_ADDRV2_SIZE;
23+
24+
CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
25+
{
26+
switch (m_net) {
27+
case NET_IPV4:
28+
return BIP155Network::IPV4;
29+
case NET_IPV6:
30+
return BIP155Network::IPV6;
31+
case NET_ONION:
32+
return BIP155Network::TORV2;
33+
case NET_INTERNAL: // should have been handled before calling this function
34+
case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
35+
case NET_MAX: // m_net is never and should not be set to NET_MAX
36+
assert(false);
37+
} // no default case, so the compiler can warn about missing cases
38+
39+
assert(false);
40+
}
41+
42+
bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size)
43+
{
44+
switch (possible_bip155_net) {
45+
case BIP155Network::IPV4:
46+
if (address_size == ADDR_IPV4_SIZE) {
47+
m_net = NET_IPV4;
48+
return true;
49+
}
50+
throw std::ios_base::failure(
51+
strprintf("BIP155 IPv4 address with length %u (should be %u)", address_size,
52+
ADDR_IPV4_SIZE));
53+
case BIP155Network::IPV6:
54+
if (address_size == ADDR_IPV6_SIZE) {
55+
m_net = NET_IPV6;
56+
return true;
57+
}
58+
throw std::ios_base::failure(
59+
strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size,
60+
ADDR_IPV6_SIZE));
61+
case BIP155Network::TORV2:
62+
if (address_size == ADDR_TORV2_SIZE) {
63+
m_net = NET_ONION;
64+
return true;
65+
}
66+
throw std::ios_base::failure(
67+
strprintf("BIP155 TORv2 address with length %u (should be %u)", address_size,
68+
ADDR_TORV2_SIZE));
69+
}
70+
71+
// Don't throw on addresses with unknown network ids (maybe from the future).
72+
// Instead silently drop them and have the unserialization code consume
73+
// subsequent ones which may be known to us.
74+
return false;
75+
}
2176

2277
/**
2378
* Construct an unspecified IPv6 network address (::/128).

src/netaddress.h

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,24 @@
1313
#include <compat.h>
1414
#include <prevector.h>
1515
#include <serialize.h>
16+
#include <tinyformat.h>
17+
#include <util/strencodings.h>
18+
#include <util/string.h>
1619

1720
#include <array>
1821
#include <cstdint>
22+
#include <ios>
1923
#include <string>
2024
#include <vector>
2125

26+
/**
27+
* A flag that is ORed into the protocol version to designate that addresses
28+
* should be serialized in (unserialized from) v2 format (BIP155).
29+
* Make sure that this does not collide with any of the values in `version.h`
30+
* or with `SERIALIZE_TRANSACTION_NO_WITNESS`.
31+
*/
32+
static const int ADDRV2_FORMAT = 0x20000000;
33+
2234
/**
2335
* A network type.
2436
* @note An address may belong to more than one network, for example `10.0.0.1`
@@ -177,7 +189,11 @@ class CNetAddr
177189
template <typename Stream>
178190
void Serialize(Stream& s) const
179191
{
180-
SerializeV1Stream(s);
192+
if (s.GetVersion() & ADDRV2_FORMAT) {
193+
SerializeV2Stream(s);
194+
} else {
195+
SerializeV1Stream(s);
196+
}
181197
}
182198

183199
/**
@@ -186,17 +202,53 @@ class CNetAddr
186202
template <typename Stream>
187203
void Unserialize(Stream& s)
188204
{
189-
UnserializeV1Stream(s);
205+
if (s.GetVersion() & ADDRV2_FORMAT) {
206+
UnserializeV2Stream(s);
207+
} else {
208+
UnserializeV1Stream(s);
209+
}
190210
}
191211

192212
friend class CSubNet;
193213

194214
private:
215+
/**
216+
* BIP155 network ids recognized by this software.
217+
*/
218+
enum BIP155Network : uint8_t {
219+
IPV4 = 1,
220+
IPV6 = 2,
221+
TORV2 = 3,
222+
};
223+
195224
/**
196225
* Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
197226
*/
198227
static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
199228

229+
/**
230+
* Maximum size of an address as defined in BIP155 (in bytes).
231+
* This is only the size of the address, not the entire CNetAddr object
232+
* when serialized.
233+
*/
234+
static constexpr size_t MAX_ADDRV2_SIZE = 512;
235+
236+
/**
237+
* Get the BIP155 network id of this address.
238+
* Must not be called for IsInternal() objects.
239+
* @returns BIP155 network id
240+
*/
241+
BIP155Network GetBIP155Network() const;
242+
243+
/**
244+
* Set `m_net` from the provided BIP155 network id and size after validation.
245+
* @retval true the network was recognized, is valid and `m_net` was set
246+
* @retval false not recognised (from future?) and should be silently ignored
247+
* @throws std::ios_base::failure if the network is one of the BIP155 founding
248+
* networks recognized by this software (id 1..3) and has wrong address size.
249+
*/
250+
bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
251+
200252
/**
201253
* Serialize in pre-ADDRv2/BIP155 format to an array.
202254
* Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
@@ -250,6 +302,25 @@ class CNetAddr
250302
s << serialized;
251303
}
252304

305+
/**
306+
* Serialize as ADDRv2 / BIP155.
307+
*/
308+
template <typename Stream>
309+
void SerializeV2Stream(Stream& s) const
310+
{
311+
if (IsInternal()) {
312+
// Serialize NET_INTERNAL as embedded in IPv6. We need to
313+
// serialize such addresses from addrman.
314+
s << static_cast<uint8_t>(BIP155Network::IPV6);
315+
s << COMPACTSIZE(ADDR_IPV6_SIZE);
316+
SerializeV1Stream(s);
317+
return;
318+
}
319+
320+
s << static_cast<uint8_t>(GetBIP155Network());
321+
s << m_addr;
322+
}
323+
253324
/**
254325
* Unserialize from a pre-ADDRv2/BIP155 format from an array.
255326
*/
@@ -272,6 +343,65 @@ class CNetAddr
272343

273344
UnserializeV1Array(serialized);
274345
}
346+
347+
/**
348+
* Unserialize from a ADDRv2 / BIP155 format.
349+
*/
350+
template <typename Stream>
351+
void UnserializeV2Stream(Stream& s)
352+
{
353+
uint8_t bip155_net;
354+
s >> bip155_net;
355+
356+
size_t address_size;
357+
s >> COMPACTSIZE(address_size);
358+
359+
if (address_size > MAX_ADDRV2_SIZE) {
360+
throw std::ios_base::failure(strprintf(
361+
"Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
362+
}
363+
364+
scopeId = 0;
365+
366+
if (SetNetFromBIP155Network(bip155_net, address_size)) {
367+
m_addr.resize(address_size);
368+
s >> MakeSpan(m_addr);
369+
370+
if (m_net != NET_IPV6) {
371+
return;
372+
}
373+
374+
// Do some special checks on IPv6 addresses.
375+
376+
// Recognize NET_INTERNAL embedded in IPv6, such addresses are not
377+
// gossiped but could be coming from addrman, when unserializing from
378+
// disk.
379+
if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
380+
m_net = NET_INTERNAL;
381+
memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
382+
ADDR_INTERNAL_SIZE);
383+
m_addr.resize(ADDR_INTERNAL_SIZE);
384+
return;
385+
}
386+
387+
if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
388+
!HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
389+
return;
390+
}
391+
392+
// IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
393+
// encoding). Unserialize as !IsValid(), thus ignoring them.
394+
} else {
395+
// If we receive an unknown BIP155 network id (from the future?) then
396+
// ignore the address - unserialize as !IsValid().
397+
s.ignore(address_size);
398+
}
399+
400+
// Mimic a default-constructed CNetAddr object which is !IsValid() and thus
401+
// will not be gossiped, but continue reading next addresses from the stream.
402+
m_net = NET_IPV6;
403+
m_addr.assign(ADDR_IPV6_SIZE, 0x0);
404+
}
275405
};
276406

277407
class CSubNet

src/primitives/transaction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414

1515
#include <tuple>
1616

17+
/**
18+
* A flag that is ORed into the protocol version to designate that a transaction
19+
* should be (un)serialized without witness data.
20+
* Make sure that this does not collide with any of the values in `version.h`
21+
* or with `ADDRV2_FORMAT`.
22+
*/
1723
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
1824

1925
/** An outpoint - a combination of a transaction hash and an index n into its vout */

0 commit comments

Comments
 (0)