LCOV - code coverage report
Current view: top level - src - addrdb.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 57 129 44.2 %
Date: 2023-11-06 23:13:05 Functions: 13 20 65.0 %
Branches: 75 412 18.2 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :            : // Copyright (c) 2009-2022 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 <common/args.h>
      12                 :            : #include <common/settings.h>
      13                 :            : #include <cstdint>
      14                 :            : #include <hash.h>
      15                 :            : #include <logging.h>
      16                 :            : #include <logging/timer.h>
      17                 :            : #include <netbase.h>
      18                 :            : #include <netgroup.h>
      19                 :            : #include <random.h>
      20                 :            : #include <streams.h>
      21                 :            : #include <tinyformat.h>
      22                 :            : #include <univalue.h>
      23                 :            : #include <util/fs.h>
      24                 :            : #include <util/fs_helpers.h>
      25                 :            : #include <util/translation.h>
      26                 :            : 
      27                 :            : namespace {
      28                 :            : 
      29                 :          0 : class DbNotFoundError : public std::exception
      30                 :            : {
      31                 :            :     using std::exception::exception;
      32                 :            : };
      33                 :            : 
      34                 :            : template <typename Stream, typename Data>
      35                 :          2 : bool SerializeDB(Stream& stream, const Data& data)
      36                 :            : {
      37                 :            :     // Write and commit header, data
      38                 :            :     try {
      39 [ +  - ][ +  - ]:          2 :         HashedSourceWriter hashwriter{stream};
      40 [ +  - ][ +  - ]:          2 :         hashwriter << Params().MessageStart() << data;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
      41 [ +  - ][ +  - ]:          2 :         stream << hashwriter.GetHash();
         [ +  - ][ +  - ]
      42 [ #  # ][ #  # ]:          2 :     } catch (const std::exception& e) {
      43 [ #  # ][ #  # ]:          0 :         return error("%s: Serialize or I/O error - %s", __func__, e.what());
      44 [ #  # ][ #  # ]:          0 :     }
      45                 :            : 
      46                 :          2 :     return true;
      47                 :          2 : }
      48                 :            : 
      49                 :            : template <typename Data>
      50                 :          2 : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
      51                 :            : {
      52                 :            :     // Generate random temporary filename
      53                 :          2 :     const uint16_t randv{GetRand<uint16_t>()};
      54                 :          2 :     std::string tmpfn = strprintf("%s.%04x", prefix, randv);
      55                 :            : 
      56                 :            :     // open temp output file
      57 [ +  - ][ +  - ]:          2 :     fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
      58 [ +  - ][ +  - ]:          2 :     FILE *file = fsbridge::fopen(pathTmp, "wb");
      59 [ +  - ][ +  - ]:          2 :     AutoFile fileout{file};
      60 [ +  - ][ -  + ]:          2 :     if (fileout.IsNull()) {
                 [ +  - ]
      61 [ #  # ][ #  # ]:          0 :         fileout.fclose();
      62 [ #  # ][ #  # ]:          0 :         remove(pathTmp);
      63 [ #  # ][ #  # ]:          0 :         return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp));
         [ #  # ][ #  # ]
      64                 :            :     }
      65                 :            : 
      66                 :            :     // Serialize
      67 [ +  - ][ -  + ]:          2 :     if (!SerializeDB(fileout, data)) {
         [ +  - ][ -  + ]
      68 [ #  # ][ #  # ]:          0 :         fileout.fclose();
      69 [ #  # ][ #  # ]:          0 :         remove(pathTmp);
      70                 :          0 :         return false;
      71                 :            :     }
      72 [ +  - ][ +  - ]:          2 :     if (!FileCommit(fileout.Get())) {
         [ -  + ][ +  - ]
                 [ -  + ]
      73 [ #  # ][ #  # ]:          0 :         fileout.fclose();
      74 [ #  # ][ #  # ]:          0 :         remove(pathTmp);
      75 [ #  # ][ #  # ]:          0 :         return error("%s: Failed to flush file %s", __func__, fs::PathToString(pathTmp));
         [ #  # ][ #  # ]
      76                 :            :     }
      77 [ +  - ][ +  - ]:          2 :     fileout.fclose();
      78                 :            : 
      79                 :            :     // replace existing file, if any, with new file
      80 [ +  - ][ +  - ]:          2 :     if (!RenameOver(pathTmp, path)) {
         [ -  + ][ -  + ]
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
      81 [ #  # ][ #  # ]:          0 :         remove(pathTmp);
      82 [ #  # ][ #  # ]:          0 :         return error("%s: Rename-into-place failed", __func__);
      83                 :            :     }
      84                 :            : 
      85                 :          2 :     return true;
      86                 :          2 : }
      87                 :            : 
      88                 :            : template <typename Stream, typename Data>
      89                 :          0 : void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)
      90                 :            : {
      91                 :          0 :     HashVerifier verifier{stream};
      92                 :            :     // de-serialize file header (network specific magic number) and ..
      93                 :            :     MessageStartChars pchMsgTmp;
      94                 :          0 :     verifier >> pchMsgTmp;
      95                 :            :     // ... verify the network matches ours
      96 [ #  # ][ #  # ]:          0 :     if (pchMsgTmp != Params().MessageStart()) {
                 [ #  # ]
      97 [ #  # ][ #  # ]:          0 :         throw std::runtime_error{"Invalid network magic number"};
                 [ #  # ]
      98                 :            :     }
      99                 :            : 
     100                 :            :     // de-serialize data
     101                 :          0 :     verifier >> data;
     102                 :            : 
     103                 :            :     // verify checksum
     104 [ #  # ][ #  # ]:          0 :     if (fCheckSum) {
                 [ #  # ]
     105                 :          0 :         uint256 hashTmp;
     106                 :          0 :         stream >> hashTmp;
     107 [ #  # ][ #  # ]:          0 :         if (hashTmp != verifier.GetHash()) {
                 [ #  # ]
     108 [ #  # ][ #  # ]:          0 :             throw std::runtime_error{"Checksum mismatch, data corrupted"};
                 [ #  # ]
     109                 :            :         }
     110                 :          0 :     }
     111                 :          0 : }
     112                 :            : 
     113                 :            : template <typename Data>
     114                 :          1 : void DeserializeFileDB(const fs::path& path, Data&& data)
     115                 :            : {
     116                 :          1 :     FILE* file = fsbridge::fopen(path, "rb");
     117 [ #  # ][ +  - ]:          2 :     AutoFile filein{file};
     118 [ #  # ][ +  - ]:          1 :     if (filein.IsNull()) {
     119 [ #  # ][ -  + ]:          1 :         throw DbNotFoundError{};
     120                 :            :     }
     121 [ #  # ][ #  # ]:          0 :     DeserializeDB(filein, data);
     122                 :          1 : }
     123                 :            : } // namespace
     124                 :            : 
     125                 :          1 : CBanDB::CBanDB(fs::path ban_list_path)
     126         [ +  - ]:          1 :     : m_banlist_dat(ban_list_path + ".dat"),
     127 [ +  - ][ -  + ]:          1 :       m_banlist_json(ban_list_path + ".json")
     128                 :            : {
     129                 :          1 : }
     130                 :            : 
     131                 :          1 : bool CBanDB::Write(const banmap_t& banSet)
     132                 :            : {
     133                 :          1 :     std::vector<std::string> errors;
     134 [ +  - ][ +  - ]:          1 :     if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
         [ +  - ][ -  + ]
         [ +  - ][ #  # ]
     135                 :          1 :         return true;
     136                 :            :     }
     137                 :            : 
     138         [ #  # ]:          0 :     for (const auto& err : errors) {
     139         [ #  # ]:          0 :         error("%s", err);
     140                 :            :     }
     141                 :          0 :     return false;
     142                 :          1 : }
     143                 :            : 
     144                 :          1 : bool CBanDB::Read(banmap_t& banSet)
     145                 :            : {
     146         [ +  - ]:          1 :     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         [ -  + ]:          1 :     if (!fs::exists(m_banlist_json)) {
     151                 :          1 :         return false;
     152                 :            :     }
     153                 :            : 
     154                 :          0 :     std::map<std::string, common::SettingsValue> settings;
     155                 :          0 :     std::vector<std::string> errors;
     156                 :            : 
     157 [ #  # ][ #  # ]:          0 :     if (!common::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 [ #  # ][ #  # ]:          0 :         BanMapFromJson(settings[JSON_KEY], banSet);
                 [ #  # ]
     166         [ #  # ]:          0 :     } 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                 :          0 :     return true;
     172                 :          1 : }
     173                 :            : 
     174                 :          1 : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
     175                 :            : {
     176         [ +  - ]:          1 :     const auto pathAddr = args.GetDataDirNet() / "peers.dat";
     177 [ +  - ][ -  + ]:          1 :     return SerializeFileDB("peers", pathAddr, addr);
     178                 :          1 : }
     179                 :            : 
     180                 :          0 : void ReadFromStream(AddrMan& addr, DataStream& ssPeers)
     181                 :            : {
     182                 :          0 :     DeserializeDB(ssPeers, addr, false);
     183                 :          0 : }
     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{args.GetDataDirNet() / "peers.dat"};
                 [ #  # ]
     192                 :            :     try {
     193         [ #  # ]:          0 :         DeserializeFileDB(path_addr, *addrman);
     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 InvalidAddrManVersionError&) {
     201 [ #  # ][ #  # ]:          0 :         if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
         [ #  # ][ #  # ]
                 [ #  # ]
     202 [ #  # ][ #  # ]:          0 :             return util::Error{strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."))};
                 [ #  # ]
     203                 :            :         }
     204                 :            :         // Addrman can be in an inconsistent state after failure, reset it
     205         [ #  # ]:          0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     206 [ #  # ][ #  # ]:          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)));
         [ #  # ][ #  # ]
                 [ #  # ]
     207         [ #  # ]:          0 :         DumpPeerAddresses(args, *addrman);
     208 [ #  # ][ #  # ]:          0 :     } catch (const std::exception& e) {
     209 [ #  # ][ #  # ]:          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."),
                 [ #  # ]
     210 [ #  # ][ #  # ]:          0 :                                      e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
     211 [ #  # ][ #  # ]:          0 :     }
     212         [ #  # ]:          0 :     return {std::move(addrman)}; // std::move should be unnecessary but is temporarily needed to work around clang bug
     213                 :            :                                  // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092)
     214                 :          0 : }
     215                 :            : 
     216                 :          1 : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
     217                 :            : {
     218 [ +  - ][ +  - ]:          1 :     LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
                 [ +  - ]
     219 [ -  + ][ +  - ]:          1 :     SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));
                 [ +  - ]
     220                 :          1 : }
     221                 :            : 
     222                 :          1 : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
     223                 :            : {
     224                 :          1 :     std::vector<CAddress> anchors;
     225                 :            :     try {
     226 [ +  - ][ -  + ]:          1 :         DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));
     227 [ #  # ][ #  # ]:          0 :         LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     228         [ +  - ]:          1 :     } catch (const std::exception&) {
     229                 :          1 :         anchors.clear();
     230         [ +  - ]:          1 :     }
     231                 :            : 
     232         [ +  - ]:          1 :     fs::remove(anchors_db_path);
     233                 :          1 :     return anchors;
     234         [ +  - ]:          2 : }

Generated by: LCOV version 1.14