Bitcoin ABC 0.32.5
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 <netbase.h>
16#include <random.h>
17#include <streams.h>
18#include <tinyformat.h>
19#include <univalue.h>
20#include <util/fs.h>
21#include <util/fs_helpers.h>
22#include <util/settings.h>
23#include <util/translation.h>
24
25#include <cstdint>
26
27static const char *BANMAN_JSON_VERSION_KEY{"version"};
28
30 : nVersion(json[BANMAN_JSON_VERSION_KEY].getInt<int>()),
31 nCreateTime(json["ban_created"].getInt<int64_t>()),
32 nBanUntil(json["banned_until"].getInt<int64_t>()) {}
33
37 json.pushKV("ban_created", nCreateTime);
38 json.pushKV("banned_until", nBanUntil);
39 return json;
40}
41
42namespace {
43
44static const char *BANMAN_JSON_ADDR_KEY = "address";
45
52UniValue BanMapToJson(const banmap_t &bans) {
53 UniValue bans_json(UniValue::VARR);
54 for (const auto &it : bans) {
55 const auto &address = it.first;
56 const auto &ban_entry = it.second;
57 UniValue j = ban_entry.ToJson();
58 j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
59 bans_json.push_back(j);
60 }
61 return bans_json;
62}
63
72void BanMapFromJson(const UniValue &bans_json, banmap_t &bans) {
73 for (const auto &ban_entry_json : bans_json.getValues()) {
74 const int version{
75 ban_entry_json[BANMAN_JSON_VERSION_KEY].getInt<int>()};
76 if (version != CBanEntry::CURRENT_VERSION) {
78 "Dropping entry with unknown version (%s) from ban list\n",
79 version);
80 continue;
81 }
82 CSubNet subnet;
83 const auto &subnet_str = ban_entry_json[BANMAN_JSON_ADDR_KEY].get_str();
84 if (!LookupSubNet(subnet_str, subnet)) {
85 LogPrintf("Dropping entry with unparseable address or subnet (%s) "
86 "from ban list\n",
87 subnet_str);
88 continue;
89 }
90 bans.insert_or_assign(subnet, CBanEntry{ban_entry_json});
91 }
92}
93
94class DbNotFoundError : public std::exception {
95 using std::exception::exception;
96};
97
98template <typename Stream, typename Data>
99bool SerializeDB(const CChainParams &chainParams, Stream &stream,
100 const Data &data) {
101 // Write and commit header, data
102 try {
103 HashedSourceWriter hashwriter{stream};
104 hashwriter << chainParams.DiskMagic() << data;
105 stream << hashwriter.GetHash();
106 } catch (const std::exception &e) {
107 LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
108 return false;
109 }
110
111 return true;
112}
113
114template <typename Data>
115bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix,
116 const fs::path &path, const Data &data, int version) {
117 // Generate random temporary filename
118 const uint16_t randv{FastRandomContext().rand<uint16_t>()};
119 std::string tmpfn = strprintf("%s.%04x", prefix, randv);
120
121 // open temp output file, and associate with CAutoFile
122 fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
123 FILE *file = fsbridge::fopen(pathTmp, "wb");
124 CAutoFile fileout(file, SER_DISK, version);
125 if (fileout.IsNull()) {
126 fileout.fclose();
127 remove(pathTmp);
128 LogError("%s: Failed to open file %s\n", __func__,
129 fs::PathToString(pathTmp));
130 return false;
131 }
132
133 // Serialize
134 if (!SerializeDB(chainParams, fileout, data)) {
135 fileout.fclose();
136 remove(pathTmp);
137 return false;
138 }
139 if (!FileCommit(fileout.Get())) {
140 fileout.fclose();
141 remove(pathTmp);
142 LogError("%s: Failed to flush file %s\n", __func__,
143 fs::PathToString(pathTmp));
144 return false;
145 }
146 fileout.fclose();
147
148 // replace existing file, if any, with new file
149 if (!RenameOver(pathTmp, path)) {
150 remove(pathTmp);
151 LogError("%s: Rename-into-place failed\n", __func__);
152 return false;
153 }
154
155 return true;
156}
157
158template <typename Stream, typename Data>
159void DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data,
160 bool fCheckSum = true) {
161 CHashVerifier<Stream> verifier(&stream);
162 // de-serialize file header (network specific magic number) and ..
163 uint8_t pchMsgTmp[4];
164 verifier >> pchMsgTmp;
165 // ... verify the network matches ours
166 if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()),
167 sizeof(pchMsgTmp))) {
168 throw std::runtime_error{"Invalid network magic number"};
169 }
170
171 // de-serialize data
172 verifier >> data;
173
174 // verify checksum
175 if (fCheckSum) {
176 uint256 hashTmp;
177 stream >> hashTmp;
178 if (hashTmp != verifier.GetHash()) {
179 throw std::runtime_error{"Checksum mismatch, data corrupted"};
180 }
181 }
182}
183
184template <typename Data>
185void DeserializeFileDB(const CChainParams &chainParams, const fs::path &path,
186 Data &data, int version) {
187 // open input file, and associate with CAutoFile
188 FILE *file = fsbridge::fopen(path, "rb");
189 CAutoFile filein(file, SER_DISK, version);
190 if (filein.IsNull()) {
191 throw DbNotFoundError{};
192 }
193
194 DeserializeDB(chainParams, filein, data);
195}
196
197} // namespace
198
199CBanDB::CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
200 : m_banlist_dat(ban_list_path + ".dat"),
201 m_banlist_json(ban_list_path + ".json"), chainParams(_chainParams) {}
202
203bool CBanDB::Write(const banmap_t &banSet) {
204 std::vector<std::string> errors;
205 if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}},
206 errors)) {
207 return true;
208 }
209
210 for (const auto &err : errors) {
211 LogError("%s\n", err);
212 }
213 return false;
214}
215
216bool CBanDB::Read(banmap_t &banSet, bool &dirty) {
217 // If the JSON banlist does not exist, then try to read the non-upgraded
218 // banlist.dat.
219 // TODO: stop supporting banlist.dat after v0.34.0. See:
220 // https://github.com/bitcoin/bitcoin/pull/22570
222 // If this succeeds then we need to flush to disk in order to create the
223 // JSON banlist.
224 dirty = true;
225 try {
226 DeserializeFileDB(chainParams, m_banlist_dat, banSet,
228 } catch (const std::exception &) {
229 LogPrintf("Missing or invalid file %s\n",
231 return false;
232 }
233 return true;
234 }
235
236 dirty = false;
237
238 std::map<std::string, util::SettingsValue> settings;
239 std::vector<std::string> errors;
240
241 if (!util::ReadSettings(m_banlist_json, settings, errors)) {
242 for (const auto &err : errors) {
243 LogPrintf("Cannot load banlist %s: %s\n",
245 }
246 return false;
247 }
248
249 try {
250 BanMapFromJson(settings[JSON_KEY], banSet);
251 } catch (const std::runtime_error &e) {
252 LogPrintf("Cannot parse banlist %s: %s\n",
254 return false;
255 }
256
257 return true;
258}
259
260bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args,
261 const AddrMan &addr) {
262 const auto pathAddr = args.GetDataDirNet() / "peers.dat";
263 return SerializeFileDB(chainParams, "peers", pathAddr, addr,
265}
266
267void ReadFromStream(const CChainParams &chainParams, AddrMan &addr,
268 CDataStream &ssPeers) {
269 DeserializeDB(chainParams, ssPeers, addr, false);
270}
271
273LoadAddrman(const CChainParams &chainparams, const std::vector<bool> &asmap,
274 const ArgsManager &args) {
275 auto check_addrman = std::clamp<int32_t>(
276 args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0,
277 1000000);
278 auto addrman{
279 std::make_unique<AddrMan>(asmap, /*deterministic=*/false,
280 /*consistency_check_ratio=*/check_addrman)};
281
282 int64_t nStart = GetTimeMillis();
283 const auto path_addr{args.GetDataDirNet() / "peers.dat"};
284 try {
285 DeserializeFileDB(chainparams, path_addr, *addrman, CLIENT_VERSION);
286 LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(),
287 GetTimeMillis() - nStart);
288 } catch (const DbNotFoundError &) {
289 // Addrman can be in an inconsistent state after failure, reset it
290 addrman = std::make_unique<AddrMan>(
291 asmap, /*deterministic=*/false,
292 /*consistency_check_ratio=*/check_addrman);
293 LogPrintf("Creating peers.dat because the file was not found (%s)\n",
294 fs::quoted(fs::PathToString(path_addr)));
295 DumpPeerAddresses(chainparams, args, *addrman);
296 } catch (const InvalidAddrManVersionError &) {
297 if (!RenameOver(path_addr, fs::path(path_addr) + ".bak")) {
298 return util::Error{
299 strprintf(_("Failed to rename invalid peers.dat file. "
300 "Please move or delete it and try again."))};
301 }
302 // Addrman can be in an inconsistent state after failure, reset it
303 addrman = std::make_unique<AddrMan>(
304 asmap, /*deterministic=*/false,
305 /*consistency_check_ratio=*/check_addrman);
306 LogPrintf("Creating new peers.dat because the file version was not "
307 "compatible (%s). Original backed up to peers.dat.bak\n",
308 fs::quoted(fs::PathToString(path_addr)));
309 DumpPeerAddresses(chainparams, args, *addrman);
310 } catch (const std::exception &e) {
311 return util::Error{strprintf(
312 _("Invalid or corrupt peers.dat (%s). If you believe this is a "
313 "bug, please report it to %s. As a workaround, you can move the "
314 "file (%s) out of the way (rename, move, or delete) to have a "
315 "new one created on the next start."),
316 e.what(), PACKAGE_BUGREPORT,
317 fs::quoted(fs::PathToString(path_addr)))};
318 }
319
320 // std::move should be unneccessary but is temporarily needed to work
321 // around clang bug
322 // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1564350880)
323 return {std::move(addrman)};
324}
325
326void DumpAnchors(const CChainParams &chainParams,
327 const fs::path &anchors_db_path,
328 const std::vector<CAddress> &anchors) {
330 "Flush %d outbound block-relay-only peer addresses to anchors.dat",
331 anchors.size()));
332 SerializeFileDB(chainParams, "anchors", anchors_db_path, anchors,
334}
335
336std::vector<CAddress> ReadAnchors(const CChainParams &chainParams,
337 const fs::path &anchors_db_path) {
338 std::vector<CAddress> anchors;
339 try {
340 DeserializeFileDB(chainParams, anchors_db_path, anchors,
342 LogPrintf("Loaded %i addresses from %s\n", anchors.size(),
343 fs::quoted(fs::PathToString(anchors_db_path.filename())));
344 } catch (const std::exception &) {
345 anchors.clear();
346 }
347
348 fs::remove(anchors_db_path);
349 return anchors;
350}
std::vector< CAddress > ReadAnchors(const CChainParams &chainParams, const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:336
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:273
void ReadFromStream(const CChainParams &chainParams, AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:267
bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:260
static const char * BANMAN_JSON_ADDR_KEY
Definition: addrdb.cpp:44
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:326
static const char * BANMAN_JSON_VERSION_KEY
Definition: addrdb.cpp:27
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:203
bool Read(banmap_t &banSet, bool &dirty)
Read the banlist from disk.
Definition: addrdb.cpp:216
const fs::path m_banlist_dat
Definition: addrdb.h:81
CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
Definition: addrdb.cpp:199
static constexpr const char * JSON_KEY
JSON key under which the data is stored in the json database.
Definition: addrdb.h:79
const fs::path m_banlist_json
Definition: addrdb.h:82
const CChainParams & chainParams
Definition: addrdb.h:83
Definition: addrdb.h:31
static const int CURRENT_VERSION
Definition: addrdb.h:33
int64_t nCreateTime
Definition: addrdb.h:35
UniValue ToJson() const
Generate a JSON representation of this ban entry.
Definition: addrdb.cpp:34
CBanEntry()
Definition: addrdb.h:38
int nVersion
Definition: addrdb.h:34
int64_t nBanUntil
Definition: addrdb.h:36
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
Fast randomness source.
Definition: random.h:411
Writes data to an underlying source stream, while hashing the written data.
Definition: hash.h:228
I rand() noexcept
Generate a random integer in its entire (non-negative) range.
Definition: random.h:299
void push_back(UniValue val)
Definition: univalue.cpp:96
@ VOBJ
Definition: univalue.h:31
@ VARR
Definition: univalue.h:32
const std::vector< UniValue > & getValues() const
Int getInt() const
Definition: univalue.h:157
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:115
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
char const * json() noexcept
Template to generate JSON data.
static auto quoted(const std::string &s)
Definition: fs.h:112
static bool exists(const path &p)
Definition: fs.h:107
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
bool ReadSettings(const fs::path &path, std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Read settings file.
Definition: settings.cpp:67
bool WriteSettings(const fs::path &path, const std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Write settings file.
Definition: settings.cpp:122
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
bool LookupSubNet(const std::string &strSubnet, CSubNet &ret, DNSLookupFn dns_lookup_function)
Parse and resolve a specified subnet string into the appropriate internal representation.
Definition: netbase.cpp:796
const char * prefix
Definition: rest.cpp:813
@ SER_DISK
Definition: serialize.h:156
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