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 <kernel/mempool_persist.h>
6 : :
7 : : #include <clientversion.h>
8 : : #include <consensus/amount.h>
9 : : #include <logging.h>
10 : : #include <primitives/transaction.h>
11 : : #include <serialize.h>
12 : : #include <streams.h>
13 : : #include <sync.h>
14 : : #include <txmempool.h>
15 : : #include <uint256.h>
16 : : #include <util/fs.h>
17 [ + - ]: 173 : #include <util/fs_helpers.h>
18 [ + - ]: 173 : #include <util/signalinterrupt.h>
19 : : #include <util/time.h>
20 : : #include <validation.h>
21 : :
22 : : #include <cstdint>
23 : : #include <cstdio>
24 : : #include <exception>
25 : : #include <functional>
26 : : #include <map>
27 : : #include <memory>
28 : : #include <set>
29 : : #include <stdexcept>
30 : : #include <utility>
31 : : #include <vector>
32 : :
33 : : using fsbridge::FopenFn;
34 : :
35 : : namespace kernel {
36 : :
37 : : static const uint64_t MEMPOOL_DUMP_VERSION = 1;
38 : :
39 : 715 : bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
40 : : {
41 [ - + ]: 715 : if (load_path.empty()) return false;
42 : :
43 : 715 : FILE* filestr{opts.mockable_fopen_function(load_path, "rb")};
44 [ + - ]: 715 : CAutoFile file{filestr, CLIENT_VERSION};
45 [ + - + + ]: 715 : if (file.IsNull()) {
46 [ + - + - : 2 : LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
+ - ]
47 : 2 : return false;
48 : : }
49 : :
50 : 713 : int64_t count = 0;
51 : 713 : int64_t expired = 0;
52 : 713 : int64_t failed = 0;
53 : 713 : int64_t already_there = 0;
54 : 713 : int64_t unbroadcast = 0;
55 : 713 : const auto now{NodeClock::now()};
56 : :
57 : : try {
58 : : uint64_t version;
59 [ + + ]: 713 : file >> version;
60 [ + + ]: 705 : if (version != MEMPOOL_DUMP_VERSION) {
61 : 1 : return false;
62 : : }
63 : : uint64_t num;
64 [ + + ]: 704 : file >> num;
65 [ + + ]: 113035 : while (num) {
66 : 112892 : --num;
67 : 112892 : CTransactionRef tx;
68 : : int64_t nTime;
69 : : int64_t nFeeDelta;
70 [ + + ]: 112892 : file >> tx;
71 [ + + ]: 112389 : file >> nTime;
72 [ + + ]: 112348 : file >> nFeeDelta;
73 : :
74 [ - + ]: 112505 : if (opts.use_current_time) {
75 [ # # ]: 0 : nTime = TicksSinceEpoch<std::chrono::seconds>(now);
76 : 0 : }
77 : :
78 : 112332 : CAmount amountdelta = nFeeDelta;
79 [ + + + - ]: 112332 : if (amountdelta && opts.apply_fee_delta_priority) {
80 [ + - + - ]: 40667 : pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
81 : 40667 : }
82 [ + - + - : 112332 : if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
+ + ]
83 [ + - + - ]: 36723 : LOCK(cs_main);
84 [ - + ]: 36723 : const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
85 [ + - ]: 36723 : if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
86 : 0 : ++count;
87 : 0 : } else {
88 : : // mempool may contain the transaction already, e.g. from
89 : : // wallet(s) having loaded it while we were processing
90 : : // mempool transactions; consider these as valid, instead of
91 : : // failed, but mark them as 'already there'
92 [ + - + - : 36723 : if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
+ - - + ]
93 : 0 : ++already_there;
94 : 0 : } else {
95 : 36723 : ++failed;
96 : : }
97 : : }
98 : 36723 : } else {
99 : 75609 : ++expired;
100 : : }
101 [ + - - + ]: 112332 : if (active_chainstate.m_chainman.m_interrupt)
102 : 0 : return false;
103 [ - + ]: 112892 : }
104 : 143 : std::map<uint256, CAmount> mapDeltas;
105 [ + + ]: 143 : file >> mapDeltas;
106 : :
107 [ - + ]: 114 : if (opts.apply_fee_delta_priority) {
108 [ + + ]: 13530 : for (const auto& i : mapDeltas) {
109 [ + - ]: 13416 : pool.PrioritiseTransaction(i.first, i.second);
110 : : }
111 : 114 : }
112 : :
113 : 114 : std::set<uint256> unbroadcast_txids;
114 [ + + ]: 114 : file >> unbroadcast_txids;
115 [ - + ]: 48 : if (opts.apply_unbroadcast_set) {
116 : 48 : unbroadcast = unbroadcast_txids.size();
117 [ + + ]: 649 : for (const auto& txid : unbroadcast_txids) {
118 : : // Ensure transactions were accepted to mempool then add to
119 : : // unbroadcast set.
120 [ + - - + : 601 : if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
# # ]
121 : : }
122 : 48 : }
123 [ - + ]: 712 : } catch (const std::exception& e) {
124 [ + - + - : 664 : LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
+ - ]
125 : 664 : return false;
126 [ + - # # ]: 664 : }
127 : :
128 [ + - + - : 48 : LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
+ - ]
129 : 48 : return true;
130 : 1379 : }
131 : :
132 : 715 : bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
133 : : {
134 : 715 : auto start = SteadyClock::now();
135 : :
136 : 715 : std::map<uint256, CAmount> mapDeltas;
137 : 715 : std::vector<TxMempoolInfo> vinfo;
138 : 715 : std::set<uint256> unbroadcast_txids;
139 : :
140 [ + + + - ]: 715 : static Mutex dump_mutex;
141 [ + - + - ]: 715 : LOCK(dump_mutex);
142 : :
143 : : {
144 [ + - + - ]: 715 : LOCK(pool.cs);
145 [ + + ]: 19989 : for (const auto &i : pool.mapDeltas) {
146 [ + - ]: 19274 : mapDeltas[i.first] = i.second;
147 : : }
148 [ + - ]: 715 : vinfo = pool.infoAll();
149 [ + - ]: 715 : unbroadcast_txids = pool.GetUnbroadcastTxs();
150 : 715 : }
151 : :
152 : 715 : auto mid = SteadyClock::now();
153 : :
154 : : try {
155 [ + - + - : 715 : FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")};
+ - ]
156 [ + + ]: 715 : if (!filestr) {
157 : 4 : return false;
158 : : }
159 : :
160 [ + - ]: 711 : CAutoFile file{filestr, CLIENT_VERSION};
161 : :
162 : 711 : uint64_t version = MEMPOOL_DUMP_VERSION;
163 [ + + ]: 711 : file << version;
164 : :
165 [ + - ]: 34 : file << (uint64_t)vinfo.size();
166 [ + - ]: 34 : for (const auto& i : vinfo) {
167 [ # # ]: 0 : file << *(i.tx);
168 [ # # # # ]: 0 : file << int64_t{count_seconds(i.m_time)};
169 [ # # ]: 0 : file << int64_t{i.nFeeDelta};
170 [ # # # # ]: 0 : mapDeltas.erase(i.tx->GetHash());
171 : : }
172 : :
173 [ + + ]: 34 : file << mapDeltas;
174 : :
175 [ + - + - : 24 : LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
+ - ]
176 [ + - ]: 24 : file << unbroadcast_txids;
177 : :
178 [ - + # # : 24 : if (!skip_file_commit && !FileCommit(file.Get()))
# # # # ]
179 [ # # ]: 0 : throw std::runtime_error("FileCommit failed");
180 [ + - ]: 24 : file.fclose();
181 [ + - + - : 24 : if (!RenameOver(dump_path + ".new", dump_path)) {
+ - + - -
+ ]
182 [ + - ]: 24 : throw std::runtime_error("Rename failed");
183 : : }
184 : 0 : auto last = SteadyClock::now();
185 : :
186 [ # # # # : 0 : LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
# # # # #
# # # #
# ]
187 : : Ticks<SecondsDouble>(mid - start),
188 : : Ticks<SecondsDouble>(last - mid));
189 [ - + ]: 711 : } catch (const std::exception& e) {
190 [ + - + - : 711 : LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
+ - ]
191 : 711 : return false;
192 [ + - # # ]: 711 : }
193 : 0 : return true;
194 : 1426 : }
195 : :
196 : : } // namespace kernel
|