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 <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/syserror.h>
28 : : #include <util/translation.h>
29 : :
30 : : namespace {
31 : :
32 : 0 : class DbNotFoundError : public std::exception
33 : : {
34 : : using std::exception::exception;
35 : : };
36 : :
37 : : template <typename Stream, typename Data>
38 : 0 : bool SerializeDB(Stream& stream, const Data& data)
39 : : {
40 : : // Write and commit header, data
41 : : try {
42 [ # # ]: 0 : HashedSourceWriter hashwriter{stream};
43 [ # # # # : 0 : hashwriter << Params().MessageStart() << data;
# # ]
44 [ # # ]: 0 : stream << hashwriter.GetHash();
45 [ - - ]: 0 : } catch (const std::exception& e) {
46 [ - - ]: 0 : LogError("%s: Serialize or I/O error - %s\n", __func__, e.what());
47 : : return false;
48 : : }
49 : :
50 : : return true;
51 : : }
52 : :
53 : : template <typename Data>
54 : 0 : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
55 : : {
56 : : // Generate random temporary filename
57 : 0 : const uint16_t randv{FastRandomContext().rand<uint16_t>()};
58 : 0 : std::string tmpfn = strprintf("%s.%04x", prefix, randv);
59 : :
60 : : // open temp output file
61 [ # # # # ]: 0 : fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
62 [ # # ]: 0 : FILE *file = fsbridge::fopen(pathTmp, "wb");
63 [ # # # # ]: 0 : AutoFile fileout{file};
64 [ # # ]: 0 : 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 : : }
69 : :
70 : : // Serialize
71 [ # # # # ]: 0 : if (!SerializeDB(fileout, data)) {
72 [ # # ]: 0 : (void)fileout.fclose();
73 [ # # ]: 0 : remove(pathTmp);
74 : : return false;
75 : : }
76 [ # # # # ]: 0 : 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 : : }
82 [ # # # # ]: 0 : 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 : : }
88 : :
89 : : // replace existing file, if any, with new file
90 [ # # # # : 0 : if (!RenameOver(pathTmp, path)) {
# # ]
91 [ # # ]: 0 : remove(pathTmp);
92 [ # # ]: 0 : LogError("%s: Rename-into-place failed\n", __func__);
93 : : return false;
94 : : }
95 : :
96 : : return true;
97 : 0 : }
98 : :
99 : : template <typename Stream, typename Data>
100 : 1487 : void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)
101 : : {
102 : 1487 : HashVerifier verifier{stream};
103 : : // de-serialize file header (network specific magic number) and ..
104 : : MessageStartChars pchMsgTmp;
105 : 1400 : verifier >> pchMsgTmp;
106 : : // ... verify the network matches ours
107 [ + + ]: 1400 : if (pchMsgTmp != Params().MessageStart()) {
108 [ + - ]: 7 : throw std::runtime_error{"Invalid network magic number"};
109 : : }
110 : :
111 : : // de-serialize data
112 : 599 : verifier >> data;
113 : :
114 : : // verify checksum
115 [ - + ]: 599 : 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 : : }
121 : : }
122 : 599 : }
123 : :
124 : : template <typename Data>
125 : 0 : void DeserializeFileDB(const fs::path& path, Data&& data)
126 : : {
127 : 0 : FILE* file = fsbridge::fopen(path, "rb");
128 [ # # # # ]: 0 : AutoFile filein{file};
129 [ # # ]: 0 : if (filein.IsNull()) {
130 : 0 : throw DbNotFoundError{};
131 : : }
132 [ # # ]: 0 : DeserializeDB(filein, data);
133 : 0 : }
134 : : } // namespace
135 : :
136 : 3065 : CBanDB::CBanDB(fs::path ban_list_path)
137 [ + - + - ]: 9195 : : m_banlist_dat(ban_list_path + ".dat"),
138 [ + - ]: 6130 : m_banlist_json(ban_list_path + ".json")
139 : : {
140 : 3065 : }
141 : :
142 : 25660 : bool CBanDB::Write(const banmap_t& banSet)
143 : : {
144 : 25660 : std::vector<std::string> errors;
145 [ + - + - : 76980 : if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
+ - + + +
+ - - ]
146 : : return true;
147 : : }
148 : :
149 [ + + ]: 12180 : for (const auto& err : errors) {
150 [ + - ]: 6090 : LogError("%s\n", err);
151 : : }
152 : : return false;
153 [ + - ]: 51320 : }
154 : :
155 : 3065 : bool CBanDB::Read(banmap_t& banSet)
156 : : {
157 [ - + ]: 3065 : if (fs::exists(m_banlist_dat)) {
158 [ # # ]: 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)));
159 : : }
160 : : // If the JSON banlist does not exist, then recreate it
161 [ + + ]: 3065 : if (!fs::exists(m_banlist_json)) {
162 : : return false;
163 : : }
164 : :
165 [ + - ]: 2526 : std::map<std::string, common::SettingsValue> settings;
166 : 2526 : std::vector<std::string> errors;
167 : :
168 [ + - + + ]: 2526 : if (!common::ReadSettings(m_banlist_json, settings, errors)) {
169 [ + + ]: 1350 : for (const auto& err : errors) {
170 [ + - + - ]: 2025 : LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
171 : : }
172 : : return false;
173 : : }
174 : :
175 : 1851 : try {
176 [ + - + - : 2003 : BanMapFromJson(settings[JSON_KEY], banSet);
+ + ]
177 [ - + ]: 152 : } catch (const std::runtime_error& e) {
178 [ + - + - ]: 304 : LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
179 : 152 : return false;
180 : 152 : }
181 : :
182 : 1699 : return true;
183 : 2526 : }
184 : :
185 : 0 : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
186 : : {
187 [ # # ]: 0 : const auto pathAddr = args.GetDataDirNet() / "peers.dat";
188 [ # # # # ]: 0 : return SerializeFileDB("peers", pathAddr, addr);
189 : 0 : }
190 : :
191 : 1487 : void ReadFromStream(AddrMan& addr, DataStream& ssPeers)
192 : : {
193 : 1487 : DeserializeDB(ssPeers, addr, false);
194 : 599 : }
195 : :
196 : 0 : util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)
197 : : {
198 [ # # # # ]: 0 : auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
199 [ # # ]: 0 : bool deterministic = HasTestOption(args, "addrman"); // use a deterministic addrman only for tests
200 : :
201 : 0 : auto addrman{std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman)};
202 : :
203 : 0 : const auto start{SteadyClock::now()};
204 [ # # # # ]: 0 : const auto path_addr{args.GetDataDirNet() / "peers.dat"};
205 : 0 : try {
206 [ # # ]: 0 : DeserializeFileDB(path_addr, *addrman);
207 [ # # # # ]: 0 : LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
208 [ - - - - ]: 0 : } catch (const DbNotFoundError&) {
209 : : // Addrman can be in an inconsistent state after failure, reset it
210 [ - - ]: 0 : addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
211 [ - - - - ]: 0 : LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
212 [ - - ]: 0 : DumpPeerAddresses(args, *addrman);
213 [ - - ]: 0 : } catch (const InvalidAddrManVersionError&) {
214 [ - - - - : 0 : 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 : : }
217 : : // Addrman can be in an inconsistent state after failure, reset it
218 [ - - ]: 0 : addrman = std::make_unique<AddrMan>(netgroupman, deterministic, /*consistency_check_ratio=*/check_addrman);
219 [ - - - - ]: 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)));
220 [ - - ]: 0 : DumpPeerAddresses(args, *addrman);
221 [ - - - - ]: 0 : } catch (const std::exception& e) {
222 [ - - ]: 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."),
223 [ - - ]: 0 : e.what(), CLIENT_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
224 : 0 : }
225 : 0 : return addrman;
226 : 0 : }
227 : :
228 : 0 : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
229 : : {
230 [ # # # # ]: 0 : LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
231 [ # # # # ]: 0 : SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));
232 : 0 : }
233 : :
234 : 0 : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
235 : : {
236 : 0 : std::vector<CAddress> anchors;
237 : 0 : try {
238 [ # # ]: 0 : DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));
239 [ # # # # ]: 0 : LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
240 [ - - ]: 0 : } catch (const std::exception&) {
241 : 0 : anchors.clear();
242 : 0 : }
243 : :
244 [ # # ]: 0 : fs::remove(anchors_db_path);
245 : 0 : return anchors;
246 : 0 : }
|