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/signalinterrupt.h>
20 : : #include <util/syserror.h>
21 : : #include <util/time.h>
22 : : #include <validation.h>
23 : :
24 : : #include <cstdint>
25 : : #include <cstdio>
26 : : #include <exception>
27 : : #include <functional>
28 : : #include <map>
29 : : #include <memory>
30 : : #include <set>
31 : : #include <stdexcept>
32 : : #include <utility>
33 : : #include <vector>
34 : :
35 : : using fsbridge::FopenFn;
36 : :
37 : : namespace node {
38 : :
39 : : static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1};
40 : : static const uint64_t MEMPOOL_DUMP_VERSION{2};
41 : :
42 : 1531 : bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
43 : : {
44 [ + - ]: 1531 : if (load_path.empty()) return false;
45 : :
46 [ + - ]: 1531 : AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
47 [ + + ]: 1531 : if (file.IsNull()) {
48 [ + - ]: 5 : LogInfo("Failed to open mempool file. Continuing anyway.\n");
49 : : return false;
50 : : }
51 : :
52 : 1526 : int64_t count = 0;
53 : 1526 : int64_t expired = 0;
54 : 1526 : int64_t failed = 0;
55 : 1526 : int64_t already_there = 0;
56 : 1526 : int64_t unbroadcast = 0;
57 : 1526 : const auto now{NodeClock::now()};
58 : :
59 : 1526 : try {
60 : 1526 : uint64_t version;
61 [ + + ]: 1526 : file >> version;
62 : 1505 : std::vector<std::byte> xor_key;
63 [ + + ]: 1505 : if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) {
64 : : // Leave XOR-key empty
65 [ + + ]: 277 : } else if (version == MEMPOOL_DUMP_VERSION) {
66 [ + + ]: 274 : file >> xor_key;
67 : : } else {
68 : : return false;
69 : : }
70 [ + - ]: 2988 : file.SetXor(xor_key);
71 : 1494 : uint64_t total_txns_to_load;
72 [ + + ]: 1494 : file >> total_txns_to_load;
73 : 1484 : uint64_t txns_tried = 0;
74 [ + - ]: 1484 : LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);
75 : : int next_tenth_to_report = 0;
76 [ + + ]: 448647 : while (txns_tried < total_txns_to_load) {
77 : 448476 : const int percentage_done(100.0 * txns_tried / total_txns_to_load);
78 [ + + ]: 448476 : if (next_tenth_to_report < percentage_done / 10) {
79 [ + - ]: 80 : LogInfo("Progress loading mempool transactions from file: %d%% (tried %u, %u remaining)\n",
80 : : percentage_done, txns_tried, total_txns_to_load - txns_tried);
81 : 80 : next_tenth_to_report = percentage_done / 10;
82 : : }
83 : 448476 : ++txns_tried;
84 : :
85 : 448476 : CTransactionRef tx;
86 : 448476 : int64_t nTime;
87 : 448476 : int64_t nFeeDelta;
88 [ + + ]: 448476 : file >> TX_WITH_WITNESS(tx);
89 [ + + ]: 447264 : file >> nTime;
90 [ + + ]: 447228 : file >> nFeeDelta;
91 : :
92 [ - + ]: 447163 : if (opts.use_current_time) {
93 : 0 : nTime = TicksSinceEpoch<std::chrono::seconds>(now);
94 : : }
95 : :
96 : 447163 : CAmount amountdelta = nFeeDelta;
97 [ + + + - ]: 447163 : if (amountdelta && opts.apply_fee_delta_priority) {
98 [ + - ]: 412938 : pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
99 : : }
100 [ + + ]: 447163 : if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) {
101 [ + - ]: 383928 : LOCK(cs_main);
102 [ + - ]: 383928 : const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
103 [ - + ]: 383928 : if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
104 : 0 : ++count;
105 : : } else {
106 : : // mempool may contain the transaction already, e.g. from
107 : : // wallet(s) having loaded it while we were processing
108 : : // mempool transactions; consider these as valid, instead of
109 : : // failed, but mark them as 'already there'
110 [ + - - + ]: 383928 : if (pool.exists(tx->GetHash())) {
111 : 0 : ++already_there;
112 : : } else {
113 : 383928 : ++failed;
114 : : }
115 : : }
116 [ + - ]: 767856 : } else {
117 : 63235 : ++expired;
118 : : }
119 [ + - - + ]: 447163 : if (active_chainstate.m_chainman.m_interrupt)
120 [ # # ]: 0 : return false;
121 : 448476 : }
122 [ + + ]: 171 : std::map<uint256, CAmount> mapDeltas;
123 [ + + ]: 171 : file >> mapDeltas;
124 : :
125 [ + - ]: 149 : if (opts.apply_fee_delta_priority) {
126 [ + + ]: 237011 : for (const auto& i : mapDeltas) {
127 [ + - ]: 236862 : pool.PrioritiseTransaction(i.first, i.second);
128 : : }
129 : : }
130 : :
131 [ + + ]: 149 : std::set<uint256> unbroadcast_txids;
132 [ + + ]: 149 : file >> unbroadcast_txids;
133 [ + - ]: 87 : if (opts.apply_unbroadcast_set) {
134 : 87 : unbroadcast = unbroadcast_txids.size();
135 [ + + ]: 2670 : for (const auto& txid : unbroadcast_txids) {
136 : : // Ensure transactions were accepted to mempool then add to
137 : : // unbroadcast set.
138 [ + - - + : 2583 : if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
- + - - ]
139 : : }
140 : : }
141 [ - + ]: 1672 : } catch (const std::exception& e) {
142 [ + - ]: 1436 : LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());
143 : 1436 : return false;
144 : 1436 : }
145 : :
146 [ + - ]: 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);
147 : : return true;
148 : 1531 : }
149 : :
150 : 1531 : bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
151 : : {
152 : 1531 : auto start = SteadyClock::now();
153 : :
154 [ + + ]: 1531 : std::map<uint256, CAmount> mapDeltas;
155 : 1531 : std::vector<TxMempoolInfo> vinfo;
156 [ + + ]: 1531 : std::set<uint256> unbroadcast_txids;
157 : :
158 [ + + + - ]: 1531 : static Mutex dump_mutex;
159 [ + - ]: 1531 : LOCK(dump_mutex);
160 : :
161 : 1531 : {
162 [ + - ]: 1531 : LOCK(pool.cs);
163 [ + + ]: 290424 : for (const auto &i : pool.mapDeltas) {
164 [ + - ]: 288893 : mapDeltas[i.first] = i.second;
165 : : }
166 [ + - ]: 3062 : vinfo = pool.infoAll();
167 [ + - + - ]: 3062 : unbroadcast_txids = pool.GetUnbroadcastTxs();
168 : 0 : }
169 : :
170 : 1531 : auto mid = SteadyClock::now();
171 : :
172 [ + - + - ]: 3062 : const fs::path file_fspath{dump_path + ".new"};
173 [ + - + - ]: 1531 : AutoFile file{mockable_fopen_function(file_fspath, "wb")};
174 [ + + ]: 1531 : if (file.IsNull()) {
175 : : return false;
176 : : }
177 : :
178 : 1492 : try {
179 [ + - ]: 1492 : const uint64_t version{pool.m_opts.persist_v1_dat ? MEMPOOL_DUMP_VERSION_NO_XOR_KEY : MEMPOOL_DUMP_VERSION};
180 [ + + ]: 1492 : file << version;
181 : :
182 [ + - ]: 131 : std::vector<std::byte> xor_key(8);
183 [ + - ]: 131 : if (!pool.m_opts.persist_v1_dat) {
184 : 131 : FastRandomContext{}.fillrand(xor_key);
185 [ + - ]: 131 : file << xor_key;
186 : : }
187 [ + - ]: 262 : file.SetXor(xor_key);
188 : :
189 [ + + ]: 131 : uint64_t mempool_transactions_to_write(vinfo.size());
190 [ + + ]: 131 : file << mempool_transactions_to_write;
191 [ + - ]: 72 : LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);
192 [ - + ]: 72 : for (const auto& i : vinfo) {
193 [ # # ]: 0 : file << TX_WITH_WITNESS(*(i.tx));
194 [ # # ]: 0 : file << int64_t{count_seconds(i.m_time)};
195 [ # # ]: 0 : file << int64_t{i.nFeeDelta};
196 : 0 : mapDeltas.erase(i.tx->GetHash());
197 : : }
198 : :
199 [ + + ]: 72 : file << mapDeltas;
200 : :
201 [ + - ]: 69 : LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
202 [ + - ]: 69 : file << unbroadcast_txids;
203 : :
204 [ - + - - : 69 : if (!skip_file_commit && !file.Commit()) {
- - ]
205 [ # # ]: 0 : (void)file.fclose();
206 [ # # ]: 0 : throw std::runtime_error("Commit failed");
207 : : }
208 [ + - + + ]: 138 : if (file.fclose() != 0) {
209 : 68 : throw std::runtime_error(
210 [ + - + - : 204 : strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
+ - ]
211 : : }
212 [ + - + - : 5 : if (!RenameOver(dump_path + ".new", dump_path)) {
+ - + - +
- ]
213 [ + - ]: 1 : throw std::runtime_error("Rename failed");
214 : : }
215 : 0 : auto last = SteadyClock::now();
216 : :
217 [ # # # # ]: 0 : LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",
218 : : Ticks<SecondsDouble>(mid - start),
219 : : Ticks<SecondsDouble>(last - mid),
220 : : fs::file_size(dump_path));
221 [ - + ]: 1492 : } catch (const std::exception& e) {
222 [ + - ]: 1492 : LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
223 [ + - ]: 1492 : (void)file.fclose();
224 : 1492 : return false;
225 : 1492 : }
226 : 0 : return true;
227 [ + - ]: 4593 : }
228 : :
229 : : } // namespace node
|