Bitcoin Core  27.99.0
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 <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/time.h>
21 #include <validation.h>
22 
23 #include <cstdint>
24 #include <cstdio>
25 #include <exception>
26 #include <functional>
27 #include <map>
28 #include <memory>
29 #include <set>
30 #include <stdexcept>
31 #include <utility>
32 #include <vector>
33 
34 using fsbridge::FopenFn;
35 
36 namespace kernel {
37 
38 static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1};
39 static const uint64_t MEMPOOL_DUMP_VERSION{2};
40 
41 bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
42 {
43  if (load_path.empty()) return false;
44 
45  AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
46  if (file.IsNull()) {
47  LogInfo("Failed to open mempool file. Continuing anyway.\n");
48  return false;
49  }
50 
51  int64_t count = 0;
52  int64_t expired = 0;
53  int64_t failed = 0;
54  int64_t already_there = 0;
55  int64_t unbroadcast = 0;
56  const auto now{NodeClock::now()};
57 
58  try {
59  uint64_t version;
60  file >> version;
61  std::vector<std::byte> xor_key;
62  if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) {
63  // Leave XOR-key empty
64  } else if (version == MEMPOOL_DUMP_VERSION) {
65  file >> xor_key;
66  } else {
67  return false;
68  }
69  file.SetXor(xor_key);
70  uint64_t total_txns_to_load;
71  file >> total_txns_to_load;
72  uint64_t txns_tried = 0;
73  LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);
74  int next_tenth_to_report = 0;
75  while (txns_tried < total_txns_to_load) {
76  const int percentage_done(100.0 * txns_tried / total_txns_to_load);
77  if (next_tenth_to_report < percentage_done / 10) {
78  LogInfo("Progress loading mempool transactions from file: %d%% (tried %u, %u remaining)\n",
79  percentage_done, txns_tried, total_txns_to_load - txns_tried);
80  next_tenth_to_report = percentage_done / 10;
81  }
82  ++txns_tried;
83 
84  CTransactionRef tx;
85  int64_t nTime;
86  int64_t nFeeDelta;
87  file >> TX_WITH_WITNESS(tx);
88  file >> nTime;
89  file >> nFeeDelta;
90 
91  if (opts.use_current_time) {
92  nTime = TicksSinceEpoch<std::chrono::seconds>(now);
93  }
94 
95  CAmount amountdelta = nFeeDelta;
96  if (amountdelta && opts.apply_fee_delta_priority) {
97  pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
98  }
99  if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) {
100  LOCK(cs_main);
101  const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
102  if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
103  ++count;
104  } else {
105  // mempool may contain the transaction already, e.g. from
106  // wallet(s) having loaded it while we were processing
107  // mempool transactions; consider these as valid, instead of
108  // failed, but mark them as 'already there'
109  if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
110  ++already_there;
111  } else {
112  ++failed;
113  }
114  }
115  } else {
116  ++expired;
117  }
118  if (active_chainstate.m_chainman.m_interrupt)
119  return false;
120  }
121  std::map<uint256, CAmount> mapDeltas;
122  file >> mapDeltas;
123 
124  if (opts.apply_fee_delta_priority) {
125  for (const auto& i : mapDeltas) {
126  pool.PrioritiseTransaction(i.first, i.second);
127  }
128  }
129 
130  std::set<uint256> unbroadcast_txids;
131  file >> unbroadcast_txids;
132  if (opts.apply_unbroadcast_set) {
133  unbroadcast = unbroadcast_txids.size();
134  for (const auto& txid : unbroadcast_txids) {
135  // Ensure transactions were accepted to mempool then add to
136  // unbroadcast set.
137  if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
138  }
139  }
140  } catch (const std::exception& e) {
141  LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());
142  return false;
143  }
144 
145  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);
146  return true;
147 }
148 
149 bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
150 {
151  auto start = SteadyClock::now();
152 
153  std::map<uint256, CAmount> mapDeltas;
154  std::vector<TxMempoolInfo> vinfo;
155  std::set<uint256> unbroadcast_txids;
156 
157  static Mutex dump_mutex;
158  LOCK(dump_mutex);
159 
160  {
161  LOCK(pool.cs);
162  for (const auto &i : pool.mapDeltas) {
163  mapDeltas[i.first] = i.second;
164  }
165  vinfo = pool.infoAll();
166  unbroadcast_txids = pool.GetUnbroadcastTxs();
167  }
168 
169  auto mid = SteadyClock::now();
170 
171  AutoFile file{mockable_fopen_function(dump_path + ".new", "wb")};
172  if (file.IsNull()) {
173  return false;
174  }
175 
176  try {
178  file << version;
179 
180  std::vector<std::byte> xor_key(8);
181  if (!pool.m_opts.persist_v1_dat) {
182  FastRandomContext{}.fillrand(xor_key);
183  file << xor_key;
184  }
185  file.SetXor(xor_key);
186 
187  uint64_t mempool_transactions_to_write(vinfo.size());
188  file << mempool_transactions_to_write;
189  LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);
190  for (const auto& i : vinfo) {
191  file << TX_WITH_WITNESS(*(i.tx));
192  file << int64_t{count_seconds(i.m_time)};
193  file << int64_t{i.nFeeDelta};
194  mapDeltas.erase(i.tx->GetHash());
195  }
196 
197  file << mapDeltas;
198 
199  LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
200  file << unbroadcast_txids;
201 
202  if (!skip_file_commit && !FileCommit(file.Get()))
203  throw std::runtime_error("FileCommit failed");
204  file.fclose();
205  if (!RenameOver(dump_path + ".new", dump_path)) {
206  throw std::runtime_error("Rename failed");
207  }
208  auto last = SteadyClock::now();
209 
210  LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",
211  Ticks<SecondsDouble>(mid - start),
212  Ticks<SecondsDouble>(last - mid),
213  fs::file_size(dump_path));
214  } catch (const std::exception& e) {
215  LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
216  return false;
217  }
218  return true;
219 }
220 
221 } // namespace kernel
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:389
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:304
void PrioritiseTransaction(const uint256 &hash, const CAmount &nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
Definition: txmempool.cpp:877
void AddUnbroadcastTx(const uint256 &txid)
Adds a transaction to the unbroadcast set.
Definition: txmempool.h:694
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:391
CTransactionRef get(const uint256 &hash) const
Definition: txmempool.cpp:848
const Options m_opts
Definition: txmempool.h:440
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:827
std::set< uint256 > GetUnbroadcastTxs() const
Returns transactions in unbroadcast set.
Definition: txmempool.h:706
bool exists(const GenTxid &gtxid) const
Definition: txmempool.h:666
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:490
ChainstateManager & m_chainman
The chainstate manager that owns this chainstate.
Definition: validation.h:530
const util::SignalInterrupt & m_interrupt
Definition: validation.h:966
Fast randomness source.
Definition: random.h:145
void fillrand(Span< std::byte > output)
Fill a byte Span with random bytes.
Definition: random.cpp:680
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:434
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
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:248
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:107
#define LogInfo(...)
Definition: logging.h:239
std::function< FILE *(const fs::path &, const char *)> FopenFn
Definition: fs.h:209
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
static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:195
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:21
std::chrono::seconds expiry
#define LOCK(cs)
Definition: sync.h:257
static int count
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:54
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(
Try to add a transaction to the mempool.