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 <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 
24 namespace {
25 
26 class DbNotFoundError : public std::exception {
27  using std::exception::exception;
28 };
29 
30 template <typename Stream, typename Data>
31 bool SerializeDB(const CChainParams &chainParams, Stream &stream,
32  const Data &data) {
33  // Write and commit header, data
34  try {
35  CHashWriter hasher(stream.GetType(), stream.GetVersion());
36  stream << chainParams.DiskMagic() << data;
37  hasher << chainParams.DiskMagic() << data;
38  stream << hasher.GetHash();
39  } catch (const std::exception &e) {
40  return error("%s: Serialize or I/O error - %s", __func__, e.what());
41  }
42 
43  return true;
44 }
45 
46 template <typename Data>
47 bool 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  return error("%s: Failed to open file %s", __func__,
61  fs::PathToString(pathTmp));
62  }
63 
64  // Serialize
65  if (!SerializeDB(chainParams, fileout, data)) {
66  fileout.fclose();
67  remove(pathTmp);
68  return false;
69  }
70  if (!FileCommit(fileout.Get())) {
71  fileout.fclose();
72  remove(pathTmp);
73  return error("%s: Failed to flush file %s", __func__,
74  fs::PathToString(pathTmp));
75  }
76  fileout.fclose();
77 
78  // replace existing file, if any, with new file
79  if (!RenameOver(pathTmp, path)) {
80  remove(pathTmp);
81  return error("%s: Rename-into-place failed", __func__);
82  }
83 
84  return true;
85 }
86 
87 template <typename Stream, typename Data>
88 void DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data,
89  bool fCheckSum = true) {
90  CHashVerifier<Stream> verifier(&stream);
91  // de-serialize file header (network specific magic number) and ..
92  uint8_t pchMsgTmp[4];
93  verifier >> pchMsgTmp;
94  // ... verify the network matches ours
95  if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()),
96  sizeof(pchMsgTmp))) {
97  throw std::runtime_error{"Invalid network magic number"};
98  }
99 
100  // de-serialize data
101  verifier >> data;
102 
103  // verify checksum
104  if (fCheckSum) {
105  uint256 hashTmp;
106  stream >> hashTmp;
107  if (hashTmp != verifier.GetHash()) {
108  throw std::runtime_error{"Checksum mismatch, data corrupted"};
109  }
110  }
111 }
112 
113 template <typename Data>
114 void DeserializeFileDB(const CChainParams &chainParams, const fs::path &path,
115  Data &data, int version) {
116  // open input file, and associate with CAutoFile
117  FILE *file = fsbridge::fopen(path, "rb");
118  CAutoFile filein(file, SER_DISK, version);
119  if (filein.IsNull()) {
120  throw DbNotFoundError{};
121  }
122 
123  DeserializeDB(chainParams, filein, data);
124 }
125 
126 } // namespace
127 
128 CBanDB::CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
129  : m_ban_list_path(std::move(ban_list_path)), chainParams(_chainParams) {}
130 
131 bool CBanDB::Write(const banmap_t &banSet) {
132  return SerializeFileDB(chainParams, "banlist", m_ban_list_path, banSet,
134 }
135 
136 bool CBanDB::Read(banmap_t &banSet) {
137  // TODO: this needs to be reworked after banlist.dat is deprecated (in
138  // favor of banlist.json). See:
139  // - https://github.com/bitcoin/bitcoin/pull/20966
140  // - https://github.com/bitcoin/bitcoin/pull/22570
141  try {
142  DeserializeFileDB(chainParams, m_ban_list_path, banSet, CLIENT_VERSION);
143  } catch (const std::exception &) {
144  LogPrintf("Missing or invalid file %s\n",
146  return false;
147  }
148 
149  return true;
150 }
151 
152 bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args,
153  const AddrMan &addr) {
154  const auto pathAddr = args.GetDataDirNet() / "peers.dat";
155  return SerializeFileDB(chainParams, "peers", pathAddr, addr,
157 }
158 
159 void ReadFromStream(const CChainParams &chainParams, AddrMan &addr,
160  CDataStream &ssPeers) {
161  DeserializeDB(chainParams, ssPeers, addr, false);
162 }
163 
165 LoadAddrman(const CChainParams &chainparams, const std::vector<bool> &asmap,
166  const ArgsManager &args) {
167  auto check_addrman = std::clamp<int32_t>(
168  args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0,
169  1000000);
170  auto addrman{std::make_unique<AddrMan>(
171  asmap, /* consistency_check_ratio= */ check_addrman)};
172 
173  int64_t nStart = GetTimeMillis();
174  const auto path_addr{args.GetDataDirNet() / "peers.dat"};
175  try {
176  DeserializeFileDB(chainparams, path_addr, *addrman, CLIENT_VERSION);
177  LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(),
178  GetTimeMillis() - nStart);
179  } catch (const DbNotFoundError &) {
180  // Addrman can be in an inconsistent state after failure, reset it
181  addrman = std::make_unique<AddrMan>(
182  asmap, /* consistency_check_ratio= */ check_addrman);
183  LogPrintf("Creating peers.dat because the file was not found (%s)\n",
184  fs::quoted(fs::PathToString(path_addr)));
185  DumpPeerAddresses(chainparams, args, *addrman);
186  } catch (const InvalidAddrManVersionError &) {
187  if (!RenameOver(path_addr, fs::path(path_addr) + ".bak")) {
188  return util::Error{
189  strprintf(_("Failed to rename invalid peers.dat file. "
190  "Please move or delete it and try again."))};
191  }
192  // Addrman can be in an inconsistent state after failure, reset it
193  addrman = std::make_unique<AddrMan>(
194  asmap, /* consistency_check_ratio= */ check_addrman);
195  LogPrintf("Creating new peers.dat because the file version was not "
196  "compatible (%s). Original backed up to peers.dat.bak\n",
197  fs::quoted(fs::PathToString(path_addr)));
198  DumpPeerAddresses(chainparams, args, *addrman);
199  } catch (const std::exception &e) {
200  return util::Error{strprintf(
201  _("Invalid or corrupt peers.dat (%s). If you believe this is a "
202  "bug, please report it to %s. As a workaround, you can move the "
203  "file (%s) out of the way (rename, move, or delete) to have a "
204  "new one created on the next start."),
205  e.what(), PACKAGE_BUGREPORT,
206  fs::quoted(fs::PathToString(path_addr)))};
207  }
208 
209  // std::move should be unneccessary but is temporarily needed to work
210  // around clang bug
211  // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1564350880)
212  return {std::move(addrman)};
213 }
214 
215 void DumpAnchors(const CChainParams &chainParams,
216  const fs::path &anchors_db_path,
217  const std::vector<CAddress> &anchors) {
219  "Flush %d outbound block-relay-only peer addresses to anchors.dat",
220  anchors.size()));
221  SerializeFileDB(chainParams, "anchors", anchors_db_path, anchors,
223 }
224 
225 std::vector<CAddress> ReadAnchors(const CChainParams &chainParams,
226  const fs::path &anchors_db_path) {
227  std::vector<CAddress> anchors;
228  try {
229  DeserializeFileDB(chainParams, anchors_db_path, anchors,
231  LogPrintf("Loaded %i addresses from %s\n", anchors.size(),
232  fs::quoted(fs::PathToString(anchors_db_path.filename())));
233  } catch (const std::exception &) {
234  anchors.clear();
235  }
236 
237  fs::remove(anchors_db_path);
238  return anchors;
239 }
void ReadFromStream(const CChainParams &chainParams, AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:159
bool DumpPeerAddresses(const CChainParams &chainParams, const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:152
std::vector< CAddress > ReadAnchors(const CChainParams &chainParams, const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:225
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:215
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:165
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS
Default for -checkaddrman.
Definition: addrman.h:29
ArgsManager gArgs
Definition: args.cpp:38
Stochastic address manager.
Definition: addrman.h:69
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:215
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:526
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:131
const fs::path m_ban_list_path
Definition: addrdb.h:60
CBanDB(fs::path ban_list_path, const CChainParams &_chainParams)
Definition: addrdb.cpp:128
bool Read(banmap_t &banSet)
Definition: addrdb.cpp:136
const CChainParams & chainParams
Definition: addrdb.h:61
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:80
const CMessageHeader::MessageMagic & DiskMagic() const
Definition: chainparams.h:93
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:169
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:272
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:125
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
#define LogPrintf(...)
Definition: logging.h:207
static auto quoted(const std::string &s)
Definition: fs.h:107
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:142
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:257
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:817
@ SER_DISK
Definition: serialize.h:153
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