Skip to content

Commit 0bd4929

Browse files
committed
Merge #20284: addrman: ensure old versions don't parse peers.dat
38ada89 addrman: ensure old versions don't parse peers.dat (Vasil Dimov) Pull request description: Even though the format of `peers.dat` was changed in a backwards incompatible way, it is not guaranteed that old versions will fail to parse it. There is a chance that old versions parse its contents as garbage and use it. Old versions expect the "key size" field to be 32 and fail the parsing if it is not. Thus, we put something other than 32 in it. This will make versions between 0.11.0 and 0.20.1 deterministically fail on the new format. Versions prior to bitcoin/bitcoin#5941 will still parse it as garbage. Also, introduce a way to increment the `peers.dat` format in a way that does not necessary make older versions refuse to read it. ACKs for top commit: jnewbery: ACK 38ada89 laanwj: Code review ACK 38ada89 MarcoFalke: re-ACK 38ada89 🥐 Tree-SHA512: 550bd660c5019dba0f9c334aca8a11c4a0463cfddf11efe7a4a5585ffb05549c82b95066fba5d073ae37893e0eccc158a7ffea9b33ea031d9be4a39e44f6face
2 parents 9bd1316 + 38ada89 commit 0bd4929

File tree

2 files changed

+55
-27
lines changed

2 files changed

+55
-27
lines changed

doc/release-notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ format of this file has been changed in a backwards-incompatible way in order to
6565
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
6666
the file is modified by 0.21.0 or newer then older versions will not be able to
6767
read it. Those old versions, in the event of a downgrade, will log an error
68-
message that deserialization has failed and will continue normal operation
69-
as if the file was missing, creating a new empty one. (#19954)
68+
message "Incorrect keysize in addrman deserialization" and will continue normal
69+
operation as if the file was missing, creating a new empty one. (#19954)
7070

7171
Notable changes
7272
===============

src/addrman.h

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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

178179
private:
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

267290
public:
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,18 @@ 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 version N knows how to parse up to format=3. If a new format=4 is
317+
* introduced in version N+1 that is compatible with format=3 and it is known that
318+
* version N will be able to parse it, then version N+1 will write
319+
* (format=4, lowest_compatible=3) in the first two bytes of the file, and so
320+
* version N will still try to parse it.
321+
* * Bitcoin Core version N+2 introduces a new incompatible format=5. It will write
322+
* (format=5, lowest_compatible=5) and so any versions that do not know how to parse
323+
* format=5 will not try to read the file.
324+
* * nKey
300325
* * nNew
301326
* * nTried
302327
* * number of "new" buckets XOR 2**30
@@ -327,12 +352,17 @@ friend class CAddrManTest;
327352
{
328353
LOCK(cs);
329354

330-
// Always serialize in the latest version (currently Format::V3_BIP155).
355+
// Always serialize in the latest version (FILE_FORMAT).
331356

332357
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
333358

334-
s << static_cast<uint8_t>(Format::V3_BIP155);
335-
s << ((unsigned char)32);
359+
s << static_cast<uint8_t>(FILE_FORMAT);
360+
361+
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
362+
// the previous one.
363+
static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
364+
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
365+
336366
s << nKey;
337367
s << nNew;
338368
s << nTried;
@@ -392,15 +422,6 @@ friend class CAddrManTest;
392422
Format format;
393423
s_ >> Using<CustomUintFormatter<1>>(format);
394424

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-
404425
int stream_version = s_.GetVersion();
405426
if (format >= Format::V3_BIP155) {
406427
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
@@ -410,9 +431,16 @@ friend class CAddrManTest;
410431

411432
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
412433

413-
unsigned char nKeySize;
414-
s >> nKeySize;
415-
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
434+
uint8_t compat;
435+
s >> compat;
436+
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
437+
if (lowest_compatible > FILE_FORMAT) {
438+
throw std::ios_base::failure(strprintf(
439+
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
440+
"but the maximum supported by this version of %s is %u.",
441+
format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
442+
}
443+
416444
s >> nKey;
417445
s >> nNew;
418446
s >> nTried;

0 commit comments

Comments
 (0)