Bitcoin ABC 0.33.10
P2P Digital Currency
mempool_persist.cpp
Go to the documentation of this file.
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
6
7#include <consensus/amount.h>
8#include <logging.h>
10#include <serialize.h>
11#include <streams.h>
12#include <sync.h>
13#include <txmempool.h>
14#include <uint256.h>
15#include <util/fs.h>
16#include <util/fs_helpers.h>
18#include <util/time.h>
19#include <validation.h>
20
21#include <cstdint>
22#include <cstdio>
23#include <exception>
24#include <functional>
25#include <map>
26#include <memory>
27#include <set>
28#include <stdexcept>
29#include <utility>
30#include <vector>
31
33
34namespace kernel {
35static const uint64_t MEMPOOL_DUMP_VERSION = 1;
36
37bool LoadMempool(CTxMemPool &pool, const fs::path &load_path,
38 Chainstate &active_chainstate, ImportMempoolOptions &&opts) {
39 if (load_path.empty()) {
40 return false;
41 }
42
43 AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
44 if (file.IsNull()) {
46 "Failed to open mempool file from disk. Continuing anyway.\n");
47 return false;
48 }
49
50 int64_t count = 0;
51 int64_t expired = 0;
52 int64_t failed = 0;
53 int64_t already_there = 0;
54 int64_t unbroadcast = 0;
55 const auto now{NodeClock::now()};
56
57 try {
58 uint64_t version;
59 file >> version;
60 if (version != MEMPOOL_DUMP_VERSION) {
61 return false;
62 }
63
64 uint64_t num;
65 file >> num;
66 while (num) {
67 --num;
69 int64_t nTime;
70 int64_t nFeeDelta;
71 file >> tx;
72 file >> nTime;
73 file >> nFeeDelta;
74
75 if (opts.use_current_time) {
76 nTime = TicksSinceEpoch<std::chrono::seconds>(now);
77 }
78
79 Amount amountdelta = nFeeDelta * SATOSHI;
80 if (amountdelta != Amount::zero() &&
81 opts.apply_fee_delta_priority) {
82 pool.PrioritiseTransaction(tx->GetId(), amountdelta);
83 }
84 if (nTime >
85 TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
87 const auto &accepted =
88 AcceptToMemoryPool(active_chainstate, tx, nTime,
89 /*bypass_limits=*/false,
90 /*test_accept=*/false);
91 if (accepted.m_result_type ==
93 ++count;
94 } else {
95 // mempool may contain the transaction already, e.g. from
96 // wallet(s) having loaded it while we were processing
97 // mempool transactions; consider these as valid, instead of
98 // failed, but mark them as 'already there'
99 if (pool.exists(tx->GetId())) {
100 ++already_there;
101 } else {
102 ++failed;
103 }
104 }
105 } else {
106 ++expired;
107 }
108
109 if (active_chainstate.m_chainman.m_interrupt) {
110 return false;
111 }
112 }
113 std::map<TxId, Amount> mapDeltas;
114 file >> mapDeltas;
115
116 if (opts.apply_fee_delta_priority) {
117 for (const auto &i : mapDeltas) {
118 pool.PrioritiseTransaction(i.first, i.second);
119 }
120 }
121
122 std::set<TxId> unbroadcast_txids;
123 file >> unbroadcast_txids;
124 if (opts.apply_unbroadcast_set) {
125 unbroadcast = unbroadcast_txids.size();
126 for (const auto &txid : unbroadcast_txids) {
127 // Ensure transactions were accepted to mempool then add to
128 // unbroadcast set.
129 if (pool.get(txid) != nullptr) {
130 pool.AddUnbroadcastTx(txid);
131 }
132 }
133 }
134 } catch (const std::exception &e) {
135 LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing "
136 "anyway.\n",
137 e.what());
138 return false;
139 }
140
141 LogPrintf("Imported mempool transactions from disk: %i succeeded, %i "
142 "failed, %i expired, %i already there, %i waiting for initial "
143 "broadcast\n",
144 count, failed, expired, already_there, unbroadcast);
145 return true;
146}
147
148bool DumpMempool(const CTxMemPool &pool, const fs::path &dump_path,
149 FopenFn mockable_fopen_function, bool skip_file_commit) {
150 auto start = SteadyClock::now();
151
152 std::map<uint256, Amount> mapDeltas;
153 std::vector<TxMempoolInfo> vinfo;
154 std::set<TxId> unbroadcast_txids;
155
156 static Mutex dump_mutex;
157 LOCK(dump_mutex);
158
159 {
160 LOCK(pool.cs);
161 for (const auto &i : pool.mapDeltas) {
162 mapDeltas[i.first] = i.second;
163 }
164
165 vinfo = pool.infoAll();
166 unbroadcast_txids = pool.GetUnbroadcastTxs();
167 }
168
169 auto mid = SteadyClock::now();
170
171 try {
172 AutoFile file{mockable_fopen_function(dump_path + ".new", "wb")};
173 if (file.IsNull()) {
174 return false;
175 }
176
177 uint64_t version = MEMPOOL_DUMP_VERSION;
178 file << version;
179
180 file << uint64_t(vinfo.size());
181 for (const auto &i : vinfo) {
182 file << *(i.tx);
183 file << int64_t(count_seconds(i.m_time));
184 file << i.nFeeDelta;
185 mapDeltas.erase(i.tx->GetId());
186 }
187
188 file << mapDeltas;
189
190 LogPrintf("Writing %d unbroadcast transactions to disk.\n",
191 unbroadcast_txids.size());
192 file << unbroadcast_txids;
193
194 if (!skip_file_commit && !FileCommit(file.Get())) {
195 throw std::runtime_error("FileCommit failed");
196 }
197 file.fclose();
198 if (!RenameOver(dump_path + ".new", dump_path)) {
199 throw std::runtime_error("Rename failed");
200 }
201 auto last = SteadyClock::now();
202
203 LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
204 Ticks<SecondsDouble>(mid - start),
205 Ticks<SecondsDouble>(last - mid));
206 } catch (const std::exception &e) {
207 LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
208 return false;
209 }
210 return true;
211}
212
213} // namespace kernel
static constexpr Amount SATOSHI
Definition: amount.h:149
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:430
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:221
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:317
const std::chrono::seconds m_expiry
Definition: txmempool.h:355
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:535
bool exists(const TxId &txid) const
Definition: txmempool.h:535
std::set< TxId > GetUnbroadcastTxs() const
Returns transactions in unbroadcast set.
Definition: txmempool.h:574
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:676
void PrioritiseTransaction(const TxId &txid, const Amount nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
Definition: txmempool.cpp:706
void AddUnbroadcastTx(const TxId &txid)
Adds a transaction to the unbroadcast set.
Definition: txmempool.h:561
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:721
ChainstateManager & m_chainman
The chainstate manager that owns this chainstate.
Definition: validation.h:783
const util::SignalInterrupt & m_interrupt
Definition: validation.h:1309
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:258
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:111
#define LogPrintf(...)
Definition: logging.h:424
std::function< FILE *(const fs::path &, const char *)> FopenFn
Definition: fs.h:204
Definition: init.h:28
bool LoadMempool(CTxMemPool &pool, const fs::path &load_path, Chainstate &active_chainstate, ImportMempoolOptions &&opts)
Import the file and attempt to add its contents to the mempool.
bool DumpMempool(const CTxMemPool &pool, const fs::path &dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
static const uint64_t MEMPOOL_DUMP_VERSION
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
Definition: amount.h:22
static constexpr Amount zero() noexcept
Definition: amount.h:35
@ VALID
Fully validated, valid.
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:29
#define LOCK(cs)
Definition: sync.h:306
static int count
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:85
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept, unsigned int heightOverride)
Try to add a transaction to the mempool.