LCOV - code coverage report
Current view: top level - src - addrdb.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 108 133 81.2 %
Date: 2026-06-25 07:23:43 Functions: 22 23 95.7 %

          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        6461 : bool SerializeDB(Stream& stream, const Data& data)
      35             : {
      36             :     // Write and commit header, data
      37             :     try {
      38        6461 :         HashedSourceWriter hashwriter{stream};
      39        6461 :         hashwriter << Params().MessageStart() << data;
      40        6461 :         stream << hashwriter.GetHash();
      41        6461 :     } catch (const std::exception& e) {
      42           0 :         return error("%s: Serialize or I/O error - %s", __func__, e.what());
      43           0 :     }
      44             : 
      45        6461 :     return true;
      46        6461 : }
      47             : 
      48             : template <typename Data>
      49        6461 : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
      50             : {
      51             :     // Generate random temporary filename
      52        6461 :     const uint16_t randv{GetRand<uint16_t>()};
      53        6461 :     std::string tmpfn = strprintf("%s.%04x", prefix, randv);
      54             : 
      55             :     // open temp output file, and associate with CAutoFile
      56        6461 :     fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
      57        6461 :     FILE *file = fsbridge::fopen(pathTmp, "wb");
      58        6461 :     CAutoFile fileout(file, SER_DISK, version);
      59        6461 :     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        6461 :     if (!SerializeDB(fileout, data)) {
      67           0 :         fileout.fclose();
      68           0 :         remove(pathTmp);
      69           0 :         return false;
      70             :     }
      71        6461 :     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        6461 :     fileout.fclose();
      77             : 
      78             :     // replace existing file, if any, with new file
      79        6461 :     if (!RenameOver(pathTmp, path)) {
      80           0 :         remove(pathTmp);
      81           0 :         return error("%s: Rename-into-place failed", __func__);
      82             :     }
      83             : 
      84        6461 :     return true;
      85        6461 : }
      86             : 
      87             : template <typename Stream, typename Data>
      88        1821 : void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
      89             : {
      90        1821 :     CHashVerifier<Stream> verifier(&stream);
      91             :     // de-serialize file header (network specific magic number) and ..
      92             :     unsigned char pchMsgTmp[4];
      93        1821 :     verifier >> pchMsgTmp;
      94             :     // ... verify the network matches ours
      95        1821 :     if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
      96           2 :         throw std::runtime_error{"Invalid network magic number"};
      97             :     }
      98             : 
      99             :     // de-serialize data
     100        1819 :     verifier >> data;
     101             : 
     102             :     // verify checksum
     103        1819 :     if (fCheckSum) {
     104        1799 :         uint256 hashTmp;
     105        1799 :         stream >> hashTmp;
     106        1799 :         if (hashTmp != verifier.GetHash()) {
     107           2 :             throw std::runtime_error{"Checksum mismatch, data corrupted"};
     108             :         }
     109        1797 :     }
     110        1817 : }
     111             : 
     112             : template <typename Data>
     113        3808 : void DeserializeFileDB(const fs::path& path, Data& data, int version)
     114             : {
     115             :     // open input file, and associate with CAutoFile
     116        3808 :     FILE* file = fsbridge::fopen(path, "rb");
     117        3808 :     CAutoFile filein(file, SER_DISK, version);
     118        3808 :     if (filein.IsNull()) {
     119        1989 :         throw DbNotFoundError{};
     120             :     }
     121        1819 :     DeserializeDB(filein, data);
     122        3808 : }
     123             : } // namespace
     124             : 
     125        6232 : CBanDB::CBanDB(fs::path ban_list_path)
     126        3116 :     : m_banlist_dat(ban_list_path + ".dat"),
     127        3116 :       m_banlist_json(ban_list_path + ".json")
     128        3116 : {
     129        6232 : }
     130             : 
     131        1800 : bool CBanDB::Write(const banmap_t& banSet)
     132             : {
     133        1800 :     std::vector<std::string> errors;
     134        1800 :     if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
     135        1800 :         return true;
     136             :     }
     137             : 
     138           0 :     for (const auto& err : errors) {
     139           0 :         error("%s", err);
     140             :     }
     141           0 :     return false;
     142        1800 : }
     143             : 
     144        3116 : bool CBanDB::Read(banmap_t& banSet)
     145             : {
     146        3116 :     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        3116 :     if (!fs::exists(m_banlist_json)) {
     151        1715 :         return false;
     152             :     }
     153             : 
     154        1401 :     std::map<std::string, util::SettingsValue> settings;
     155        1401 :     std::vector<std::string> errors;
     156             : 
     157        1401 :     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        1401 :         BanMapFromJson(settings[JSON_KEY], banSet);
     166        1401 :     } 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        1401 :     return true;
     172        3116 : }
     173             : 
     174        5614 : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
     175             : {
     176        5614 :     const auto pathAddr = gArgs.GetDataDirNet() / "peers.dat";
     177        5614 :     return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
     178        5614 : }
     179             : 
     180           2 : void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
     181             : {
     182           2 :     DeserializeDB(ssPeers, addr, false);
     183           2 : }
     184             : 
     185        2961 : util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)
     186             : {
     187        2961 :     auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
     188        2961 :     auto addrman{std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman)};
     189             : 
     190        2961 :     const auto start{SteadyClock::now()};
     191        2961 :     const auto path_addr{gArgs.GetDataDirNet() / "peers.dat"};
     192             :     try {
     193        2961 :         DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
     194        1406 :         LogPrintf("Loaded %i addresses from peers.dat  %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
     195        2961 :     } catch (const DbNotFoundError&) {
     196             :         // Addrman can be in an inconsistent state after failure, reset it
     197        1535 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     198        1535 :         LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
     199        1535 :         DumpPeerAddresses(args, *addrman);
     200        1535 :     } 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           2 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     207           2 :         LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what());
     208           2 :         DumpPeerAddresses(args, *addrman);
     209        1537 :     } catch (const InvalidAddrManVersionError&) {
     210           2 :         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           2 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     215           2 :         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           2 :         DumpPeerAddresses(args, *addrman);
     217           6 :     } catch (const std::exception& e) {
     218          32 :         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          16 :                                      e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
     220          18 :     }
     221        2945 :     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        4516 : }
     223             : 
     224         847 : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
     225             : {
     226         847 :     LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
     227         847 :     SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
     228         847 : }
     229             : 
     230         847 : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
     231             : {
     232         847 :     std::vector<CAddress> anchors;
     233             :     try {
     234         847 :         DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
     235         391 :         LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
     236         847 :     } catch (const std::exception&) {
     237         456 :         anchors.clear();
     238         456 :     }
     239             : 
     240         847 :     fs::remove(anchors_db_path);
     241         847 :     return anchors;
     242        1303 : }

Generated by: LCOV version 1.16