Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <bitcoin-build-config.h> // IWYU pragma: keep8#include <addrdb.h>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/timer.h>18#include <netbase.h>19#include <netgroup.h>20#include <random.h>21#include <streams.h>22#include <tinyformat.h>23#include <univalue.h>24#include <util/fs.h>25#include <util/fs_helpers.h>26#include <util/log.h>27#include <util/syserror.h>28#include <util/translation.h>30namespace {32class DbNotFoundError : public std::exception33{34using std::exception::exception;35};37template <typename Stream, typename Data>38bool SerializeDB(Stream& stream, const Data& data)39{40// Write and commit header, data41try {42HashedSourceWriter hashwriter{stream};43hashwriter << Params().MessageStart() << data;44stream << hashwriter.GetHash();45} catch (const std::exception& e) {46LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());47return false;48}50return true;51}53template <typename Data>54bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)55{56// Generate random temporary filename57const uint16_t randv{FastRandomContext().rand<uint16_t>()};58std::string tmpfn = strprintf("%s.%04x", prefix, randv);60// open temp output file61fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);62FILE *file = fsbridge::fopen(pathTmp, "wb");63AutoFile fileout{file};64if (fileout.IsNull()) {65remove(pathTmp);66LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));67return false;68}70// Serialize71if (!SerializeDB(fileout, data)) {72(void)fileout.fclose();73remove(pathTmp);74return false;75}76if (!fileout.Commit()) {77(void)fileout.fclose();78remove(pathTmp);79LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));80return false;81}82if (fileout.fclose() != 0) {83const int errno_save{errno};84remove(pathTmp);85LogError("Failed to close file %s after commit: %s", fs::PathToString(pathTmp), SysErrorString(errno_save));86return false;87}89// replace existing file, if any, with new file90if (!RenameOver(pathTmp, path)) {91remove(pathTmp);92LogError("%s: Rename-into-place failed\n", __func__);93return false;94}96return true;97}99template <typename Stream, typename Data>100void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)101{102HashVerifier verifier{stream};103// de-serialize file header (network specific magic number) and ..104MessageStartChars pchMsgTmp;105verifier >> pchMsgTmp;106// ... verify the network matches ours107if (pchMsgTmp != Params().MessageStart()) {108throw std::runtime_error{"Invalid network magic number"};109}111// de-serialize data112verifier >> data;114// verify checksum115if (fCheckSum) {116uint256 hashTmp;117stream >> hashTmp;118if (hashTmp != verifier.GetHash()) {119throw std::runtime_error{"Checksum mismatch, data corrupted"};120}121}122}124template <typename Data>125void DeserializeFileDB(const fs::path& path, Data&& data)126{127FILE* file = fsbridge::fopen(path, "rb");128AutoFile filein{file};129if (filein.IsNull()) {130throw DbNotFoundError{};131}132DeserializeDB(filein, data);133}134} // namespace136CBanDB::CBanDB(fs::path ban_list_path)137: m_banlist_dat(ban_list_path + ".dat"),138m_banlist_json(ban_list_path + ".json")139{140}142bool CBanDB::Write(const banmap_t& banSet)143{144std::vector<std::string> errors;145if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {146return true;147}149for (const auto& err : errors) {150LogError("%s\n", err);151}152return false;153}155bool CBanDB::Read(banmap_t& banSet)156{157if (fs::exists(m_banlist_dat)) {158LogWarning("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 it161if (!fs::exists(m_banlist_json)) {162return false;163}165std::map<std::string, common::SettingsValue> settings;166std::vector<std::string> errors;168if (!common::ReadSettings(m_banlist_json, settings, errors)) {169for (const auto& err : errors) {170LogWarning("Cannot load banlist %s: %s", fs::PathToString(m_banlist_json), err);171}172return false;173}175try {176BanMapFromJson(settings[JSON_KEY], banSet);177} catch (const std::runtime_error& e) {178LogWarning("Cannot parse banlist %s: %s", fs::PathToString(m_banlist_json), e.what());179return false;180}182return true;183}185bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)186{187const auto pathAddr = args.GetDataDirNet() / "peers.dat";188return SerializeFileDB("peers", pathAddr, addr);189}191void ReadFromStream(AddrMan& addr, DataStream& ssPeers)192{193DeserializeDB(ssPeers, addr, false);194}196util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)197{198auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);199bool deterministic = HasTestOption(args, "addrman"); // use a deterministic addrman only for tests201auto addrman{std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman)};203const auto start{SteadyClock::now()};204const auto path_addr{args.GetDataDirNet() / "peers.dat"};205try {206DeserializeFileDB(path_addr, *addrman);207LogInfo("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 it210addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);211LogInfo("Creating peers.dat because the file was not found (%s)", fs::quoted(fs::PathToString(path_addr)));212DumpPeerAddresses(args, *addrman);213} catch (const InvalidAddrManVersionError&) {214if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {215return 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 it218addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);219LogWarning("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)));220DumpPeerAddresses(args, *addrman);221} catch (const std::exception& e) {222return 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."),223e.what(), CLIENT_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};224}225return addrman;226}228void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)229{230LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));231SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));232}234std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)235{236std::vector<CAddress> anchors;237try {238DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));239LogInfo("Loaded %i addresses from %s", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));240} catch (const std::exception&) {241anchors.clear();242}244fs::remove(anchors_db_path);245return anchors;246}