Branch data Line data Source code
1 : : // Copyright (c) 2022 The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <node/mempool_persist.h>
6 : :
7 : : #include <clientversion.h>
8 : : #include <consensus/amount.h>
9 : : #include <logging.h>
10 : : #include <primitives/transaction.h>
11 : : #include <random.h>
12 : : #include <serialize.h>
13 : : #include <streams.h>
14 : : #include <sync.h>
15 : : #include <txmempool.h>
16 : : #include <uint256.h>
17 : : #include <util/fs.h>
18 : : #include <util/fs_helpers.h>
19 : : #include <util/obfuscation.h>
20 : : #include <util/signalinterrupt.h>
21 : : #include <util/syserror.h>
22 : : #include <util/time.h>
23 : : #include <validation.h>
24 : :
25 : : #include <cstdint>
26 : : #include <cstdio>
27 : : #include <exception>
28 : : #include <functional>
29 : : #include <map>
30 : : #include <memory>
31 : : #include <set>
32 : : #include <stdexcept>
33 : : #include <utility>
34 : : #include <vector>
35 : :
36 : : using fsbridge::FopenFn;
37 : :
38 : : namespace node {
39 : :
40 : : static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1};
41 : : static const uint64_t MEMPOOL_DUMP_VERSION{2};
42 : :
43 : 1657 : bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
44 : : {
45 [ + - ]: 1657 : if (load_path.empty()) return false;
46 : :
47 : 3314 : AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
48 [ + + ]: 1657 : if (file.IsNull()) {
49 [ + - ]: 7 : LogInfo("Failed to open mempool file. Continuing anyway.\n");
50 : : return false;
51 : : }
52 : :
53 : 1650 : int64_t count = 0;
54 : 1650 : int64_t expired = 0;
55 : 1650 : int64_t failed = 0;
56 : 1650 : int64_t already_there = 0;
57 : 1650 : int64_t unbroadcast = 0;
58 : 1650 : const auto now{NodeClock::now()};
59 : :
60 : 1650 : try {
61 : 1650 : uint64_t version;
62 [ + + ]: 1650 : file >> version;
63 : :
64 [ + + ]: 1628 : if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) {
65 : 2670 : file.SetObfuscation({});
66 [ + + ]: 293 : } else if (version == MEMPOOL_DUMP_VERSION) {
67 : 290 : Obfuscation obfuscation;
68 [ + + ]: 290 : file >> obfuscation;
69 : 20 : file.SetObfuscation(obfuscation);
70 : : } else {
71 : : return false;
72 : : }
73 : :
74 : 1355 : uint64_t total_txns_to_load;
75 [ + + ]: 1355 : file >> total_txns_to_load;
76 : 1354 : uint64_t txns_tried = 0;
77 [ + - ]: 1354 : LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);
78 : : int next_tenth_to_report = 0;
79 [ + + ]: 429427 : while (txns_tried < total_txns_to_load) {
80 : 429252 : const int percentage_done(100.0 * txns_tried / total_txns_to_load);
81 [ + + ]: 429252 : if (next_tenth_to_report < percentage_done / 10) {
82 [ + - ]: 68 : LogInfo("Progress loading mempool transactions from file: %d%% (tried %u, %u remaining)\n",
83 : : percentage_done, txns_tried, total_txns_to_load - txns_tried);
84 : 68 : next_tenth_to_report = percentage_done / 10;
85 : : }
86 : 429252 : ++txns_tried;
87 : :
88 : 429252 : CTransactionRef tx;
89 : 429252 : int64_t nTime;
90 : 429252 : int64_t nFeeDelta;
91 [ + + ]: 429252 : file >> TX_WITH_WITNESS(tx);
92 [ + + ]: 428169 : file >> nTime;
93 [ + + ]: 428139 : file >> nFeeDelta;
94 : :
95 [ - + ]: 428073 : if (opts.use_current_time) {
96 : 0 : nTime = TicksSinceEpoch<std::chrono::seconds>(now);
97 : : }
98 : :
99 : 428073 : CAmount amountdelta = nFeeDelta;
100 [ + + + - ]: 428073 : if (amountdelta && opts.apply_fee_delta_priority) {
101 [ + - ]: 396556 : pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
102 : : }
103 [ + + ]: 428073 : if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) {
104 [ + - ]: 368320 : LOCK(cs_main);
105 [ + - ]: 368320 : const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
106 [ - + ]: 368320 : if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
107 : 0 : ++count;
108 : : } else {
109 : : // mempool may contain the transaction already, e.g. from
110 : : // wallet(s) having loaded it while we were processing
111 : : // mempool transactions; consider these as valid, instead of
112 : : // failed, but mark them as 'already there'
113 [ + - - + ]: 368320 : if (pool.exists(tx->GetHash())) {
114 : 0 : ++already_there;
115 : : } else {
116 : 368320 : ++failed;
117 : : }
118 : : }
119 [ + - ]: 736640 : } else {
120 : 59753 : ++expired;
121 : : }
122 [ + - - + ]: 428073 : if (active_chainstate.m_chainman.m_interrupt)
123 [ # # ]: 0 : return false;
124 : 429252 : }
125 [ + + ]: 175 : std::map<Txid, CAmount> mapDeltas;
126 [ + + ]: 175 : file >> mapDeltas;
127 : :
128 [ + - ]: 154 : if (opts.apply_fee_delta_priority) {
129 [ + + ]: 250572 : for (const auto& i : mapDeltas) {
130 [ + - ]: 250418 : pool.PrioritiseTransaction(i.first, i.second);
131 : : }
132 : : }
133 : :
134 [ + + ]: 154 : std::set<Txid> unbroadcast_txids;
135 [ + + ]: 154 : file >> unbroadcast_txids;
136 [ + - ]: 87 : if (opts.apply_unbroadcast_set) {
137 : 87 : unbroadcast = unbroadcast_txids.size();
138 [ + + ]: 2289 : for (const auto& txid : unbroadcast_txids) {
139 : : // Ensure transactions were accepted to mempool then add to
140 : : // unbroadcast set.
141 [ + - - + : 2202 : if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
- + - - ]
142 : : }
143 : : }
144 [ - + ]: 1714 : } catch (const std::exception& e) {
145 [ + - ]: 1560 : LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());
146 : 1560 : return false;
147 : 1560 : }
148 : :
149 [ + - ]: 87 : LogInfo("Imported mempool transactions from file: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
150 : : return true;
151 : 1657 : }
152 : :
153 : 1657 : bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
154 : : {
155 : 1657 : auto start = SteadyClock::now();
156 : :
157 [ + + ]: 1657 : std::map<Txid, CAmount> mapDeltas;
158 : 1657 : std::vector<TxMempoolInfo> vinfo;
159 [ + + ]: 1657 : std::set<Txid> unbroadcast_txids;
160 : :
161 [ + + + - ]: 1657 : static Mutex dump_mutex;
162 [ + - ]: 1657 : LOCK(dump_mutex);
163 : :
164 : 1657 : {
165 [ + - ]: 1657 : LOCK(pool.cs);
166 [ + + ]: 302233 : for (const auto &i : pool.mapDeltas) {
167 [ + - ]: 300576 : mapDeltas[i.first] = i.second;
168 : : }
169 [ + - ]: 3314 : vinfo = pool.infoAll();
170 [ + - + - ]: 3314 : unbroadcast_txids = pool.GetUnbroadcastTxs();
171 : 0 : }
172 : :
173 : 1657 : auto mid = SteadyClock::now();
174 : :
175 [ + - + - ]: 3314 : const fs::path file_fspath{dump_path + ".new"};
176 [ + - + - ]: 3314 : AutoFile file{mockable_fopen_function(file_fspath, "wb")};
177 [ + + ]: 1657 : if (file.IsNull()) {
178 : : return false;
179 : : }
180 : :
181 : 1609 : try {
182 [ + - ]: 1609 : const uint64_t version{pool.m_opts.persist_v1_dat ? MEMPOOL_DUMP_VERSION_NO_XOR_KEY : MEMPOOL_DUMP_VERSION};
183 [ + + ]: 1609 : file << version;
184 : :
185 [ + - ]: 160 : if (!pool.m_opts.persist_v1_dat) {
186 : 160 : const Obfuscation obfuscation{FastRandomContext{}.randbytes<Obfuscation::KEY_SIZE>()};
187 [ + - ]: 160 : file << obfuscation;
188 : 160 : file.SetObfuscation(obfuscation);
189 : : } else {
190 : 0 : file.SetObfuscation({});
191 : : }
192 : :
193 [ - + ]: 160 : uint64_t mempool_transactions_to_write(vinfo.size());
194 [ + + ]: 160 : file << mempool_transactions_to_write;
195 [ + - ]: 92 : LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);
196 [ - + ]: 92 : for (const auto& i : vinfo) {
197 [ # # ]: 0 : file << TX_WITH_WITNESS(*(i.tx));
198 [ # # ]: 0 : file << int64_t{count_seconds(i.m_time)};
199 [ # # ]: 0 : file << int64_t{i.nFeeDelta};
200 : 0 : mapDeltas.erase(i.tx->GetHash());
201 : : }
202 : :
203 [ + + ]: 92 : file << mapDeltas;
204 : :
205 [ + - ]: 89 : LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
206 [ + - ]: 89 : file << unbroadcast_txids;
207 : :
208 [ - + - - : 89 : if (!skip_file_commit && !file.Commit()) {
- - ]
209 [ # # ]: 0 : (void)file.fclose();
210 [ # # ]: 0 : throw std::runtime_error("Commit failed");
211 : : }
212 [ + - + + ]: 178 : if (file.fclose() != 0) {
213 : 87 : throw std::runtime_error(
214 [ + - + - : 261 : strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
+ - ]
215 : : }
216 [ + - + - : 10 : if (!RenameOver(dump_path + ".new", dump_path)) {
+ - + - +
- ]
217 [ + - ]: 2 : throw std::runtime_error("Rename failed");
218 : : }
219 : 0 : auto last = SteadyClock::now();
220 : :
221 [ # # # # ]: 0 : LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",
222 : : Ticks<SecondsDouble>(mid - start),
223 : : Ticks<SecondsDouble>(last - mid),
224 : : fs::file_size(dump_path));
225 [ - + ]: 1609 : } catch (const std::exception& e) {
226 [ + - ]: 1609 : LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
227 [ + - ]: 1609 : (void)file.fclose();
228 : 1609 : return false;
229 : 1609 : }
230 : : return true;
231 [ + - ]: 4971 : }
232 : :
233 : : } // namespace node
|