77#define BITCOIN_ADDRMAN_H
88
99#include < clientversion.h>
10+ #include < config/bitcoin-config.h>
1011#include < netaddress.h>
1112#include < protocol.h>
1213#include < random.h>
@@ -176,6 +177,28 @@ friend class CAddrManTest;
176177 mutable RecursiveMutex cs;
177178
178179private:
180+ // ! Serialization versions.
181+ enum Format : uint8_t {
182+ V0_HISTORICAL = 0 , // !< historic format, before commit e6b343d88
183+ V1_DETERMINISTIC = 1 , // !< for pre-asmap files
184+ V2_ASMAP = 2 , // !< for files including asmap version
185+ V3_BIP155 = 3 , // !< same as V2_ASMAP plus addresses are in BIP155 format
186+ };
187+
188+ // ! The maximum format this software knows it can unserialize. Also, we always serialize
189+ // ! in this format.
190+ // ! The format (first byte in the serialized stream) can be higher than this and
191+ // ! still this software may be able to unserialize the file - if the second byte
192+ // ! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
193+ static constexpr Format FILE_FORMAT = Format::V3_BIP155;
194+
195+ // ! The initial value of a field that is incremented every time an incompatible format
196+ // ! change is made (such that old software versions would not be able to parse and
197+ // ! understand the new file format). This is 32 because we overtook the "key size"
198+ // ! field which was 32 historically.
199+ // ! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
200+ static constexpr uint8_t INCOMPATIBILITY_BASE = 32 ;
201+
179202 // ! last used nId
180203 int nIdCount GUARDED_BY (cs);
181204
@@ -265,14 +288,6 @@ friend class CAddrManTest;
265288 void SetServices_ (const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
266289
267290public:
268- // ! Serialization versions.
269- enum class Format : uint8_t {
270- V0_HISTORICAL = 0 , // !< historic format, before commit e6b343d88
271- V1_DETERMINISTIC = 1 , // !< for pre-asmap files
272- V2_ASMAP = 2 , // !< for files including asmap version
273- V3_BIP155 = 3 , // !< same as V2_ASMAP plus addresses are in BIP155 format
274- };
275-
276291 // Compressed IP->ASN mapping, loaded from a file when a node starts.
277292 // Should be always empty if no file was provided.
278293 // This mapping is then used for bucketing nodes in Addrman.
@@ -295,8 +310,17 @@ friend class CAddrManTest;
295310
296311 /* *
297312 * Serialized format.
298- * * version byte (@see `Format`)
299- * * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
313+ * * format version byte (@see `Format`)
314+ * * lowest compatible format version byte. This is used to help old software decide
315+ * whether to parse the file. For example:
316+ * * Bitcoin Core V knows how to parse up to format=3. If a new format=4 is introduced
317+ * in Bitcoin Core V+1 which is compatible with format=3 and it is known that V will
318+ * be able to parse it, then V+1 will write (format=4, lowest_compatible=3) in the
319+ * first two bytes of the file and so V will still try to parse it.
320+ * * Bitcoin Core V+2 introduces a new incompatible format=5. It will write
321+ * (format=5, lowest_compatible=5) and so any versions that do not know how to parse
322+ * format=5 will not try to read the file.
323+ * * nKey
300324 * * nNew
301325 * * nTried
302326 * * number of "new" buckets XOR 2**30
@@ -327,12 +351,17 @@ friend class CAddrManTest;
327351 {
328352 LOCK (cs);
329353
330- // Always serialize in the latest version (currently Format::V3_BIP155 ).
354+ // Always serialize in the latest version (FILE_FORMAT ).
331355
332356 OverrideStream<Stream> s (&s_, s_.GetType (), s_.GetVersion () | ADDRV2_FORMAT);
333357
334- s << static_cast <uint8_t >(Format::V3_BIP155);
335- s << ((unsigned char )32 );
358+ s << Using<CustomUintFormatter<1 >>(FILE_FORMAT);
359+
360+ // Increment `lowest_compatible` iff a newly introduced format is incompatible with
361+ // the previous one.
362+ static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
363+ s << static_cast <uint8_t >(INCOMPATIBILITY_BASE + lowest_compatible);
364+
336365 s << nKey;
337366 s << nNew;
338367 s << nTried;
@@ -392,15 +421,6 @@ friend class CAddrManTest;
392421 Format format;
393422 s_ >> Using<CustomUintFormatter<1 >>(format);
394423
395- static constexpr Format maximum_supported_format = Format::V3_BIP155;
396- if (format > maximum_supported_format) {
397- throw std::ios_base::failure (strprintf (
398- " Unsupported format of addrman database: %u. Maximum supported is %u. "
399- " Continuing operation without using the saved list of peers." ,
400- static_cast <uint8_t >(format),
401- static_cast <uint8_t >(maximum_supported_format)));
402- }
403-
404424 int stream_version = s_.GetVersion ();
405425 if (format >= Format::V3_BIP155) {
406426 // Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
@@ -410,9 +430,16 @@ friend class CAddrManTest;
410430
411431 OverrideStream<Stream> s (&s_, s_.GetType (), stream_version);
412432
413- unsigned char nKeySize;
414- s >> nKeySize;
415- if (nKeySize != 32 ) throw std::ios_base::failure (" Incorrect keysize in addrman deserialization" );
433+ uint8_t compat;
434+ s >> compat;
435+ const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
436+ if (lowest_compatible > FILE_FORMAT) {
437+ throw std::ios_base::failure (
438+ strprintf (" Unsupported format of addrman database: %u. It is compatible with "
439+ " formats >=%u, but the maximum supported by this version of %s is %u." ,
440+ format, lowest_compatible, PACKAGE_NAME, FILE_FORMAT));
441+ }
442+
416443 s >> nKey;
417444 s >> nNew;
418445 s >> nTried;
0 commit comments