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