/workdir/bitcoin/src/addrdb.cpp
Line | Count | Source (jump to first uncovered line) |
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 <config/bitcoin-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 | 0 | { |
39 | | // Write and commit header, data |
40 | 0 | try { |
41 | 0 | HashedSourceWriter hashwriter{stream}; |
42 | 0 | hashwriter << Params().MessageStart() << data; |
43 | 0 | stream << hashwriter.GetHash(); |
44 | 0 | } 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 | 0 | return true; |
50 | 0 | } Unexecuted instantiation: addrdb.cpp:bool (anonymous namespace)::SerializeDB<AutoFile, AddrMan>(AutoFile&, AddrMan const&) Unexecuted instantiation: 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&) |
51 | | |
52 | | template <typename Data> |
53 | | bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data) |
54 | 0 | { |
55 | | // Generate random temporary filename |
56 | 0 | const uint16_t randv{FastRandomContext().rand<uint16_t>()}; |
57 | 0 | std::string tmpfn = strprintf("%s.%04x", prefix, randv); |
58 | | |
59 | | // open temp output file |
60 | 0 | fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn); |
61 | 0 | FILE *file = fsbridge::fopen(pathTmp, "wb"); |
62 | 0 | AutoFile fileout{file}; |
63 | 0 | if (fileout.IsNull()) { Branch (63:9): [True: 0, False: 0]
Branch (63:9): [True: 0, False: 0]
|
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 | 0 | if (!SerializeDB(fileout, data)) { Branch (71:9): [True: 0, False: 0]
Branch (71:9): [True: 0, False: 0]
|
72 | 0 | fileout.fclose(); |
73 | 0 | remove(pathTmp); |
74 | 0 | return false; |
75 | 0 | } |
76 | 0 | if (!FileCommit(fileout.Get())) { Branch (76:9): [True: 0, False: 0]
Branch (76:9): [True: 0, False: 0]
|
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 | 0 | fileout.fclose(); |
83 | | |
84 | | // replace existing file, if any, with new file |
85 | 0 | if (!RenameOver(pathTmp, path)) { Branch (85:9): [True: 0, False: 0]
Branch (85:9): [True: 0, False: 0]
|
86 | 0 | remove(pathTmp); |
87 | 0 | LogError("%s: Rename-into-place failed\n", __func__); |
88 | 0 | return false; |
89 | 0 | } |
90 | | |
91 | 0 | return true; |
92 | 0 | } Unexecuted instantiation: 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&) Unexecuted instantiation: 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&) |
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 | 0 | { |
122 | 0 | FILE* file = fsbridge::fopen(path, "rb"); |
123 | 0 | AutoFile filein{file}; |
124 | 0 | if (filein.IsNull()) { Branch (124:9): [True: 0, False: 0]
Branch (124:9): [True: 0, False: 0]
|
125 | 0 | throw DbNotFoundError{}; |
126 | 0 | } |
127 | 0 | DeserializeDB(filein, data); |
128 | 0 | } Unexecuted instantiation: addrdb.cpp:void (anonymous namespace)::DeserializeFileDB<AddrMan&>(fs::path const&, AddrMan&) Unexecuted instantiation: 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> > >&&) |
129 | | } // namespace |
130 | | |
131 | | CBanDB::CBanDB(fs::path ban_list_path) |
132 | 0 | : m_banlist_dat(ban_list_path + ".dat"), |
133 | 0 | m_banlist_json(ban_list_path + ".json") |
134 | 0 | { |
135 | 0 | } |
136 | | |
137 | | bool CBanDB::Write(const banmap_t& banSet) |
138 | 0 | { |
139 | 0 | std::vector<std::string> errors; |
140 | 0 | if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) { Branch (140:9): [True: 0, False: 0]
|
141 | 0 | return true; |
142 | 0 | } |
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 | 0 | } |
149 | | |
150 | | bool CBanDB::Read(banmap_t& banSet) |
151 | 0 | { |
152 | 0 | if (fs::exists(m_banlist_dat)) { Branch (152:9): [True: 0, False: 0]
|
153 | 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))); |
154 | 0 | } |
155 | | // If the JSON banlist does not exist, then recreate it |
156 | 0 | if (!fs::exists(m_banlist_json)) { Branch (156:9): [True: 0, False: 0]
|
157 | 0 | return false; |
158 | 0 | } |
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 | 0 | { |
182 | 0 | const auto pathAddr = args.GetDataDirNet() / "peers.dat"; |
183 | 0 | return SerializeFileDB("peers", pathAddr, addr); |
184 | 0 | } |
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 | 0 | { |
193 | 0 | auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000); |
194 | 0 | bool deterministic = HasTestOption(args, "addrman"); // use a deterministic addrman only for tests |
195 | |
|
196 | 0 | auto addrman{std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman)}; |
197 | |
|
198 | 0 | const auto start{SteadyClock::now()}; |
199 | 0 | const auto path_addr{args.GetDataDirNet() / "peers.dat"}; |
200 | 0 | try { |
201 | 0 | DeserializeFileDB(path_addr, *addrman); |
202 | 0 | LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); |
203 | 0 | } catch (const DbNotFoundError&) { |
204 | | // Addrman can be in an inconsistent state after failure, reset it |
205 | 0 | addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman); |
206 | 0 | LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr))); |
207 | 0 | DumpPeerAddresses(args, *addrman); |
208 | 0 | } 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(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))}; |
219 | 0 | } |
220 | 0 | return addrman; |
221 | 0 | } |
222 | | |
223 | | void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors) |
224 | 0 | { |
225 | 0 | LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size())); |
226 | 0 | SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors)); |
227 | 0 | } |
228 | | |
229 | | std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path) |
230 | 0 | { |
231 | 0 | std::vector<CAddress> anchors; |
232 | 0 | try { |
233 | 0 | DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors)); |
234 | 0 | LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename()))); |
235 | 0 | } catch (const std::exception&) { |
236 | 0 | anchors.clear(); |
237 | 0 | } |
238 | |
|
239 | 0 | fs::remove(anchors_db_path); |
240 | 0 | return anchors; |
241 | 0 | } |