Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/addrdb.cpp
Line
Count
Source
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 <bitcoin-build-config.h> // IWYU pragma: keep
7
8
#include <addrdb.h>
9
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.h>
18
#include <logging/timer.h>
19
#include <netbase.h>
20
#include <netgroup.h>
21
#include <random.h>
22
#include <streams.h>
23
#include <tinyformat.h>
24
#include <univalue.h>
25
#include <util/fs.h>
26
#include <util/fs_helpers.h>
27
#include <util/translation.h>
28
29
namespace {
30
31
class DbNotFoundError : public std::exception
32
{
33
    using std::exception::exception;
34
};
35
36
template <typename Stream, typename Data>
37
bool SerializeDB(Stream& stream, const Data& data)
38
33.2k
{
39
    // Write and commit header, data
40
33.2k
    try {
41
33.2k
        HashedSourceWriter hashwriter{stream};
42
33.2k
        hashwriter << Params().MessageStart() << data;
43
33.2k
        stream << hashwriter.GetHash();
44
33.2k
    } catch (const std::exception& e) {
45
0
        LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
46
0
        return false;
47
0
    }
48
49
33.2k
    return true;
50
33.2k
}
addrdb.cpp:bool (anonymous namespace)::SerializeDB<AutoFile, AddrMan>(AutoFile&, AddrMan const&)
Line
Count
Source
38
22.1k
{
39
    // Write and commit header, data
40
22.1k
    try {
41
22.1k
        HashedSourceWriter hashwriter{stream};
42
22.1k
        hashwriter << Params().MessageStart() << data;
43
22.1k
        stream << hashwriter.GetHash();
44
22.1k
    } catch (const std::exception& e) {
45
0
        LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
46
0
        return false;
47
0
    }
48
49
22.1k
    return true;
50
22.1k
}
addrdb.cpp:bool (anonymous namespace)::SerializeDB<AutoFile, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > const> >(AutoFile&, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > const> const&)
Line
Count
Source
38
11.0k
{
39
    // Write and commit header, data
40
11.0k
    try {
41
11.0k
        HashedSourceWriter hashwriter{stream};
42
11.0k
        hashwriter << Params().MessageStart() << data;
43
11.0k
        stream << hashwriter.GetHash();
44
11.0k
    } catch (const std::exception& e) {
45
0
        LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
46
0
        return false;
47
0
    }
48
49
11.0k
    return true;
50
11.0k
}
51
52
template <typename Data>
53
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
54
33.2k
{
55
    // Generate random temporary filename
56
33.2k
    const uint16_t randv{FastRandomContext().rand<uint16_t>()};
57
33.2k
    std::string tmpfn = strprintf("%s.%04x", prefix, randv);
58
59
    // open temp output file
60
33.2k
    fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
61
33.2k
    FILE *file = fsbridge::fopen(pathTmp, "wb");
62
33.2k
    AutoFile fileout{file};
63
33.2k
    if (fileout.IsNull()) {
  Branch (63:9): [True: 0, False: 22.1k]
  Branch (63:9): [True: 0, False: 11.0k]
64
0
        fileout.fclose();
65
0
        remove(pathTmp);
66
0
        LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
67
0
        return false;
68
0
    }
69
70
    // Serialize
71
33.2k
    if (!SerializeDB(fileout, data)) {
  Branch (71:9): [True: 0, False: 22.1k]
  Branch (71:9): [True: 0, False: 11.0k]
72
0
        fileout.fclose();
73
0
        remove(pathTmp);
74
0
        return false;
75
0
    }
76
33.2k
    if (!fileout.Commit()) {
  Branch (76:9): [True: 0, False: 22.1k]
  Branch (76:9): [True: 0, False: 11.0k]
77
0
        fileout.fclose();
78
0
        remove(pathTmp);
79
0
        LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
80
0
        return false;
81
0
    }
82
33.2k
    fileout.fclose();
83
84
    // replace existing file, if any, with new file
85
33.2k
    if (!RenameOver(pathTmp, path)) {
  Branch (85:9): [True: 0, False: 22.1k]
  Branch (85:9): [True: 0, False: 11.0k]
86
0
        remove(pathTmp);
87
0
        LogError("%s: Rename-into-place failed\n", __func__);
88
0
        return false;
89
0
    }
90
91
33.2k
    return true;
92
33.2k
}
addrdb.cpp:bool (anonymous namespace)::SerializeFileDB<AddrMan>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, fs::path const&, AddrMan const&)
Line
Count
Source
54
22.1k
{
55
    // Generate random temporary filename
56
22.1k
    const uint16_t randv{FastRandomContext().rand<uint16_t>()};
57
22.1k
    std::string tmpfn = strprintf("%s.%04x", prefix, randv);
58
59
    // open temp output file
60
22.1k
    fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
61
22.1k
    FILE *file = fsbridge::fopen(pathTmp, "wb");
62
22.1k
    AutoFile fileout{file};
63
22.1k
    if (fileout.IsNull()) {
  Branch (63:9): [True: 0, False: 22.1k]
64
0
        fileout.fclose();
65
0
        remove(pathTmp);
66
0
        LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
67
0
        return false;
68
0
    }
69
70
    // Serialize
71
22.1k
    if (!SerializeDB(fileout, data)) {
  Branch (71:9): [True: 0, False: 22.1k]
72
0
        fileout.fclose();
73
0
        remove(pathTmp);
74
0
        return false;
75
0
    }
76
22.1k
    if (!fileout.Commit()) {
  Branch (76:9): [True: 0, False: 22.1k]
77
0
        fileout.fclose();
78
0
        remove(pathTmp);
79
0
        LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
80
0
        return false;
81
0
    }
82
22.1k
    fileout.fclose();
83
84
    // replace existing file, if any, with new file
85
22.1k
    if (!RenameOver(pathTmp, path)) {
  Branch (85:9): [True: 0, False: 22.1k]
86
0
        remove(pathTmp);
87
0
        LogError("%s: Rename-into-place failed\n", __func__);
88
0
        return false;
89
0
    }
90
91
22.1k
    return true;
92
22.1k
}
addrdb.cpp:bool (anonymous namespace)::SerializeFileDB<ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > const> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, fs::path const&, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > const> const&)
Line
Count
Source
54
11.0k
{
55
    // Generate random temporary filename
56
11.0k
    const uint16_t randv{FastRandomContext().rand<uint16_t>()};
57
11.0k
    std::string tmpfn = strprintf("%s.%04x", prefix, randv);
58
59
    // open temp output file
60
11.0k
    fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
61
11.0k
    FILE *file = fsbridge::fopen(pathTmp, "wb");
62
11.0k
    AutoFile fileout{file};
63
11.0k
    if (fileout.IsNull()) {
  Branch (63:9): [True: 0, False: 11.0k]
64
0
        fileout.fclose();
65
0
        remove(pathTmp);
66
0
        LogError("%s: Failed to open file %s\n", __func__, fs::PathToString(pathTmp));
67
0
        return false;
68
0
    }
69
70
    // Serialize
71
11.0k
    if (!SerializeDB(fileout, data)) {
  Branch (71:9): [True: 0, False: 11.0k]
72
0
        fileout.fclose();
73
0
        remove(pathTmp);
74
0
        return false;
75
0
    }
76
11.0k
    if (!fileout.Commit()) {
  Branch (76:9): [True: 0, False: 11.0k]
77
0
        fileout.fclose();
78
0
        remove(pathTmp);
79
0
        LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));
80
0
        return false;
81
0
    }
82
11.0k
    fileout.fclose();
83
84
    // replace existing file, if any, with new file
85
11.0k
    if (!RenameOver(pathTmp, path)) {
  Branch (85:9): [True: 0, False: 11.0k]
86
0
        remove(pathTmp);
87
0
        LogError("%s: Rename-into-place failed\n", __func__);
88
0
        return false;
89
0
    }
90
91
11.0k
    return true;
92
11.0k
}
93
94
template <typename Stream, typename Data>
95
void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)
96
0
{
97
0
    HashVerifier verifier{stream};
98
    // de-serialize file header (network specific magic number) and ..
99
0
    MessageStartChars pchMsgTmp;
100
0
    verifier >> pchMsgTmp;
101
    // ... verify the network matches ours
102
0
    if (pchMsgTmp != Params().MessageStart()) {
  Branch (102:9): [True: 0, False: 0]
  Branch (102:9): [True: 0, False: 0]
  Branch (102:9): [True: 0, False: 0]
103
0
        throw std::runtime_error{"Invalid network magic number"};
104
0
    }
105
106
    // de-serialize data
107
0
    verifier >> data;
108
109
    // verify checksum
110
0
    if (fCheckSum) {
  Branch (110:9): [True: 0, False: 0]
  Branch (110:9): [True: 0, False: 0]
  Branch (110:9): [True: 0, False: 0]
111
0
        uint256 hashTmp;
112
0
        stream >> hashTmp;
113
0
        if (hashTmp != verifier.GetHash()) {
  Branch (113:13): [True: 0, False: 0]
  Branch (113:13): [True: 0, False: 0]
  Branch (113:13): [True: 0, False: 0]
114
0
            throw std::runtime_error{"Checksum mismatch, data corrupted"};
115
0
        }
116
0
    }
117
0
}
Unexecuted instantiation: addrdb.cpp:void (anonymous namespace)::DeserializeDB<DataStream, AddrMan&>(DataStream&, AddrMan&, bool)
Unexecuted instantiation: addrdb.cpp:void (anonymous namespace)::DeserializeDB<AutoFile, AddrMan&>(AutoFile&, AddrMan&, bool)
Unexecuted instantiation: addrdb.cpp:void (anonymous namespace)::DeserializeDB<AutoFile, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > >&>(AutoFile&, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > >&, bool)
118
119
template <typename Data>
120
void DeserializeFileDB(const fs::path& path, Data&& data)
121
22.1k
{
122
22.1k
    FILE* file = fsbridge::fopen(path, "rb");
123
22.1k
    AutoFile filein{file};
124
22.1k
    if (filein.IsNull()) {
  Branch (124:9): [True: 11.0k, False: 0]
  Branch (124:9): [True: 11.0k, False: 0]
125
22.1k
        throw DbNotFoundError{};
126
22.1k
    }
127
0
    DeserializeDB(filein, data);
128
0
}
addrdb.cpp:void (anonymous namespace)::DeserializeFileDB<AddrMan&>(fs::path const&, AddrMan&)
Line
Count
Source
121
11.0k
{
122
11.0k
    FILE* file = fsbridge::fopen(path, "rb");
123
11.0k
    AutoFile filein{file};
124
11.0k
    if (filein.IsNull()) {
  Branch (124:9): [True: 11.0k, False: 0]
125
11.0k
        throw DbNotFoundError{};
126
11.0k
    }
127
0
    DeserializeDB(filein, data);
128
0
}
addrdb.cpp:void (anonymous namespace)::DeserializeFileDB<ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > > >(fs::path const&, ParamsWrapper<CAddress::SerParams, std::vector<CAddress, std::allocator<CAddress> > >&&)
Line
Count
Source
121
11.0k
{
122
11.0k
    FILE* file = fsbridge::fopen(path, "rb");
123
11.0k
    AutoFile filein{file};
124
11.0k
    if (filein.IsNull()) {
  Branch (124:9): [True: 11.0k, False: 0]
125
11.0k
        throw DbNotFoundError{};
126
11.0k
    }
127
0
    DeserializeDB(filein, data);
128
0
}
129
} // namespace
130
131
CBanDB::CBanDB(fs::path ban_list_path)
132
11.0k
    : m_banlist_dat(ban_list_path + ".dat"),
133
11.0k
      m_banlist_json(ban_list_path + ".json")
134
11.0k
{
135
11.0k
}
136
137
bool CBanDB::Write(const banmap_t& banSet)
138
11.0k
{
139
11.0k
    std::vector<std::string> errors;
140
11.0k
    if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
  Branch (140:9): [True: 11.0k, False: 0]
141
11.0k
        return true;
142
11.0k
    }
143
144
0
    for (const auto& err : errors) {
  Branch (144:26): [True: 0, False: 0]
145
0
        LogError("%s\n", err);
146
0
    }
147
0
    return false;
148
11.0k
}
149
150
bool CBanDB::Read(banmap_t& banSet)
151
11.0k
{
152
11.0k
    if (fs::exists(m_banlist_dat)) {
  Branch (152:9): [True: 0, False: 11.0k]
153
0
        LogPrintf("banlist.dat ignored because it can only be read by " CLIENT_NAME " version 22.x. Remove %s to silence this warning.\n", fs::quoted(fs::PathToString(m_banlist_dat)));
154
0
    }
155
    // If the JSON banlist does not exist, then recreate it
156
11.0k
    if (!fs::exists(m_banlist_json)) {
  Branch (156:9): [True: 11.0k, False: 0]
157
11.0k
        return false;
158
11.0k
    }
159
160
0
    std::map<std::string, common::SettingsValue> settings;
161
0
    std::vector<std::string> errors;
162
163
0
    if (!common::ReadSettings(m_banlist_json, settings, errors)) {
  Branch (163:9): [True: 0, False: 0]
164
0
        for (const auto& err : errors) {
  Branch (164:30): [True: 0, False: 0]
165
0
            LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
166
0
        }
167
0
        return false;
168
0
    }
169
170
0
    try {
171
0
        BanMapFromJson(settings[JSON_KEY], banSet);
172
0
    } catch (const std::runtime_error& e) {
173
0
        LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
174
0
        return false;
175
0
    }
176
177
0
    return true;
178
0
}
179
180
bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
181
22.1k
{
182
22.1k
    const auto pathAddr = args.GetDataDirNet() / "peers.dat";
183
22.1k
    return SerializeFileDB("peers", pathAddr, addr);
184
22.1k
}
185
186
void ReadFromStream(AddrMan& addr, DataStream& ssPeers)
187
0
{
188
0
    DeserializeDB(ssPeers, addr, false);
189
0
}
190
191
util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)
192
11.0k
{
193
11.0k
    auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
194
11.0k
    bool deterministic = HasTestOption(args, "addrman"); // use a deterministic addrman only for tests
195
196
11.0k
    auto addrman{std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman)};
197
198
11.0k
    const auto start{SteadyClock::now()};
199
11.0k
    const auto path_addr{args.GetDataDirNet() / "peers.dat"};
200
11.0k
    try {
201
11.0k
        DeserializeFileDB(path_addr, *addrman);
202
11.0k
        LogPrintf("Loaded %i addresses from peers.dat  %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
203
11.0k
    } catch (const DbNotFoundError&) {
204
        // Addrman can be in an inconsistent state after failure, reset it
205
11.0k
        addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
206
11.0k
        LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
207
11.0k
        DumpPeerAddresses(args, *addrman);
208
11.0k
    } catch (const InvalidAddrManVersionError&) {
209
0
        if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
  Branch (209:13): [True: 0, False: 0]
210
0
            return util::Error{strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."))};
211
0
        }
212
        // Addrman can be in an inconsistent state after failure, reset it
213
0
        addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
214
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)));
215
0
        DumpPeerAddresses(args, *addrman);
216
0
    } catch (const std::exception& e) {
217
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."),
218
0
                                     e.what(), CLIENT_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
219
0
    }
220
11.0k
    return addrman;
221
11.0k
}
222
223
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
224
11.0k
{
225
11.0k
    LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
226
11.0k
    SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));
227
11.0k
}
228
229
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
230
11.0k
{
231
11.0k
    std::vector<CAddress> anchors;
232
11.0k
    try {
233
11.0k
        DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));
234
11.0k
        LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
235
11.0k
    } catch (const std::exception&) {
236
11.0k
        anchors.clear();
237
11.0k
    }
238
239
11.0k
    fs::remove(anchors_db_path);
240
11.0k
    return anchors;
241
11.0k
}