aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/addrdb.cpp90
-rw-r--r--src/addrdb.h8
-rw-r--r--src/init.cpp14
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/fuzz/data_stream.cpp5
-rwxr-xr-xtest/functional/feature_addrman.py34
6 files changed, 88 insertions, 67 deletions
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 856f318961..1e73750ce3 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -18,8 +18,15 @@
#include <univalue.h>
#include <util/settings.h>
#include <util/system.h>
+#include <util/translation.h>
namespace {
+
+class DbNotFoundError : public std::exception
+{
+ using std::exception::exception;
+};
+
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
@@ -77,47 +84,40 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
}
template <typename Stream, typename Data>
-bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
+void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
{
- try {
- CHashVerifier<Stream> verifier(&stream);
- // de-serialize file header (network specific magic number) and ..
- unsigned char pchMsgTmp[4];
- verifier >> pchMsgTmp;
- // ... verify the network matches ours
- if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
- return error("%s: Invalid network magic number", __func__);
-
- // de-serialize data
- verifier >> data;
-
- // verify checksum
- if (fCheckSum) {
- uint256 hashTmp;
- stream >> hashTmp;
- if (hashTmp != verifier.GetHash()) {
- return error("%s: Checksum mismatch, data corrupted", __func__);
- }
- }
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ CHashVerifier<Stream> verifier(&stream);
+ // de-serialize file header (network specific magic number) and ..
+ unsigned char pchMsgTmp[4];
+ verifier >> pchMsgTmp;
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
+ throw std::runtime_error{"Invalid network magic number"};
}
- return true;
+ // de-serialize data
+ verifier >> data;
+
+ // verify checksum
+ if (fCheckSum) {
+ uint256 hashTmp;
+ stream >> hashTmp;
+ if (hashTmp != verifier.GetHash()) {
+ throw std::runtime_error{"Checksum mismatch, data corrupted"};
+ }
+ }
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data, int version)
+void DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
- LogPrintf("Missing or invalid file %s\n", path.string());
- return false;
+ throw DbNotFoundError{};
}
- return DeserializeDB(filein, data);
+ DeserializeDB(filein, data);
}
} // namespace
@@ -176,15 +176,32 @@ bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
-bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr)
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
{
- const auto pathAddr = args.GetDataDirNet() / "peers.dat";
- return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
+ DeserializeDB(ssPeers, addr, false);
}
-bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
{
- return DeserializeDB(ssPeers, addr, false);
+ auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+
+ int64_t nStart = GetTimeMillis();
+ const auto path_addr{args.GetDataDirNet() / "peers.dat"};
+ try {
+ DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
+ LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
+ } catch (const DbNotFoundError&) {
+ // Addrman can be in an inconsistent state after failure, reset it
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
+ DumpPeerAddresses(args, *addrman);
+ } catch (const std::exception& e) {
+ addrman = nullptr;
+ 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."),
+ e.what(), PACKAGE_BUGREPORT, path_addr);
+ }
+ return std::nullopt;
}
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
@@ -196,9 +213,10 @@ void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& a
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
+ try {
+ DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
- } else {
+ } catch (const std::exception&) {
anchors.clear();
}
diff --git a/src/addrdb.h b/src/addrdb.h
index c31c126ee3..33cc1f9204 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -10,17 +10,18 @@
#include <net_types.h> // For banmap_t
#include <univalue.h>
+#include <optional>
#include <vector>
class ArgsManager;
class CAddrMan;
class CAddress;
class CDataStream;
+struct bilingual_str;
bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
-bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr);
/** Only used by tests. */
-bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
/** Access to the banlist database (banlist.json) */
class CBanDB
@@ -46,6 +47,9 @@ public:
bool Read(banmap_t& banSet);
};
+/** Returns an error string on failure */
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman);
+
/**
* Dump the anchor IP address database (anchors.dat)
*
diff --git a/src/init.cpp b/src/init.cpp
index 2753745843..d4ba441b0c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1200,19 +1200,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Using /16 prefix for IP bucketing\n");
}
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
-
- // Load addresses from peers.dat
uiInterface.InitMessage(_("Loading P2P addresses…").translated);
- int64_t nStart = GetTimeMillis();
- if (ReadPeerAddresses(args, *node.addrman)) {
- LogPrintf("Loaded %i addresses from peers.dat %dms\n", node.addrman->size(), GetTimeMillis() - nStart);
- } else {
- // Addrman can be in an inconsistent state after failure, reset it
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
- LogPrintf("Recreating peers.dat\n");
- DumpPeerAddresses(args, *node.addrman);
+ if (const auto error{LoadAddrman(asmap, args, node.addrman)}) {
+ return InitError(*error);
}
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 835b18d42e..01a492a20b 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(ReadFromStream(addrman2, ssPeers2));
+ ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
}
@@ -1073,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(!ReadFromStream(addrman2, ssPeers2));
+ BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index 08e9c91ba3..323090e041 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.cpp
@@ -23,5 +23,8 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- ReadFromStream(addr_man, data_stream);
+ try {
+ ReadFromStream(addr_man, data_stream);
+ } catch (const std::exception&) {
+ }
}
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 8ccff340f0..ee421c89b5 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -10,6 +10,7 @@ import struct
from test_framework.messages import ser_uint256, hash256
from test_framework.p2p import MAGIC_BYTES
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import ErrorMatch
from test_framework.util import assert_equal
@@ -43,6 +44,12 @@ class AddrmanTest(BitcoinTestFramework):
def run_test(self):
peers_dat = os.path.join(self.nodes[0].datadir, self.chain, "peers.dat")
+ init_error = lambda reason: (
+ f"Error: Invalid or corrupt peers.dat \\({reason}\\). If you believe this "
+ f"is a bug, please report it to {self.config['environment']['PACKAGE_BUGREPORT']}. "
+ f'As a workaround, you can move the file \\("{peers_dat}"\\) out of the way \\(rename, '
+ "move, or delete\\) to have a new one created on the next start."
+ )
self.log.info("Check that mocked addrman is valid")
self.stop_node(0)
@@ -54,30 +61,29 @@ class AddrmanTest(BitcoinTestFramework):
self.log.info("Check that addrman from future cannot be read")
self.stop_node(0)
write_addrman(peers_dat, lowest_compatible=111)
- with self.nodes[0].assert_debug_log([
- f'ERROR: DeserializeDB: Deserialize or I/O error - Unsupported format of addrman database: 1. It is compatible with formats >=111, but the maximum supported by this version of {self.config["environment"]["PACKAGE_NAME"]} is 3.',
- "Recreating peers.dat",
- ]):
- self.start_node(0)
- assert_equal(self.nodes[0].getnodeaddresses(), [])
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg=init_error(
+ "Unsupported format of addrman database: 1. It is compatible with "
+ "formats >=111, but the maximum supported by this version of "
+ f"{self.config['environment']['PACKAGE_NAME']} is 3.: (.+)"
+ ),
+ match=ErrorMatch.FULL_REGEX,
+ )
self.log.info("Check that corrupt addrman cannot be read")
self.stop_node(0)
with open(peers_dat, "wb") as f:
f.write(serialize_addrman()[:-1])
- with self.nodes[0].assert_debug_log([
- "ERROR: DeserializeDB: Deserialize or I/O error - CAutoFile::read: end of file",
- "Recreating peers.dat",
- ]):
- self.start_node(0)
- assert_equal(self.nodes[0].getnodeaddresses(), [])
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg=init_error("CAutoFile::read: end of file.*"),
+ match=ErrorMatch.FULL_REGEX,
+ )
self.log.info("Check that missing addrman is recreated")
self.stop_node(0)
os.remove(peers_dat)
with self.nodes[0].assert_debug_log([
- f"Missing or invalid file {peers_dat}",
- "Recreating peers.dat",
+ f'Creating peers.dat because the file was not found ("{peers_dat}")',
]):
self.start_node(0)
assert_equal(self.nodes[0].getnodeaddresses(), [])