Bitcoin Core  24.99.0
P2P Digital Currency
addrdb.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2021 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 <cstdint>
12 #include <fs.h>
13 #include <hash.h>
14 #include <logging/timer.h>
15 #include <netbase.h>
16 #include <netgroup.h>
17 #include <random.h>
18 #include <streams.h>
19 #include <tinyformat.h>
20 #include <univalue.h>
21 #include <util/settings.h>
22 #include <util/system.h>
23 #include <util/translation.h>
24 
25 namespace {
26 
27 class DbNotFoundError : public std::exception
28 {
29  using std::exception::exception;
30 };
31 
32 template <typename Stream, typename Data>
33 bool SerializeDB(Stream& stream, const Data& data)
34 {
35  // Write and commit header, data
36  try {
37  CHashWriter hasher(stream.GetType(), stream.GetVersion());
38  stream << Params().MessageStart() << data;
39  hasher << Params().MessageStart() << data;
40  stream << hasher.GetHash();
41  } catch (const std::exception& e) {
42  return error("%s: Serialize or I/O error - %s", __func__, e.what());
43  }
44 
45  return true;
46 }
47 
48 template <typename Data>
49 bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
50 {
51  // Generate random temporary filename
52  const uint16_t randv{GetRand<uint16_t>()};
53  std::string tmpfn = strprintf("%s.%04x", prefix, randv);
54 
55  // open temp output file, and associate with CAutoFile
56  fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
57  FILE *file = fsbridge::fopen(pathTmp, "wb");
58  CAutoFile fileout(file, SER_DISK, version);
59  if (fileout.IsNull()) {
60  fileout.fclose();
61  remove(pathTmp);
62  return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp));
63  }
64 
65  // Serialize
66  if (!SerializeDB(fileout, data)) {
67  fileout.fclose();
68  remove(pathTmp);
69  return false;
70  }
71  if (!FileCommit(fileout.Get())) {
72  fileout.fclose();
73  remove(pathTmp);
74  return error("%s: Failed to flush file %s", __func__, 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(Stream& stream, Data& data, bool fCheckSum = true)
89 {
90  CHashVerifier<Stream> verifier(&stream);
91  // de-serialize file header (network specific magic number) and ..
92  unsigned char pchMsgTmp[4];
93  verifier >> pchMsgTmp;
94  // ... verify the network matches ours
95  if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
96  throw std::runtime_error{"Invalid network magic number"};
97  }
98 
99  // de-serialize data
100  verifier >> data;
101 
102  // verify checksum
103  if (fCheckSum) {
104  uint256 hashTmp;
105  stream >> hashTmp;
106  if (hashTmp != verifier.GetHash()) {
107  throw std::runtime_error{"Checksum mismatch, data corrupted"};
108  }
109  }
110 }
111 
112 template <typename Data>
113 void DeserializeFileDB(const fs::path& path, Data& data, int version)
114 {
115  // open input file, and associate with CAutoFile
116  FILE* file = fsbridge::fopen(path, "rb");
117  CAutoFile filein(file, SER_DISK, version);
118  if (filein.IsNull()) {
119  throw DbNotFoundError{};
120  }
121  DeserializeDB(filein, data);
122 }
123 } // namespace
124 
125 CBanDB::CBanDB(fs::path ban_list_path)
126  : m_banlist_dat(ban_list_path + ".dat"),
127  m_banlist_json(ban_list_path + ".json")
128 {
129 }
130 
131 bool CBanDB::Write(const banmap_t& banSet)
132 {
133  std::vector<std::string> errors;
134  if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
135  return true;
136  }
137 
138  for (const auto& err : errors) {
139  error("%s", err);
140  }
141  return false;
142 }
143 
144 bool CBanDB::Read(banmap_t& banSet)
145 {
146  if (fs::exists(m_banlist_dat)) {
147  LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 22.x. Remove %s to silence this warning.\n", fs::quoted(fs::PathToString(m_banlist_dat)));
148  }
149  // If the JSON banlist does not exist, then recreate it
150  if (!fs::exists(m_banlist_json)) {
151  return false;
152  }
153 
154  std::map<std::string, util::SettingsValue> settings;
155  std::vector<std::string> errors;
156 
157  if (!util::ReadSettings(m_banlist_json, settings, errors)) {
158  for (const auto& err : errors) {
159  LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
160  }
161  return false;
162  }
163 
164  try {
165  BanMapFromJson(settings[JSON_KEY], banSet);
166  } catch (const std::runtime_error& e) {
167  LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
168  return false;
169  }
170 
171  return true;
172 }
173 
174 bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
175 {
176  const auto pathAddr = args.GetDataDirNet() / "peers.dat";
177  return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
178 }
179 
180 void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
181 {
182  DeserializeDB(ssPeers, addr, false);
183 }
184 
185 std::optional<bilingual_str> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman)
186 {
187  auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
188  addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
189 
190  const auto start{SteadyClock::now()};
191  const auto path_addr{args.GetDataDirNet() / "peers.dat"};
192  try {
193  DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
194  LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
195  } catch (const DbNotFoundError&) {
196  // Addrman can be in an inconsistent state after failure, reset it
197  addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
198  LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
199  DumpPeerAddresses(args, *addrman);
200  } catch (const InvalidAddrManVersionError&) {
201  if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
202  addrman = nullptr;
203  return strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."));
204  }
205  // Addrman can be in an inconsistent state after failure, reset it
206  addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
207  LogPrintf("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak\n", fs::quoted(fs::PathToString(path_addr)));
208  DumpPeerAddresses(args, *addrman);
209  } catch (const std::exception& e) {
210  addrman = nullptr;
211  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."),
212  e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)));
213  }
214  return std::nullopt;
215 }
216 
217 void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
218 {
219  LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
220  SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
221 }
222 
223 std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
224 {
225  std::vector<CAddress> anchors;
226  try {
227  DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
228  LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
229  } catch (const std::exception&) {
230  anchors.clear();
231  }
232 
233  fs::remove(anchors_db_path);
234  return anchors;
235 }
std::vector< CAddress > ReadAnchors(const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:223
std::optional< bilingual_str > LoadAddrman(const NetGroupManager &netgroupman, const ArgsManager &args, std::unique_ptr< AddrMan > &addrman)
Returns an error string on failure.
Definition: addrdb.cpp:185
bool DumpPeerAddresses(const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:174
void DumpAnchors(const fs::path &anchors_db_path, const std::vector< CAddress > &anchors)
Dump the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:217
void ReadFromStream(AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:180
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS
Default for -checkaddrman.
Definition: addrman.h:30
#define PACKAGE_NAME
#define PACKAGE_BUGREPORT
const CChainParams & Params()
Return the currently selected parameters.
Stochastic address manager.
Definition: addrman.h:87
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:303
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: system.cpp:629
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:131
const fs::path m_banlist_dat
Definition: addrdb.h:36
bool Read(banmap_t &banSet)
Read the banlist from disk.
Definition: addrdb.cpp:144
static constexpr const char * JSON_KEY
JSON key under which the data is stored in the json database.
Definition: addrdb.h:34
const fs::path m_banlist_json
Definition: addrdb.h:37
CBanDB(fs::path ban_list_path)
Definition: addrdb.cpp:125
const CMessageHeader::MessageStartChars & MessageStart() const
Definition: chainparams.h:83
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:186
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:170
Netgroup manager.
Definition: netgroup.h:16
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:31
path filename() const
Definition: fs.h:67
256-bit opaque blob.
Definition: uint256.h:119
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:33
#define LogPrintf(...)
Definition: logging.h:234
static path u8path(const std::string &utf8_str)
Definition: fs.h:70
static auto quoted(const std::string &s)
Definition: fs.h:94
static bool exists(const path &p)
Definition: fs.h:88
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:150
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
bool ReadSettings(const fs::path &path, std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Read settings file.
Definition: settings.cpp:64
bool WriteSettings(const fs::path &path, const std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Write settings file.
Definition: settings.cpp:107
void BanMapFromJson(const UniValue &bans_json, banmap_t &bans)
Convert a JSON array to a banmap_t object.
Definition: net_types.cpp:58
UniValue BanMapToJson(const banmap_t &bans)
Convert a banmap_t object to a JSON array.
Definition: net_types.cpp:38
std::map< CSubNet, CBanEntry > banmap_t
Definition: net_types.h:41
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
ArgsManager args
const char * prefix
Definition: rest.cpp:930
@ SER_DISK
Definition: serialize.h:132
bool error(const char *fmt, const Args &... args)
Definition: system.h:48
#define LOG_TIME_SECONDS(end_msg)
Definition: timer.h:105
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:65
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: system.cpp:1136
ArgsManager gArgs
Definition: system.cpp:86
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: system.cpp:1173