Coverage Report

Created: 2026-07-29 23:27

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