LCOV - code coverage report
Current view: top level - src - addrdb.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 31 133 23.3 %
Date: 2026-06-25 07:23:51 Functions: 6 23 26.1 %

          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 <fs.h>
      12             : #include <hash.h>
      13             : #include <logging/timer.h>
      14             : #include <netbase.h>
      15             : #include <netgroup.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             : #include <cstdint>
      25             : 
      26             : namespace {
      27             : 
      28             : class DbNotFoundError : public std::exception
      29             : {
      30             :     using std::exception::exception;
      31             : };
      32             : 
      33             : template <typename Stream, typename Data>
      34           0 : bool SerializeDB(Stream& stream, const Data& data)
      35             : {
      36             :     // Write and commit header, data
      37             :     try {
      38           0 :         HashedSourceWriter hashwriter{stream};
      39           0 :         hashwriter << Params().MessageStart() << data;
      40           0 :         stream << hashwriter.GetHash();
      41           0 :     } catch (const std::exception& e) {
      42           0 :         return error("%s: Serialize or I/O error - %s", __func__, e.what());
      43           0 :     }
      44             : 
      45           0 :     return true;
      46           0 : }
      47             : 
      48             : template <typename Data>
      49           0 : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
      50             : {
      51             :     // Generate random temporary filename
      52           0 :     const uint16_t randv{GetRand<uint16_t>()};
      53           0 :     std::string tmpfn = strprintf("%s.%04x", prefix, randv);
      54             : 
      55             :     // open temp output file, and associate with CAutoFile
      56           0 :     fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
      57           0 :     FILE *file = fsbridge::fopen(pathTmp, "wb");
      58           0 :     CAutoFile fileout(file, SER_DISK, version);
      59           0 :     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             :     }
      64             : 
      65             :     // Serialize
      66           0 :     if (!SerializeDB(fileout, data)) {
      67           0 :         fileout.fclose();
      68           0 :         remove(pathTmp);
      69           0 :         return false;
      70             :     }
      71           0 :     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             :     }
      76           0 :     fileout.fclose();
      77             : 
      78             :     // replace existing file, if any, with new file
      79           0 :     if (!RenameOver(pathTmp, path)) {
      80           0 :         remove(pathTmp);
      81           0 :         return error("%s: Rename-into-place failed", __func__);
      82             :     }
      83             : 
      84           0 :     return true;
      85           0 : }
      86             : 
      87             : template <typename Stream, typename Data>
      88           2 : void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
      89             : {
      90           2 :     CHashVerifier<Stream> verifier(&stream);
      91             :     // de-serialize file header (network specific magic number) and ..
      92             :     unsigned char pchMsgTmp[4];
      93           2 :     verifier >> pchMsgTmp;
      94             :     // ... verify the network matches ours
      95           2 :     if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
      96           0 :         throw std::runtime_error{"Invalid network magic number"};
      97             :     }
      98             : 
      99             :     // de-serialize data
     100           2 :     verifier >> data;
     101             : 
     102             :     // verify checksum
     103           2 :     if (fCheckSum) {
     104           0 :         uint256 hashTmp;
     105           0 :         stream >> hashTmp;
     106           0 :         if (hashTmp != verifier.GetHash()) {
     107           0 :             throw std::runtime_error{"Checksum mismatch, data corrupted"};
     108             :         }
     109           0 :     }
     110           2 : }
     111             : 
     112             : template <typename Data>
     113           0 : void DeserializeFileDB(const fs::path& path, Data& data, int version)
     114             : {
     115             :     // open input file, and associate with CAutoFile
     116           0 :     FILE* file = fsbridge::fopen(path, "rb");
     117           0 :     CAutoFile filein(file, SER_DISK, version);
     118           0 :     if (filein.IsNull()) {
     119           0 :         throw DbNotFoundError{};
     120             :     }
     121           0 :     DeserializeDB(filein, data);
     122           0 : }
     123             : } // namespace
     124             : 
     125         362 : CBanDB::CBanDB(fs::path ban_list_path)
     126         181 :     : m_banlist_dat(ban_list_path + ".dat"),
     127         181 :       m_banlist_json(ban_list_path + ".json")
     128         181 : {
     129         362 : }
     130             : 
     131         195 : bool CBanDB::Write(const banmap_t& banSet)
     132             : {
     133         195 :     std::vector<std::string> errors;
     134         195 :     if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
     135         195 :         return true;
     136             :     }
     137             : 
     138           0 :     for (const auto& err : errors) {
     139           0 :         error("%s", err);
     140             :     }
     141           0 :     return false;
     142         195 : }
     143             : 
     144         181 : bool CBanDB::Read(banmap_t& banSet)
     145             : {
     146         181 :     if (fs::exists(m_banlist_dat)) {
     147           0 :         LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 19.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         181 :     if (!fs::exists(m_banlist_json)) {
     151         178 :         return false;
     152             :     }
     153             : 
     154           3 :     std::map<std::string, util::SettingsValue> settings;
     155           3 :     std::vector<std::string> errors;
     156             : 
     157           3 :     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             :         }
     161           0 :         return false;
     162             :     }
     163             : 
     164             :     try {
     165           3 :         BanMapFromJson(settings[JSON_KEY], banSet);
     166           3 :     } 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           3 :     return true;
     172         181 : }
     173             : 
     174           0 : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
     175             : {
     176           0 :     const auto pathAddr = gArgs.GetDataDirNet() / "peers.dat";
     177           0 :     return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
     178           0 : }
     179             : 
     180           2 : void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
     181             : {
     182           2 :     DeserializeDB(ssPeers, addr, false);
     183           2 : }
     184             : 
     185           0 : util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)
     186             : {
     187           0 :     auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
     188           0 :     auto addrman{std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman)};
     189             : 
     190           0 :     const auto start{SteadyClock::now()};
     191           0 :     const auto path_addr{gArgs.GetDataDirNet() / "peers.dat"};
     192             :     try {
     193           0 :         DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
     194           0 :         LogPrintf("Loaded %i addresses from peers.dat  %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
     195           0 :     } catch (const DbNotFoundError&) {
     196             :         // Addrman can be in an inconsistent state after failure, reset it
     197           0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     198           0 :         LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
     199           0 :         DumpPeerAddresses(args, *addrman);
     200           0 :     } catch (const DbInconsistentError& e) {
     201             :         // Addrman has shown a tendency to corrupt itself even with graceful shutdowns on known-good
     202             :         // hardware. As the user would have to delete and recreate a new database regardless to cope
     203             :         // with frequent corruption, we are restoring old behaviour that does the same, silently.
     204             :         //
     205             :         // TODO: Evaluate cause and fix, revert this change at some point.
     206           0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     207           0 :         LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what());
     208           0 :         DumpPeerAddresses(args, *addrman);
     209           0 :     } catch (const InvalidAddrManVersionError&) {
     210           0 :         if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
     211           0 :             return util::Error{strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."))};
     212             :         }
     213             :         // Addrman can be in an inconsistent state after failure, reset it
     214           0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     215           0 :         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)));
     216           0 :         DumpPeerAddresses(args, *addrman);
     217           0 :     } catch (const std::exception& e) {
     218           0 :         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."),
     219           0 :                                      e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
     220           0 :     }
     221           0 :     return addrman; // std::move should be unneccessary but is temporarily needed to work around clang bug (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092)
     222           0 : }
     223             : 
     224           0 : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
     225             : {
     226           0 :     LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
     227           0 :     SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
     228           0 : }
     229             : 
     230           0 : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
     231             : {
     232           0 :     std::vector<CAddress> anchors;
     233             :     try {
     234           0 :         DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
     235           0 :         LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
     236           0 :     } catch (const std::exception&) {
     237           0 :         anchors.clear();
     238           0 :     }
     239             : 
     240           0 :     fs::remove(anchors_db_path);
     241           0 :     return anchors;
     242           0 : }

Generated by: LCOV version 1.16