Bitcoin ABC 0.32.4
P2P Digital Currency
addrdb.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2016 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <addrdb.h>
7
8#include <addrman.h>
9#include <chainparams.h>
10#include <clientversion.h>
11#include <common/args.h>
12#include <hash.h>
13#include <logging.h>
14#include <logging/timer.h>
15#include <random.h>
16#include <streams.h>
17#include <tinyformat.h>
18#include <util/fs.h>
19#include <util/fs_helpers.h>
20#include <util/translation.h>
21
22#include <cstdint>
23
24namespace {
25
26class DbNotFoundError : public std::exception {
27 using std::exception::exception;
28};
29
30template <typename Stream, typename Data>
31bool SerializeDB(const CChainParams &chainParams, Stream &stream,
32 const Data &data) {
33 // Write and commit header, data
34 try {
35 HashedSourceWriter hashwriter{stream};
36 hashwriter << chainParams.DiskMagic() << data;
37 stream << hashwriter.GetHash();
38 } catch (const std::exception &e) {
39 LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
40 return false;
41 }
42
43 return true;
44}
45
46template <typename Data>
47bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix,
48 const fs::path &path, const Data &data, int version) {
49 // Generate random temporary filename
50 const uint16_t randv{GetRand<uint16_t>()};
51 std::string tmpfn = strprintf("%s.%04x", prefix, randv);
52
53 // open temp output file, and associate with CAutoFile
54 fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
55 FILE *file = fsbridge::fopen(pathTmp, "wb");
56 CAutoFile fileout(file, SER_DISK, version);
57 if (fileout.IsNull()) {
58 fileout.fclose();
59 remove(pathTmp);
60 LogError("%s: Failed to open file %s\n", __func__,
61 fs::PathToString(pathTmp));
62 return false;
63 }
64
65 // Serialize
66 if (!SerializeDB(chainParams, fileout, data)) {
67 fileout.fclose();
68 remove(pathTmp);
69 return false;
70 }
71 if (!FileCommit(fileout.Get())) {
72 fileout.fclose();
73 remove(pathTmp);
74 LogError("%s: Failed to flush file %s\n", __func__,
75 fs::PathToString(pathTmp));
76 return false;
77 }
78 fileout.fclose();
79
80 // replace existing file, if any, with new file
81 if (!RenameOver(pathTmp, path)) {
82 remove(pathTmp);
83 LogError("%s: Rename-into-place failed\n", __func__);
84 return false;
85 }
86
87 return true;
88}
89
90template <typename Stream, typename Data>
91void DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data,
92 bool fCheckSum = true) {
93 CHashVerifier<Stream> verifier(&stream);
94 // de-serialize file header (network specific magic number) and ..
95 uint8_t pchMsgTmp[4];
96 verifier >> pchMsgTmp;
97 // ... verify the network matches ours
98 if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()),
99 sizeof(pchMsgTmp))) {
100 throw std::runtime_error{"Invalid network magic number"};
101 }
102
103 // de-serialize data
104 verifier >> data;
105
106 // verify checksum
107 if (fCheckSum) {
108 uint256 hashTmp;
109 stream >> hashTmp;
110 if (hashTmp != verifier.GetHash()) {
111 throw std::runtime_error{"Checksum mismatch, data corrupted"};
112 }
113 }
114}
115
116template <typename Data>
117void DeserializeFileDB(const CChainParams &chainParams, const fs::path &path,
118 Data &data, int version) {
119 // open input file, and associate with CAutoFile
120 FILE *file = fsbridge::fopen(path, "rb");
121 CAutoFile filein(file, SER_DISK, version);
122 if (filein.IsNull()) {
123 throw DbNotFoundError{};
124 }
125
126 DeserializeDB(chainParams, filein, data);
127}
128
129} // namespace
130
131CBanDB::CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
132 : m_ban_list_path(std::move(ban_list_path)), chainParams(_chainParams) {}
133
134bool CBanDB::Write(const banmap_t &banSet) {
135 return SerializeFileDB(chainParams, "banlist", m_ban_list_path, banSet,
137}
138
139bool CBanDB::Read(banmap_t &banSet) {
140 // TODO: this needs to be reworked after banlist.dat is deprecated (in
141 // favor of banlist.json). See:
142 // - https://github.com/bitcoin/bitcoin/pull/20966
143 // - https://github.com/bitcoin/bitcoin/pull/22570
144 try {
145 DeserializeFileDB(chainParams, m_ban_list_path, banSet, CLIENT_VERSION);
146 } catch (const std::exception &) {
147 LogPrintf("Missing or invalid file %s\n",
149 return false;
150 }
151
152 return true;
153}
154
155bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args,
156 const AddrMan &addr) {
157 const auto pathAddr = args.GetDataDirNet() / "peers.dat";
158 return SerializeFileDB(chainParams, "peers", pathAddr, addr,
160}
161
162void ReadFromStream(const CChainParams &chainParams, AddrMan &addr,
163 CDataStream &ssPeers) {
164 DeserializeDB(chainParams, ssPeers, addr, false);
165}
166
168LoadAddrman(const CChainParams &chainparams, const std::vector<bool> &asmap,
169 const ArgsManager &args) {
170 auto check_addrman = std::clamp<int32_t>(
171 args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0,
172 1000000);
173 auto addrman{
174 std::make_unique<AddrMan>(asmap, /*deterministic=*/false,
175 /*consistency_check_ratio=*/check_addrman)};
176
177 int64_t nStart = GetTimeMillis();
178 const auto path_addr{args.GetDataDirNet() / "peers.dat"};
179 try {
180 DeserializeFileDB(chainparams, path_addr, *addrman, CLIENT_VERSION);
181 LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(),
182 GetTimeMillis() - nStart);
183 } catch (const DbNotFoundError &) {
184 // Addrman can be in an inconsistent state after failure, reset it
185 addrman = std::make_unique<AddrMan>(
186 asmap, /*deterministic=*/false,
187 /*consistency_check_ratio=*/check_addrman);
188 LogPrintf("Creating peers.dat because the file was not found (%s)\n",
189 fs::quoted(fs::PathToString(path_addr)));
190 DumpPeerAddresses(chainparams, args, *addrman);
191 } catch (const InvalidAddrManVersionError &) {
192 if (!RenameOver(path_addr, fs::path(path_addr) + ".bak")) {
193 return util::Error{
194 strprintf(_("Failed to rename invalid peers.dat file. "
195 "Please move or delete it and try again."))};
196 }
197 // Addrman can be in an inconsistent state after failure, reset it
198 addrman = std::make_unique<AddrMan>(
199 asmap, /*deterministic=*/false,
200 /*consistency_check_ratio=*/check_addrman);
201 LogPrintf("Creating new peers.dat because the file version was not "
202 "compatible (%s). Original backed up to peers.dat.bak\n",
203 fs::quoted(fs::PathToString(path_addr)));
204 DumpPeerAddresses(chainparams, args, *addrman);
205 } catch (const std::exception &e) {
206 return util::Error{strprintf(
207 _("Invalid or corrupt peers.dat (%s). If you believe this is a "
208 "bug, please report it to %s. As a workaround, you can move the "
209 "file (%s) out of the way (rename, move, or delete) to have a "
210 "new one created on the next start."),
211 e.what(), PACKAGE_BUGREPORT,
212 fs::quoted(fs::PathToString(path_addr)))};
213 }
214
215 // std::move should be unneccessary but is temporarily needed to work
216 // around clang bug
217 // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1564350880)
218 return {std::move(addrman)};
219}
220
221void DumpAnchors(const CChainParams &chainParams,
222 const fs::path &anchors_db_path,
223 const std::vector<CAddress> &anchors) {
225 "Flush %d outbound block-relay-only peer addresses to anchors.dat",
226 anchors.size()));
227 SerializeFileDB(chainParams, "anchors", anchors_db_path, anchors,
229}
230
231std::vector<CAddress> ReadAnchors(const CChainParams &chainParams,
232 const fs::path &anchors_db_path) {
233 std::vector<CAddress> anchors;
234 try {
235 DeserializeFileDB(chainParams, anchors_db_path, anchors,
237 LogPrintf("Loaded %i addresses from %s\n", anchors.size(),
238 fs::quoted(fs::PathToString(anchors_db_path.filename())));
239 } catch (const std::exception &) {
240 anchors.clear();
241 }
242
243 fs::remove(anchors_db_path);
244 return anchors;
245}
std::vector< CAddress > ReadAnchors(const CChainParams &chainParams, const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:231
util::Result< std::unique_ptr< AddrMan > > LoadAddrman(const CChainParams &chainparams, const std::vector< bool > &asmap, const ArgsManager &args)
Returns an error string on failure.
Definition: addrdb.cpp:168
void ReadFromStream(const CChainParams &chainParams, AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:162
bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:155
void DumpAnchors(const CChainParams &chainParams, const fs::path &anchors_db_path, const std::vector< CAddress > &anchors)
Dump the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:221
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS
Default for -checkaddrman.
Definition: addrman.h:28
ArgsManager gArgs
Definition: args.cpp:40
Stochastic address manager.
Definition: addrman.h:68
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:495
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:134
const fs::path m_ban_list_path
Definition: addrdb.h:60
CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
Definition: addrdb.cpp:131
bool Read(banmap_t &banSet)
Definition: addrdb.cpp:139
const CChainParams & chainParams
Definition: addrdb.h:61
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:86
const CMessageHeader::MessageMagic & DiskMagic() const
Definition: chainparams.h:99
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:169
Writes data to an underlying source stream, while hashing the written data.
Definition: hash.h:201
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
path filename() const
Definition: fs.h:87
256-bit opaque blob.
Definition: uint256.h:129
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
bool RenameOver(fs::path src, fs::path dest)
Definition: fs_helpers.cpp:273
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:126
#define LogError(...)
Definition: logging.h:419
#define LogPrintf(...)
Definition: logging.h:424
static auto quoted(const std::string &s)
Definition: fs.h:112
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:147
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:30
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:259
std::map< CSubNet, CBanEntry > banmap_t
Definition: net_types.h:13
static constexpr int ADDRV2_FORMAT
A flag that is ORed into the protocol version to designate that addresses should be serialized in (un...
Definition: netaddress.h:33
const char * prefix
Definition: rest.cpp:812
@ SER_DISK
Definition: serialize.h:155
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: time.cpp:101
#define LOG_TIME_SECONDS(end_msg)
Definition: timer.h:103
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68