Skip to content

Commit 74fcd02

Browse files
a93fec6 merge bitcoin#23380: Fix AddrMan::Add() return semantics and logging (Kittywhiskers Van Gogh) d1a4b14 merge bitcoin#23354: Introduce new V4 format addrman (Kittywhiskers Van Gogh) 7a97aab test: restore pre-bitcoin#23306 tests to validate port distinguishment (Kittywhiskers Van Gogh) 1a050d6 merge bitcoin#23306: Make AddrMan support multiple ports per IP (Kittywhiskers Van Gogh) d56702a merge bitcoin#23140: Make CAddrman::Select_ select buckets, not positions, first (Kittywhiskers Van Gogh) 19b0145 merge bitcoin#22839: improve addrman logging (Kittywhiskers Van Gogh) 3910c68 merge bitcoin#23053: Use public methods in addrman fuzz tests (Kittywhiskers Van Gogh) b6ec8ab merge bitcoin#22950: Pimpl AddrMan to abstract implementation details (Kittywhiskers Van Gogh) 236cf36 merge bitcoin#22734: Avoid crash on corrupt data, Force Check after deserialize (Kittywhiskers Van Gogh) 2420ac9 merge bitcoin#23041: Add addrman deserialization error tests (Kittywhiskers Van Gogh) 8aefa9b merge bitcoin#22762: Raise InitError when peers.dat is invalid or corrupted (Kittywhiskers Van Gogh) c9a645f merge bitcoin#22879: Fix format string in deserialize error (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Dependent on dashpay#6040. * Dash already introduced support for storage of address-port pairs (referred to as "port discrimination") and allowed the usage of non-default ports in P2P with [dash#2168](dashpay#2168). * Albeit this was only permitted for networks with `fAllowMultiplePorts` enabled (which at the time was `devnet` and as it stands currently, on every network except `mainnet`). * Keeping in line with the above policy (discussion on lifting such restrictions on `mainnet` is outside the scope of this PR), when backporting [bitcoin#23306](bitcoin#23306), changes have been made to retain the effects of `discriminate_ports`. * This involves appending placing a `!m_discriminate_ports` condition to behaviour that otherwise would be _removed_ entirely. * Additionally, in line with upstream backports, changes have been made that render port distinguishment _enabled_ as the new default in `addrman_tests` (the old default was to keep it _disabled_, to mirror `mainnet` and pre-change upstream behaviour). * To ensure distinguishment _disabled_ works as expected, affected pre-backport tests were reintroduced with the `_nondiscriminate` suffix. --- I would propose at some point to rename the flag to `ignore_port`/`suppress_port` (if not remove it altogether should the `mainnet` restriction be lifted) as discriminate (or distinguish) isn't immediately clear with if address entries will be discriminated/distinguished _using_ ports (i.e. considered) or ports will be discriminated _against_ (i.e. ignored). ## Breaking Changes It's unclear if these backports result in serialization issues for older versions, as Dash Core technically supported address-port pairs since 0.12 and suppressed it on `mainnet` by setting zero-ing out the port ([source](https://github.com/dashpay/dash/blob/19512988c6e6e8641245bd9c5fab21dd737561f0/src/addrman.cpp#L135-L138)), meaning even with port discrimination _disabled_, the serialization format should remain the same. Regardless, following upstream backports, a new version has been introduced (v4) that marks the AddrMan format incompatible with older versions of Dash Core. ## Checklist: _Go over all the following points, and put an `x` in all the boxes that apply._ - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK a93fec6 PastaPastaPasta: utACK dashpay@a93fec6 Tree-SHA512: 49b35af3e4eb660249ce9a65d5a539205d852e9c728da22dc88779f6b3b15c13cf91522896a313bfe2a91889fedf3b6b2cebdea12cc2bbe865ec3b85b6a5dfa8
2 parents 0254959 + a93fec6 commit 74fcd02

28 files changed

+1381
-979
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ BITCOIN_CORE_H = \
133133
addressindex.h \
134134
spentindex.h \
135135
addrman.h \
136+
addrman_impl.h \
136137
attributes.h \
137138
banman.h \
138139
base58.h \

src/Makefile.test.include

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ test_fuzz_fuzz_SOURCES = \
263263
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
264264
test/fuzz/crypto_poly1305.cpp \
265265
test/fuzz/cuckoocache.cpp \
266-
test/fuzz/data_stream.cpp \
267266
test/fuzz/decode_tx.cpp \
268267
test/fuzz/descriptor_parse.cpp \
269268
test/fuzz/deserialize.cpp \

src/addrdb.cpp

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@
1717
#include <univalue.h>
1818
#include <util/settings.h>
1919
#include <util/system.h>
20+
#include <util/translation.h>
2021

2122
#include <cstdint>
2223

2324
namespace {
25+
26+
class DbNotFoundError : public std::exception
27+
{
28+
using std::exception::exception;
29+
};
30+
2431
template <typename Stream, typename Data>
2532
bool SerializeDB(Stream& stream, const Data& data)
2633
{
@@ -78,47 +85,40 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
7885
}
7986

8087
template <typename Stream, typename Data>
81-
bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
88+
void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
8289
{
83-
try {
84-
CHashVerifier<Stream> verifier(&stream);
85-
// de-serialize file header (network specific magic number) and ..
86-
unsigned char pchMsgTmp[4];
87-
verifier >> pchMsgTmp;
88-
// ... verify the network matches ours
89-
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
90-
return error("%s: Invalid network magic number", __func__);
91-
92-
// de-serialize data
93-
verifier >> data;
94-
95-
// verify checksum
96-
if (fCheckSum) {
97-
uint256 hashTmp;
98-
stream >> hashTmp;
99-
if (hashTmp != verifier.GetHash()) {
100-
return error("%s: Checksum mismatch, data corrupted", __func__);
101-
}
102-
}
103-
}
104-
catch (const std::exception& e) {
105-
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
90+
CHashVerifier<Stream> verifier(&stream);
91+
// de-serialize file header (network specific magic number) and ..
92+
unsigned char pchMsgTmp[4];
93+
verifier >> pchMsgTmp;
94+
// ... verify the network matches ours
95+
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
96+
throw std::runtime_error{"Invalid network magic number"};
10697
}
10798

108-
return true;
99+
// de-serialize data
100+
verifier >> data;
101+
102+
// verify checksum
103+
if (fCheckSum) {
104+
uint256 hashTmp;
105+
stream >> hashTmp;
106+
if (hashTmp != verifier.GetHash()) {
107+
throw std::runtime_error{"Checksum mismatch, data corrupted"};
108+
}
109+
}
109110
}
110111

111112
template <typename Data>
112-
bool DeserializeFileDB(const fs::path& path, Data& data, int version)
113+
void DeserializeFileDB(const fs::path& path, Data& data, int version)
113114
{
114115
// open input file, and associate with CAutoFile
115116
FILE* file = fsbridge::fopen(path, "rb");
116117
CAutoFile filein(file, SER_DISK, version);
117118
if (filein.IsNull()) {
118-
LogPrintf("Missing or invalid file %s\n", path.string());
119-
return false;
119+
throw DbNotFoundError{};
120120
}
121-
return DeserializeDB(filein, data);
121+
DeserializeDB(filein, data);
122122
}
123123
} // namespace
124124

@@ -171,21 +171,38 @@ bool CBanDB::Read(banmap_t& banSet)
171171
return true;
172172
}
173173

174-
bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
174+
bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
175175
{
176176
const auto pathAddr = GetDataDir() / "peers.dat";
177177
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
178178
}
179179

180-
bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr)
180+
void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
181181
{
182-
const auto pathAddr = GetDataDir() / "peers.dat";
183-
return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
182+
DeserializeDB(ssPeers, addr, false);
184183
}
185184

186-
bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
185+
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman)
187186
{
188-
return DeserializeDB(ssPeers, addr, false);
187+
auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
188+
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
189+
190+
int64_t nStart = GetTimeMillis();
191+
const auto path_addr{GetDataDir() / "peers.dat"};
192+
try {
193+
DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
194+
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
195+
} catch (const DbNotFoundError&) {
196+
// Addrman can be in an inconsistent state after failure, reset it
197+
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
198+
LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
199+
DumpPeerAddresses(args, *addrman);
200+
} catch (const std::exception& e) {
201+
addrman = nullptr;
202+
return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),
203+
e.what(), PACKAGE_BUGREPORT, path_addr);
204+
}
205+
return std::nullopt;
189206
}
190207

191208
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
@@ -197,9 +214,10 @@ void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& a
197214
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
198215
{
199216
std::vector<CAddress> anchors;
200-
if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
217+
try {
218+
DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
201219
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
202-
} else {
220+
} catch (const std::exception&) {
203221
anchors.clear();
204222
}
205223

src/addrdb.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@
1010
#include <net_types.h> // For banmap_t
1111
#include <univalue.h>
1212

13+
#include <optional>
1314
#include <vector>
1415

1516
class ArgsManager;
16-
class CAddrMan;
17+
class AddrMan;
1718
class CAddress;
1819
class CDataStream;
20+
struct bilingual_str;
1921

20-
bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
21-
bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr);
22+
bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr);
2223
/** Only used by tests. */
23-
bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
24+
void ReadFromStream(AddrMan& addr, CDataStream& ssPeers);
2425

2526
/** Access to the banlist database (banlist.json) */
2627
class CBanDB
@@ -46,6 +47,9 @@ class CBanDB
4647
bool Read(banmap_t& banSet);
4748
};
4849

50+
/** Returns an error string on failure */
51+
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman);
52+
4953
/**
5054
* Dump the anchor IP address database (anchors.dat)
5155
*

0 commit comments

Comments
 (0)