Bitcoin ABC  0.26.3
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 <hash.h>
12 #include <logging/timer.h>
13 #include <random.h>
14 #include <streams.h>
15 #include <tinyformat.h>
16 #include <util/system.h>
17 #include <util/translation.h>
18 
19 #include <cstdint>
20 
21 namespace {
22 
23 class DbNotFoundError : public std::exception {
24  using std::exception::exception;
25 };
26 
27 template <typename Stream, typename Data>
28 bool SerializeDB(const CChainParams &chainParams, Stream &stream,
29  const Data &data) {
30  // Write and commit header, data
31  try {
33  stream << chainParams.DiskMagic() << data;
34  hasher << chainParams.DiskMagic() << data;
35  stream << hasher.GetHash();
36  } catch (const std::exception &e) {
37  return error("%s: Serialize or I/O error - %s", __func__, e.what());
38  }
39 
40  return true;
41 }
42 
43 template <typename Data>
44 bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix,
45  const fs::path &path, const Data &data) {
46  // Generate random temporary filename
47  uint16_t randv = 0;
48  GetRandBytes((uint8_t *)&randv, sizeof(randv));
49  std::string tmpfn = strprintf("%s.%04x", prefix, randv);
50 
51  // open temp output file, and associate with CAutoFile
52  fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
53  FILE *file = fsbridge::fopen(pathTmp, "wb");
54  CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
55  if (fileout.IsNull()) {
56  fileout.fclose();
57  remove(pathTmp);
58  return error("%s: Failed to open file %s", __func__,
59  fs::PathToString(pathTmp));
60  }
61 
62  // Serialize
63  if (!SerializeDB(chainParams, fileout, data)) {
64  fileout.fclose();
65  remove(pathTmp);
66  return false;
67  }
68  if (!FileCommit(fileout.Get())) {
69  fileout.fclose();
70  remove(pathTmp);
71  return error("%s: Failed to flush file %s", __func__,
72  fs::PathToString(pathTmp));
73  }
74  fileout.fclose();
75 
76  // replace existing file, if any, with new file
77  if (!RenameOver(pathTmp, path)) {
78  remove(pathTmp);
79  return error("%s: Rename-into-place failed", __func__);
80  }
81 
82  return true;
83 }
84 
85 template <typename Stream, typename Data>
86 void DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data,
87  bool fCheckSum = true) {
88  CHashVerifier<Stream> verifier(&stream);
89  // de-serialize file header (network specific magic number) and ..
90  uint8_t pchMsgTmp[4];
91  verifier >> pchMsgTmp;
92  // ... verify the network matches ours
93  if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()),
94  sizeof(pchMsgTmp))) {
95  throw std::runtime_error{"Invalid network magic number"};
96  }
97 
98  // de-serialize data
99  verifier >> data;
100 
101  // verify checksum
102  if (fCheckSum) {
103  uint256 hashTmp;
104  stream >> hashTmp;
105  if (hashTmp != verifier.GetHash()) {
106  throw std::runtime_error{"Checksum mismatch, data corrupted"};
107  }
108  }
109 }
110 
111 template <typename Data>
112 void DeserializeFileDB(const CChainParams &chainParams, const fs::path &path,
113  Data &data) {
114  // open input file, and associate with CAutoFile
115  FILE *file = fsbridge::fopen(path, "rb");
116  CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
117  if (filein.IsNull()) {
118  throw DbNotFoundError{};
119  }
120 
121  DeserializeDB(chainParams, filein, data);
122 }
123 
124 } // namespace
125 
126 CBanDB::CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
127  : m_ban_list_path(std::move(ban_list_path)), chainParams(_chainParams) {}
128 
129 bool CBanDB::Write(const banmap_t &banSet) {
130  return SerializeFileDB(chainParams, "banlist", m_ban_list_path, banSet);
131 }
132 
133 bool CBanDB::Read(banmap_t &banSet) {
134  // TODO: this needs to be reworked after banlist.dat is deprecated (in
135  // favor of banlist.json). See:
136  // - https://github.com/bitcoin/bitcoin/pull/20966
137  // - https://github.com/bitcoin/bitcoin/pull/22570
138  try {
139  DeserializeFileDB(chainParams, m_ban_list_path, banSet);
140  } catch (const std::exception &) {
141  LogPrintf("Missing or invalid file %s\n",
143  return false;
144  }
145 
146  return true;
147 }
148 
149 bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args,
150  const AddrMan &addr) {
151  const auto pathAddr = args.GetDataDirNet() / "peers.dat";
152  return SerializeFileDB(chainParams, "peers", pathAddr, addr);
153 }
154 
155 void ReadFromStream(const CChainParams &chainParams, AddrMan &addr,
156  CDataStream &ssPeers) {
157  DeserializeDB(chainParams, ssPeers, addr, false);
158 }
159 
160 std::optional<bilingual_str> LoadAddrman(const CChainParams &chainparams,
161  const std::vector<bool> &asmap,
162  const ArgsManager &args,
163  std::unique_ptr<AddrMan> &addrman) {
164  auto check_addrman = std::clamp<int32_t>(
165  args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0,
166  1000000);
167  addrman = std::make_unique<AddrMan>(
168  asmap, /* consistency_check_ratio= */ check_addrman);
169 
170  int64_t nStart = GetTimeMillis();
171  const auto path_addr{args.GetDataDirNet() / "peers.dat"};
172  try {
173  DeserializeFileDB(chainparams, path_addr, *addrman);
174  LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(),
175  GetTimeMillis() - nStart);
176  } catch (const DbNotFoundError &) {
177  // Addrman can be in an inconsistent state after failure, reset it
178  addrman = std::make_unique<AddrMan>(
179  asmap, /* consistency_check_ratio= */ check_addrman);
180  LogPrintf("Creating peers.dat because the file was not found (%s)\n",
181  fs::quoted(fs::PathToString(path_addr)));
182  DumpPeerAddresses(chainparams, args, *addrman);
183  } catch (const InvalidAddrManVersionError &) {
184  if (!RenameOver(path_addr, fs::path(path_addr) + ".bak")) {
185  addrman = nullptr;
186  return strprintf(_("Failed to rename invalid peers.dat file. "
187  "Please move or delete it and try again."));
188  }
189  // Addrman can be in an inconsistent state after failure, reset it
190  addrman = std::make_unique<AddrMan>(
191  asmap, /* consistency_check_ratio= */ check_addrman);
192  LogPrintf("Creating new peers.dat because the file version was not "
193  "compatible (%s). Original backed up to peers.dat.bak\n",
194  fs::quoted(fs::PathToString(path_addr)));
195  DumpPeerAddresses(chainparams, args, *addrman);
196  } catch (const std::exception &e) {
197  addrman = nullptr;
198  return strprintf(
199  _("Invalid or corrupt peers.dat (%s). If you believe this is a "
200  "bug, please report it to %s. As a workaround, you can move the "
201  "file (%s) out of the way (rename, move, or delete) to have a "
202  "new one created on the next start."),
203  e.what(), PACKAGE_BUGREPORT,
204  fs::quoted(fs::PathToString(path_addr)));
205  }
206 
207  return std::nullopt;
208 }
209 
210 void DumpAnchors(const CChainParams &chainParams,
211  const fs::path &anchors_db_path,
212  const std::vector<CAddress> &anchors) {
214  "Flush %d outbound block-relay-only peer addresses to anchors.dat",
215  anchors.size()));
216  SerializeFileDB(chainParams, "anchors", anchors_db_path, anchors);
217 }
218 
219 std::vector<CAddress> ReadAnchors(const CChainParams &chainParams,
220  const fs::path &anchors_db_path) {
221  std::vector<CAddress> anchors;
222  try {
223  DeserializeFileDB(chainParams, anchors_db_path, anchors);
224  LogPrintf("Loaded %i addresses from %s\n", anchors.size(),
225  fs::quoted(fs::PathToString(anchors_db_path.filename())));
226  } catch (const std::exception &) {
227  anchors.clear();
228  }
229 
230  fs::remove(anchors_db_path);
231  return anchors;
232 }
void ReadFromStream(const CChainParams &chainParams, AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:155
bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:149
std::vector< CAddress > ReadAnchors(const CChainParams &chainParams, const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:219
std::optional< bilingual_str > LoadAddrman(const CChainParams &chainparams, const std::vector< bool > &asmap, const ArgsManager &args, std::unique_ptr< AddrMan > &addrman)
Returns an error string on failure.
Definition: addrdb.cpp:160
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:210
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS
Default for -checkaddrman.
Definition: addrman.h:28
Stochastic address manager.
Definition: addrman.h:68
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:260
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: system.cpp:593
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:581
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:129
const fs::path m_ban_list_path
Definition: addrdb.h:60
CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
Definition: addrdb.cpp:126
bool Read(banmap_t &banSet)
Definition: addrdb.cpp:133
const CChainParams & chainParams
Definition: addrdb.h:61
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:74
const CMessageHeader::MessageMagic & DiskMagic() const
Definition: chainparams.h:87
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:197
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:161
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:33
256-bit opaque blob.
Definition: uint256.h:127
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:44
#define LogPrintf(...)
Definition: logging.h:204
static auto quoted(const std::string &s)
Definition: fs.h:99
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:134
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:27
std::map< CSubNet, CBanEntry > banmap_t
Definition: net_types.h:13
void GetRandBytes(uint8_t *buf, int num) noexcept
Overall design of the RNG and entropy sources.
Definition: random.cpp:634
const char * prefix
Definition: rest.cpp:823
@ SER_DISK
Definition: serialize.h:167
bool RenameOver(fs::path src, fs::path dest)
Definition: system.cpp:1124
ArgsManager gArgs
Definition: system.cpp:77
bool FileCommit(FILE *file)
Definition: system.cpp:1153
bool error(const char *fmt, const Args &...args)
Definition: system.h:46
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: time.cpp:106
#define LOG_TIME_SECONDS(end_msg)
Definition: timer.h:104
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:55