Bitcoin Core  31.0.0
P2P Digital Currency
addrdb.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present 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 <bitcoin-build-config.h> // IWYU pragma: keep
7 
8 #include <addrdb.h>
9 
10 #include <addrman.h>
11 #include <chainparams.h>
12 #include <clientversion.h>
13 #include <common/args.h>
14 #include <common/settings.h>
15 #include <cstdint>
16 #include <hash.h>
17 #include <logging.h>
18 #include <logging/timer.h>
19 #include <netbase.h>
20 #include <netgroup.h>
21 #include <random.h>
22 #include <streams.h>
23 #include <tinyformat.h>
24 #include <univalue.h>
25 #include <util/fs.h>
26 #include <util/fs_helpers.h>
27 #include <util/syserror.h>
28 #include <util/translation.h>
29 
30 namespace {
31 
32 class DbNotFoundError : public std::exception
33 {
34  using std::exception::exception;
35 };
36 
37 template <typename Stream, typename Data>
38 bool SerializeDB(Stream& stream, const Data& data)
39 {
40  // Write and commit header, data
41  try {
42  HashedSourceWriter hashwriter{stream};
43  hashwriter << Params().MessageStart() << data;
44  stream << hashwriter.GetHash();
45  } catch (const std::exception& e) {
46  LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
47  return false;
48  }
49 
50  return true;
51 }
52 
53 template <typename Data>
54 bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
55 {
56  // Generate random temporary filename
57  const uint16_t randv{FastRandomContext().rand<uint16_t>()};
58  std::string tmpfn = strprintf("%s.%04x", prefix, randv);
59 
60  // open temp output file
61  fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
62  FILE *file = fsbridge::fopen(pathTmp, "wb");
63  AutoFile fileout{file};
64  if (fileout.IsNull()) {
65  remove(pathTmp);
66  LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
67  return false;
68  }
69 
70  // Serialize
71  if (!SerializeDB(fileout, data)) {
72  (void)fileout.fclose();
73  remove(pathTmp);
74  return false;
75  }
76  if (!fileout.Commit()) {
77  (void)fileout.fclose();
78  remove(pathTmp);
79  LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
80  return false;
81  }
82  if (fileout.fclose() != 0) {
83  const int errno_save{errno};
84  remove(pathTmp);
85  LogError("Failed to close file %s after commit: %s", fs::PathToString(pathTmp), SysErrorString(errno_save));
86  return false;
87  }
88 
89  // replace existing file, if any, with new file
90  if (!RenameOver(pathTmp, path)) {
91  remove(pathTmp);
92  LogError("%s: Rename-into-place failed\n", __func__);
93  return false;
94  }
95 
96  return true;
97 }
98 
99 template <typename Stream, typename Data>
100 void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)
101 {
102  HashVerifier verifier{stream};
103  // de-serialize file header (network specific magic number) and ..
104  MessageStartChars pchMsgTmp;
105  verifier >> pchMsgTmp;
106  // ... verify the network matches ours
107  if (pchMsgTmp != Params().MessageStart()) {
108  throw std::runtime_error{"Invalid network magic number"};
109  }
110 
111  // de-serialize data
112  verifier >> data;
113 
114  // verify checksum
115  if (fCheckSum) {
116  uint256 hashTmp;
117  stream >> hashTmp;
118  if (hashTmp != verifier.GetHash()) {
119  throw std::runtime_error{"Checksum mismatch, data corrupted"};
120  }
121  }
122 }
123 
124 template <typename Data>
125 void DeserializeFileDB(const fs::path& path, Data&& data)
126 {
127  FILE* file = fsbridge::fopen(path, "rb");
128  AutoFile filein{file};
129  if (filein.IsNull()) {
130  throw DbNotFoundError{};
131  }
132  DeserializeDB(filein, data);
133 }
134 } // namespace
135 
136 CBanDB::CBanDB(fs::path ban_list_path)
137  : m_banlist_dat(ban_list_path + ".dat"),
138  m_banlist_json(ban_list_path + ".json")
139 {
140 }
141 
142 bool CBanDB::Write(const banmap_t& banSet)
143 {
144  std::vector<std::string> errors;
145  if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
146  return true;
147  }
148 
149  for (const auto& err : errors) {
150  LogError("%s\n", err);
151  }
152  return false;
153 }
154 
155 bool CBanDB::Read(banmap_t& banSet)
156 {
157  if (fs::exists(m_banlist_dat)) {
158  LogWarning("banlist.dat ignored because it can only be read by " CLIENT_NAME " version 22.x. Remove %s to silence this warning.", fs::quoted(fs::PathToString(m_banlist_dat)));
159  }
160  // If the JSON banlist does not exist, then recreate it
161  if (!fs::exists(m_banlist_json)) {
162  return false;
163  }
164 
165  std::map<std::string, common::SettingsValue> settings;
166  std::vector<std::string> errors;
167 
168  if (!common::ReadSettings(m_banlist_json, settings, errors)) {
169  for (const auto& err : errors) {
170  LogWarning("Cannot load banlist %s: %s", fs::PathToString(m_banlist_json), err);
171  }
172  return false;
173  }
174 
175  try {
176  BanMapFromJson(settings[JSON_KEY], banSet);
177  } catch (const std::runtime_error& e) {
178  LogWarning("Cannot parse banlist %s: %s", fs::PathToString(m_banlist_json), e.what());
179  return false;
180  }
181 
182  return true;
183 }
184 
185 bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
186 {
187  const auto pathAddr = args.GetDataDirNet() / "peers.dat";
188  return SerializeFileDB("peers", pathAddr, addr);
189 }
190 
191 void ReadFromStream(AddrMan& addr, DataStream& ssPeers)
192 {
193  DeserializeDB(ssPeers, addr, false);
194 }
195 
197 {
198  auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
199  bool deterministic = HasTestOption(args, "addrman"); // use a deterministic addrman only for tests
200 
201  auto addrman{std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman)};
202 
203  const auto start{SteadyClock::now()};
204  const auto path_addr{args.GetDataDirNet() / "peers.dat"};
205  try {
206  DeserializeFileDB(path_addr, *addrman);
207  LogInfo("Loaded %i addresses from peers.dat %dms", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
208  } catch (const DbNotFoundError&) {
209  // Addrman can be in an inconsistent state after failure, reset it
210  addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
211  LogInfo("Creating peers.dat because the file was not found (%s)", fs::quoted(fs::PathToString(path_addr)));
212  DumpPeerAddresses(args, *addrman);
213  } catch (const InvalidAddrManVersionError&) {
214  if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
215  return util::Error{strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."))};
216  }
217  // Addrman can be in an inconsistent state after failure, reset it
218  addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
219  LogWarning("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak", fs::quoted(fs::PathToString(path_addr)));
220  DumpPeerAddresses(args, *addrman);
221  } catch (const std::exception& e) {
222  return util::Error{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."),
223  e.what(), CLIENT_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
224  }
225  return addrman;
226 }
227 
228 void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
229 {
230  LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
231  SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));
232 }
233 
234 std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
235 {
236  std::vector<CAddress> anchors;
237  try {
238  DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));
239  LogInfo("Loaded %i addresses from %s", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
240  } catch (const std::exception&) {
241  anchors.clear();
242  }
243 
244  fs::remove(anchors_db_path);
245  return anchors;
246 }
UniValue BanMapToJson(const banmap_t &bans)
Convert a banmap_t object to a JSON array.
Definition: net_types.cpp:38
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
#define LogWarning(...)
Definition: log.h:96
bool ReadSettings(const fs::path &path, std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Read settings file.
Definition: settings.cpp:72
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
const char * prefix
Definition: rest.cpp:1141
#define LOG_TIME_SECONDS(end_msg)
Definition: timer.h:107
bool DumpPeerAddresses(const ArgsManager &args, const AddrMan &addr)
Definition: addrdb.cpp:185
void BanMapFromJson(const UniValue &bans_json, banmap_t &bans)
Convert a JSON array to a banmap_t object.
Definition: net_types.cpp:58
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
Netgroup manager.
Definition: netgroup.h:17
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:372
void DumpAnchors(const fs::path &anchors_db_path, const std::vector< CAddress > &anchors)
Dump the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:228
I rand() noexcept
Generate a random integer in its entire (non-negative) range.
Definition: random.h:287
Stochastic address manager.
Definition: addrman.h:88
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:243
util::Result< std::unique_ptr< AddrMan > > LoadAddrman(const NetGroupManager &netgroupman, const ArgsManager &args)
Returns an error string on failure.
Definition: addrdb.cpp:196
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:17
std::array< uint8_t, 4 > MessageStartChars
static constexpr SerParams V2_DISK
Definition: protocol.h:411
static bool exists(const path &p)
Definition: fs.h:95
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
CBanDB(fs::path ban_list_path)
Definition: addrdb.cpp:136
ArgsManager & args
Definition: bitcoind.cpp:277
std::vector< CAddress > ReadAnchors(const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:234
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:132
#define LogInfo(...)
Definition: log.h:95
Fast randomness source.
Definition: random.h:385
static constexpr const char * JSON_KEY
JSON key under which the data is stored in the json database.
Definition: addrdb.h:34
bool HasTestOption(const ArgsManager &args, const std::string &test_option)
Checks if a particular test option is present in -test command-line arg options.
Definition: args.cpp:749
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:142
ArgsManager gArgs
Definition: args.cpp:40
bool Read(banmap_t &banSet)
Read the banlist from disk.
Definition: addrdb.cpp:155
void ReadFromStream(AddrMan &addr, DataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:191
256-bit opaque blob.
Definition: uint256.h:195
const fs::path m_banlist_dat
Definition: addrdb.h:36
const CChainParams & Params()
Return the currently selected parameters.
static auto quoted(const std::string &s)
Definition: fs.h:101
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Definition: args.h:306
const MessageStartChars & MessageStart() const
Definition: chainparams.h:90
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS
Default for -checkaddrman.
Definition: addrman.h:32
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
const fs::path m_banlist_json
Definition: addrdb.h:37
Writes data to an underlying source stream, while hashing the written data.
Definition: hash.h:184
bool WriteSettings(const fs::path &path, const std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Write settings file.
Definition: settings.cpp:123
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:150
std::map< CSubNet, CBanEntry > banmap_t
Definition: net_types.h:41
static path u8path(std::string_view utf8_str)
Definition: fs.h:81
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
#define LogError(...)
Definition: log.h:97