|
13 | 13 | #include <netaddress.h>
|
14 | 14 | #include <primitives/transaction.h>
|
15 | 15 | #include <serialize.h>
|
| 16 | +#include <streams.h> |
16 | 17 | #include <uint256.h>
|
17 | 18 | #include <version.h>
|
18 | 19 |
|
@@ -358,43 +359,103 @@ class CAddress : public CService
|
358 | 359 | {
|
359 | 360 | static constexpr uint32_t TIME_INIT{100000000};
|
360 | 361 |
|
| 362 | + /** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with |
| 363 | + * the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been |
| 364 | + * disentangled from client versioning, and now instead: |
| 365 | + * - The low bits (masked by DISK_VERSION_IGNORE_MASK) store the fixed value DISK_VERSION_INIT, |
| 366 | + * (in case any code exists that treats it as a client version) but are ignored on |
| 367 | + * deserialization. |
| 368 | + * - The high bits (masked by ~DISK_VERSION_IGNORE_MASK) store actual serialization information. |
| 369 | + * Only 0 or DISK_VERSION_ADDRV2 (equal to the historical ADDRV2_FORMAT) are valid now, and |
| 370 | + * any other value triggers a deserialization failure. Other values can be added later if |
| 371 | + * needed. |
| 372 | + * |
| 373 | + * For disk deserialization, ADDRV2_FORMAT in the stream version signals that ADDRV2 |
| 374 | + * deserialization is permitted, but the actual format is determined by the high bits in the |
| 375 | + * stored version field. For network serialization, the stream version having ADDRV2_FORMAT or |
| 376 | + * not determines the actual format used (as it has no embedded version number). |
| 377 | + */ |
| 378 | + static constexpr uint32_t DISK_VERSION_INIT{220000}; |
| 379 | + static constexpr uint32_t DISK_VERSION_IGNORE_MASK{0b00000000'00000111'11111111'11111111}; |
| 380 | + /** The version number written in disk serialized addresses to indicate V2 serializations. |
| 381 | + * It must be exactly 1<<29, as that is the value that historical versions used for this |
| 382 | + * (they used their internal ADDRV2_FORMAT flag here). */ |
| 383 | + static constexpr uint32_t DISK_VERSION_ADDRV2{1 << 29}; |
| 384 | + static_assert((DISK_VERSION_INIT & ~DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_INIT must be covered by DISK_VERSION_IGNORE_MASK"); |
| 385 | + static_assert((DISK_VERSION_ADDRV2 & DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_ADDRV2 must not be covered by DISK_VERSION_IGNORE_MASK"); |
| 386 | + |
361 | 387 | public:
|
362 | 388 | CAddress() : CService{} {};
|
363 | 389 | CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
|
364 | 390 | CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nTime{nTimeIn}, nServices{nServicesIn} {};
|
365 | 391 |
|
366 | 392 | SERIALIZE_METHODS(CAddress, obj)
|
367 | 393 | {
|
368 |
| - SER_READ(obj, obj.nTime = TIME_INIT); |
369 |
| - int nVersion = s.GetVersion(); |
| 394 | + // CAddress has a distinct network serialization and a disk serialization, but it should never |
| 395 | + // be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's |
| 396 | + // ambiguous what that would mean. Make sure no code relying on that is introduced: |
| 397 | + assert(!(s.GetType() & SER_GETHASH)); |
| 398 | + bool use_v2; |
| 399 | + bool store_time; |
370 | 400 | if (s.GetType() & SER_DISK) {
|
371 |
| - READWRITE(nVersion); |
372 |
| - } |
373 |
| - if ((s.GetType() & SER_DISK) || |
374 |
| - (nVersion != INIT_PROTO_VERSION && !(s.GetType() & SER_GETHASH))) { |
| 401 | + // In the disk serialization format, the encoding (v1 or v2) is determined by a flag version |
| 402 | + // that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines |
| 403 | + // whether V2 is chosen/permitted at all. |
| 404 | + uint32_t stored_format_version = DISK_VERSION_INIT; |
| 405 | + if (s.GetVersion() & ADDRV2_FORMAT) stored_format_version |= DISK_VERSION_ADDRV2; |
| 406 | + READWRITE(stored_format_version); |
| 407 | + stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits |
| 408 | + if (stored_format_version == 0) { |
| 409 | + use_v2 = false; |
| 410 | + } else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) { |
| 411 | + // Only support v2 deserialization if ADDRV2_FORMAT is set. |
| 412 | + use_v2 = true; |
| 413 | + } else { |
| 414 | + throw std::ios_base::failure("Unsupported CAddress disk format version"); |
| 415 | + } |
| 416 | + store_time = true; |
| 417 | + } else { |
| 418 | + // In the network serialization format, the encoding (v1 or v2) is determined directly by |
| 419 | + // the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version |
| 420 | + // exists in the stream. |
| 421 | + assert(s.GetType() & SER_NETWORK); |
| 422 | + use_v2 = s.GetVersion() & ADDRV2_FORMAT; |
375 | 423 | // The only time we serialize a CAddress object without nTime is in
|
376 | 424 | // the initial VERSION messages which contain two CAddress records.
|
377 | 425 | // At that point, the serialization version is INIT_PROTO_VERSION.
|
378 | 426 | // After the version handshake, serialization version is >=
|
379 | 427 | // MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
|
380 | 428 | // nTime.
|
381 |
| - READWRITE(obj.nTime); |
| 429 | + store_time = s.GetVersion() != INIT_PROTO_VERSION; |
382 | 430 | }
|
383 |
| - if (nVersion & ADDRV2_FORMAT) { |
| 431 | + |
| 432 | + SER_READ(obj, obj.nTime = TIME_INIT); |
| 433 | + if (store_time) READWRITE(obj.nTime); |
| 434 | + // nServices is serialized as CompactSize in V2; as uint64_t in V1. |
| 435 | + if (use_v2) { |
384 | 436 | uint64_t services_tmp;
|
385 | 437 | SER_WRITE(obj, services_tmp = obj.nServices);
|
386 | 438 | READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
|
387 | 439 | SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
|
388 | 440 | } else {
|
389 | 441 | READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
|
390 | 442 | }
|
391 |
| - READWRITEAS(CService, obj); |
| 443 | + // Invoke V1/V2 serializer for CService parent object. |
| 444 | + OverrideStream<Stream> os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0); |
| 445 | + SerReadWriteMany(os, ser_action, ReadWriteAsHelper<CService>(obj)); |
392 | 446 | }
|
393 | 447 |
|
394 |
| - // disk and network only |
| 448 | + //! Always included in serialization, except in the network format on INIT_PROTO_VERSION. |
395 | 449 | uint32_t nTime{TIME_INIT};
|
396 |
| - |
| 450 | + //! Serialized as uint64_t in V1, and as CompactSize in V2. |
397 | 451 | ServiceFlags nServices{NODE_NONE};
|
| 452 | + |
| 453 | + friend bool operator==(const CAddress& a, const CAddress& b) |
| 454 | + { |
| 455 | + return a.nTime == b.nTime && |
| 456 | + a.nServices == b.nServices && |
| 457 | + static_cast<const CService&>(a) == static_cast<const CService&>(b); |
| 458 | + } |
398 | 459 | };
|
399 | 460 |
|
400 | 461 | /** getdata message type flags */
|
|
0 commit comments