Bitcoin Core  31.0.0
P2P Digital Currency
mempool_persist.cpp
Go to the documentation of this file.
1 // Copyright (c) 2022-present 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 bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
44 {
45  if (load_path.empty()) return false;
46 
47  AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
48  if (file.IsNull()) {
49  LogInfo("Failed to open mempool file. Continuing anyway.\n");
50  return false;
51  }
52 
53  int64_t count = 0;
54  int64_t expired = 0;
55  int64_t failed = 0;
56  int64_t already_there = 0;
57  int64_t unbroadcast = 0;
58  const auto now{NodeClock::now()};
59 
60  try {
61  uint64_t version;
62  file >> version;
63 
64  if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) {
65  file.SetObfuscation({});
66  } else if (version == MEMPOOL_DUMP_VERSION) {
67  Obfuscation obfuscation;
68  file >> obfuscation;
69  file.SetObfuscation(obfuscation);
70  } else {
71  return false;
72  }
73 
74  uint64_t total_txns_to_load;
75  file >> total_txns_to_load;
76  uint64_t txns_tried = 0;
77  LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);
78  int next_tenth_to_report = 0;
79  while (txns_tried < total_txns_to_load) {
80  const int percentage_done(100.0 * txns_tried / total_txns_to_load);
81  if (next_tenth_to_report < percentage_done / 10) {
82  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  next_tenth_to_report = percentage_done / 10;
85  }
86  ++txns_tried;
87 
88  CTransactionRef tx;
89  int64_t nTime;
90  int64_t nFeeDelta;
91  file >> TX_WITH_WITNESS(tx);
92  file >> nTime;
93  file >> nFeeDelta;
94 
95  if (opts.use_current_time) {
96  nTime = TicksSinceEpoch<std::chrono::seconds>(now);
97  }
98 
99  CAmount amountdelta = nFeeDelta;
100  if (amountdelta && opts.apply_fee_delta_priority) {
101  pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
102  }
103  if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) {
104  LOCK(cs_main);
105  const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
106  if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
107  ++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  if (pool.exists(tx->GetHash())) {
114  ++already_there;
115  } else {
116  ++failed;
117  }
118  }
119  } else {
120  ++expired;
121  }
122  if (active_chainstate.m_chainman.m_interrupt)
123  return false;
124  }
125  std::map<Txid, CAmount> mapDeltas;
126  file >> mapDeltas;
127 
128  if (opts.apply_fee_delta_priority) {
129  for (const auto& i : mapDeltas) {
130  pool.PrioritiseTransaction(i.first, i.second);
131  }
132  }
133 
134  std::set<Txid> unbroadcast_txids;
135  file >> unbroadcast_txids;
136  if (opts.apply_unbroadcast_set) {
137  unbroadcast = unbroadcast_txids.size();
138  for (const auto& txid : unbroadcast_txids) {
139  // Ensure transactions were accepted to mempool then add to
140  // unbroadcast set.
141  if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
142  }
143  }
144  } catch (const std::exception& e) {
145  LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());
146  return false;
147  }
148 
149  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 }
152 
153 bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
154 {
155  auto start = SteadyClock::now();
156 
157  std::map<Txid, CAmount> mapDeltas;
158  std::vector<TxMempoolInfo> vinfo;
159  std::set<Txid> unbroadcast_txids;
160 
161  static Mutex dump_mutex;
162  LOCK(dump_mutex);
163 
164  {
165  LOCK(pool.cs);
166  for (const auto &i : pool.mapDeltas) {
167  mapDeltas[i.first] = i.second;
168  }
169  vinfo = pool.infoAll();
170  unbroadcast_txids = pool.GetUnbroadcastTxs();
171  }
172 
173  auto mid = SteadyClock::now();
174 
175  const fs::path file_fspath{dump_path + ".new"};
176  AutoFile file{mockable_fopen_function(file_fspath, "wb")};
177  if (file.IsNull()) {
178  return false;
179  }
180 
181  try {
183  file << version;
184 
185  if (!pool.m_opts.persist_v1_dat) {
187  file << obfuscation;
188  file.SetObfuscation(obfuscation);
189  } else {
190  file.SetObfuscation({});
191  }
192 
193  uint64_t mempool_transactions_to_write(vinfo.size());
194  file << mempool_transactions_to_write;
195  LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);
196  for (const auto& i : vinfo) {
197  file << TX_WITH_WITNESS(*(i.tx));
198  file << int64_t{count_seconds(i.m_time)};
199  file << int64_t{i.nFeeDelta};
200  mapDeltas.erase(i.tx->GetHash());
201  }
202 
203  file << mapDeltas;
204 
205  LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
206  file << unbroadcast_txids;
207 
208  if (!skip_file_commit && !file.Commit()) {
209  (void)file.fclose();
210  throw std::runtime_error("Commit failed");
211  }
212  if (file.fclose() != 0) {
213  throw std::runtime_error(
214  strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
215  }
216  if (!RenameOver(dump_path + ".new", dump_path)) {
217  throw std::runtime_error("Rename failed");
218  }
219  auto last = SteadyClock::now();
220 
221  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  } catch (const std::exception& e) {
226  LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
227  (void)file.fclose();
228  return false;
229  }
230  return true;
231 }
232 
233 } // namespace node
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
std::vector< B > randbytes(size_t len) noexcept
Generate random bytes.
Definition: random.h:297
void AddUnbroadcastTx(const Txid &txid)
Adds a transaction to the unbroadcast set.
Definition: txmempool.h:542
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:88
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:600
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
CTransactionRef get(const Txid &hash) const
Definition: txmempool.cpp:621
static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept)
Try to add a transaction to the mempool.
std::chrono::seconds expiry
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:372
static constexpr size_t KEY_SIZE
Definition: obfuscation.h:23
void PrioritiseTransaction(const Txid &hash, const CAmount &nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
Definition: txmempool.cpp:630
std::set< Txid > GetUnbroadcastTxs() const
Returns transactions in unbroadcast set.
Definition: txmempool.h:556
const util::SignalInterrupt & m_interrupt
Definition: validation.h:1034
bool DumpMempool(const CTxMemPool &pool, const fs::path &dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:243
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:17
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.
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
std::function< FILE *(const fs::path &, const char *)> FopenFn
Definition: fs.h:192
ChainstateManager & m_chainman
The chainstate manager that owns this chainstate.
Definition: validation.h:583
Chainstate stores and provides an API to update our local knowledge of the current best chain...
Definition: validation.h:550
#define LOCK(cs)
Definition: sync.h:258
#define LogInfo(...)
Definition: log.h:95
Fast randomness source.
Definition: random.h:385
bool exists(const Txid &txid) const
Definition: txmempool.h:503
static const uint64_t MEMPOOL_DUMP_VERSION
Definition: messages.h:21
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:30
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:186
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
static int count
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it...
Definition: txmempool.h:260
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:180
const Options m_opts
Definition: txmempool.h:303