Branch data Line data Source code
# 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 <random.h>
# 17 : : #include <streams.h>
# 18 : : #include <tinyformat.h>
# 19 : : #include <univalue.h>
# 20 : : #include <util/settings.h>
# 21 : : #include <util/system.h>
# 22 : : #include <util/translation.h>
# 23 : :
# 24 : : namespace {
# 25 : :
# 26 : : class DbNotFoundError : public std::exception
# 27 : : {
# 28 : : using std::exception::exception;
# 29 : : };
# 30 : :
# 31 : : template <typename Stream, typename Data>
# 32 : : bool SerializeDB(Stream& stream, const Data& data)
# 33 : 1165 : {
# 34 : : // Write and commit header, data
# 35 : 1165 : try {
# 36 : 1165 : CHashWriter hasher(stream.GetType(), stream.GetVersion());
# 37 : 1165 : stream << Params().MessageStart() << data;
# 38 : 1165 : hasher << Params().MessageStart() << data;
# 39 : 1165 : stream << hasher.GetHash();
# 40 : 1165 : } catch (const std::exception& e) {
# 41 : 0 : return error("%s: Serialize or I/O error - %s", __func__, e.what());
# 42 : 0 : }
# 43 : :
# 44 : 1165 : return true;
# 45 : 1165 : }
# 46 : :
# 47 : : template <typename Data>
# 48 : : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
# 49 : 1165 : {
# 50 : : // Generate random temporary filename
# 51 : 1165 : uint16_t randv = 0;
# 52 : 1165 : GetRandBytes((unsigned char*)&randv, sizeof(randv));
# 53 : 1165 : std::string tmpfn = strprintf("%s.%04x", prefix, randv);
# 54 : :
# 55 : : // open temp output file, and associate with CAutoFile
# 56 : 1165 : fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
# 57 : 1165 : FILE *file = fsbridge::fopen(pathTmp, "wb");
# 58 : 1165 : CAutoFile fileout(file, SER_DISK, version);
# 59 [ - + ][ - + ]: 1165 : if (fileout.IsNull()) {
# 60 : 0 : fileout.fclose();
# 61 : 0 : remove(pathTmp);
# 62 : 0 : return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp));
# 63 : 0 : }
# 64 : :
# 65 : : // Serialize
# 66 [ - + ][ - + ]: 1165 : if (!SerializeDB(fileout, data)) {
# 67 : 0 : fileout.fclose();
# 68 : 0 : remove(pathTmp);
# 69 : 0 : return false;
# 70 : 0 : }
# 71 [ - + ][ - + ]: 1165 : if (!FileCommit(fileout.Get())) {
# 72 : 0 : fileout.fclose();
# 73 : 0 : remove(pathTmp);
# 74 : 0 : return error("%s: Failed to flush file %s", __func__, fs::PathToString(pathTmp));
# 75 : 0 : }
# 76 : 1165 : fileout.fclose();
# 77 : :
# 78 : : // replace existing file, if any, with new file
# 79 [ - + ][ - + ]: 1165 : if (!RenameOver(pathTmp, path)) {
# 80 : 0 : remove(pathTmp);
# 81 : 0 : return error("%s: Rename-into-place failed", __func__);
# 82 : 0 : }
# 83 : :
# 84 : 1165 : return true;
# 85 : 1165 : }
# 86 : :
# 87 : : template <typename Stream, typename Data>
# 88 : : void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
# 89 : 359 : {
# 90 : 359 : CHashVerifier<Stream> verifier(&stream);
# 91 : : // de-serialize file header (network specific magic number) and ..
# 92 : 359 : unsigned char pchMsgTmp[4];
# 93 : 359 : verifier >> pchMsgTmp;
# 94 : : // ... verify the network matches ours
# 95 [ - + ][ + + ]: 359 : if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
# [ - + ]
# 96 : 1 : throw std::runtime_error{"Invalid network magic number"};
# 97 : 1 : }
# 98 : :
# 99 : : // de-serialize data
# 100 : 358 : verifier >> data;
# 101 : :
# 102 : : // verify checksum
# 103 [ - + ][ + + ]: 358 : if (fCheckSum) {
# [ + - ]
# 104 : 349 : uint256 hashTmp;
# 105 : 349 : stream >> hashTmp;
# 106 [ # # ][ + + ]: 349 : if (hashTmp != verifier.GetHash()) {
# [ - + ]
# 107 : 1 : throw std::runtime_error{"Checksum mismatch, data corrupted"};
# 108 : 1 : }
# 109 : 349 : }
# 110 : 358 : }
# 111 : :
# 112 : : template <typename Data>
# 113 : : void DeserializeFileDB(const fs::path& path, Data& data, int version)
# 114 : 783 : {
# 115 : : // open input file, and associate with CAutoFile
# 116 : 783 : FILE* file = fsbridge::fopen(path, "rb");
# 117 : 783 : CAutoFile filein(file, SER_DISK, version);
# 118 [ + + ][ + + ]: 783 : if (filein.IsNull()) {
# 119 : 428 : throw DbNotFoundError{};
# 120 : 428 : }
# 121 : 355 : DeserializeDB(filein, data);
# 122 : 355 : }
# 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 : 964 : {
# 129 : 964 : }
# 130 : :
# 131 : : bool CBanDB::Write(const banmap_t& banSet)
# 132 : 670 : {
# 133 : 670 : std::vector<std::string> errors;
# 134 [ + - ]: 670 : if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
# 135 : 670 : return true;
# 136 : 670 : }
# 137 : :
# 138 [ # # ]: 0 : for (const auto& err : errors) {
# 139 : 0 : error("%s", err);
# 140 : 0 : }
# 141 : 0 : return false;
# 142 : 670 : }
# 143 : :
# 144 : : bool CBanDB::Read(banmap_t& banSet)
# 145 : 964 : {
# 146 [ - + ]: 964 : if (fs::exists(m_banlist_dat)) {
# 147 : 0 : 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 : 0 : }
# 149 : : // If the JSON banlist does not exist, then recreate it
# 150 [ + + ]: 964 : if (!fs::exists(m_banlist_json)) {
# 151 : 623 : return false;
# 152 : 623 : }
# 153 : :
# 154 : 341 : std::map<std::string, util::SettingsValue> settings;
# 155 : 341 : std::vector<std::string> errors;
# 156 : :
# 157 [ - + ]: 341 : if (!util::ReadSettings(m_banlist_json, settings, errors)) {
# 158 [ # # ]: 0 : for (const auto& err : errors) {
# 159 : 0 : LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
# 160 : 0 : }
# 161 : 0 : return false;
# 162 : 0 : }
# 163 : :
# 164 : 341 : try {
# 165 : 341 : BanMapFromJson(settings[JSON_KEY], banSet);
# 166 : 341 : } catch (const std::runtime_error& e) {
# 167 : 0 : LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
# 168 : 0 : return false;
# 169 : 0 : }
# 170 : :
# 171 : 341 : return true;
# 172 : 341 : }
# 173 : :
# 174 : : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
# 175 : 1146 : {
# 176 : 1146 : const auto pathAddr = args.GetDataDirNet() / "peers.dat";
# 177 : 1146 : return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
# 178 : 1146 : }
# 179 : :
# 180 : : void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
# 181 : 4 : {
# 182 : 4 : DeserializeDB(ssPeers, addr, false);
# 183 : 4 : }
# 184 : :
# 185 : : std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman)
# 186 : 764 : {
# 187 : 764 : auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
# 188 : 764 : addrman = std::make_unique<AddrMan>(asmap, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
# 189 : :
# 190 : 764 : int64_t nStart = GetTimeMillis();
# 191 : 764 : const auto path_addr{args.GetDataDirNet() / "peers.dat"};
# 192 : 764 : try {
# 193 : 764 : DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
# 194 : 764 : LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
# 195 : 764 : } catch (const DbNotFoundError&) {
# 196 : : // Addrman can be in an inconsistent state after failure, reset it
# 197 : 423 : addrman = std::make_unique<AddrMan>(asmap, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
# 198 : 423 : LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
# 199 : 423 : DumpPeerAddresses(args, *addrman);
# 200 : 423 : } catch (const InvalidAddrManVersionError&) {
# 201 [ - + ]: 1 : if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
# 202 : 0 : addrman = nullptr;
# 203 : 0 : return strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."));
# 204 : 0 : }
# 205 : : // Addrman can be in an inconsistent state after failure, reset it
# 206 : 1 : addrman = std::make_unique<AddrMan>(asmap, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
# 207 : 1 : 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 : 1 : DumpPeerAddresses(args, *addrman);
# 209 : 7 : } catch (const std::exception& e) {
# 210 : 7 : addrman = nullptr;
# 211 : 7 : 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 : 7 : e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)));
# 213 : 7 : }
# 214 : 757 : return std::nullopt;
# 215 : 764 : }
# 216 : :
# 217 : : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
# 218 : 19 : {
# 219 : 19 : LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
# 220 : 19 : SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
# 221 : 19 : }
# 222 : :
# 223 : : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
# 224 : 19 : {
# 225 : 19 : std::vector<CAddress> anchors;
# 226 : 19 : try {
# 227 : 19 : DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
# 228 : 19 : LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
# 229 : 19 : } catch (const std::exception&) {
# 230 : 5 : anchors.clear();
# 231 : 5 : }
# 232 : :
# 233 : 19 : fs::remove(anchors_db_path);
# 234 : 19 : return anchors;
# 235 : 19 : }
|