Bitcoin Core  26.1.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 <serialize.h>
12 #include <streams.h>
13 #include <sync.h>
14 #include <txmempool.h>
15 #include <uint256.h>
16 #include <util/fs.h>
17 #include <util/fs_helpers.h>
18 #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 bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
40 {
41  if (load_path.empty()) return false;
42 
43  FILE* filestr{opts.mockable_fopen_function(load_path, "rb")};
44  CAutoFile file{filestr, CLIENT_VERSION};
45  if (file.IsNull()) {
46  LogPrintf("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  uint64_t total_txns_to_load;
64  file >> total_txns_to_load;
65  uint64_t txns_tried = 0;
66  LogPrintf("Loading %u mempool transactions from disk...\n", total_txns_to_load);
67  int next_tenth_to_report = 0;
68  while (txns_tried < total_txns_to_load) {
69  const int percentage_done(100.0 * txns_tried / total_txns_to_load);
70  if (next_tenth_to_report < percentage_done / 10) {
71  LogPrintf("Progress loading mempool transactions from disk: %d%% (tried %u, %u remaining)\n",
72  percentage_done, txns_tried, total_txns_to_load - txns_tried);
73  next_tenth_to_report = percentage_done / 10;
74  }
75  ++txns_tried;
76 
77  CTransactionRef tx;
78  int64_t nTime;
79  int64_t nFeeDelta;
80  file >> tx;
81  file >> nTime;
82  file >> nFeeDelta;
83 
84  if (opts.use_current_time) {
85  nTime = TicksSinceEpoch<std::chrono::seconds>(now);
86  }
87 
88  CAmount amountdelta = nFeeDelta;
89  if (amountdelta && opts.apply_fee_delta_priority) {
90  pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
91  }
92  if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
93  LOCK(cs_main);
94  const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
95  if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
96  ++count;
97  } else {
98  // mempool may contain the transaction already, e.g. from
99  // wallet(s) having loaded it while we were processing
100  // mempool transactions; consider these as valid, instead of
101  // failed, but mark them as 'already there'
102  if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
103  ++already_there;
104  } else {
105  ++failed;
106  }
107  }
108  } else {
109  ++expired;
110  }
111  if (active_chainstate.m_chainman.m_interrupt)
112  return false;
113  }
114  std::map<uint256, CAmount> mapDeltas;
115  file >> mapDeltas;
116 
117  if (opts.apply_fee_delta_priority) {
118  for (const auto& i : mapDeltas) {
119  pool.PrioritiseTransaction(i.first, i.second);
120  }
121  }
122 
123  std::set<uint256> unbroadcast_txids;
124  file >> unbroadcast_txids;
125  if (opts.apply_unbroadcast_set) {
126  unbroadcast = unbroadcast_txids.size();
127  for (const auto& txid : unbroadcast_txids) {
128  // Ensure transactions were accepted to mempool then add to
129  // unbroadcast set.
130  if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
131  }
132  }
133  } catch (const std::exception& e) {
134  LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
135  return false;
136  }
137 
138  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);
139  return true;
140 }
141 
142 bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
143 {
144  auto start = SteadyClock::now();
145 
146  std::map<uint256, CAmount> mapDeltas;
147  std::vector<TxMempoolInfo> vinfo;
148  std::set<uint256> unbroadcast_txids;
149 
150  static Mutex dump_mutex;
151  LOCK(dump_mutex);
152 
153  {
154  LOCK(pool.cs);
155  for (const auto &i : pool.mapDeltas) {
156  mapDeltas[i.first] = i.second;
157  }
158  vinfo = pool.infoAll();
159  unbroadcast_txids = pool.GetUnbroadcastTxs();
160  }
161 
162  auto mid = SteadyClock::now();
163 
164  try {
165  FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")};
166  if (!filestr) {
167  return false;
168  }
169 
170  CAutoFile file{filestr, CLIENT_VERSION};
171 
172  uint64_t version = MEMPOOL_DUMP_VERSION;
173  file << version;
174 
175  file << (uint64_t)vinfo.size();
176  for (const auto& i : vinfo) {
177  file << *(i.tx);
178  file << int64_t{count_seconds(i.m_time)};
179  file << int64_t{i.nFeeDelta};
180  mapDeltas.erase(i.tx->GetHash());
181  }
182 
183  file << mapDeltas;
184 
185  LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
186  file << unbroadcast_txids;
187 
188  if (!skip_file_commit && !FileCommit(file.Get()))
189  throw std::runtime_error("FileCommit failed");
190  file.fclose();
191  if (!RenameOver(dump_path + ".new", dump_path)) {
192  throw std::runtime_error("Rename failed");
193  }
194  auto last = SteadyClock::now();
195 
196  LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
197  Ticks<SecondsDouble>(mid - start),
198  Ticks<SecondsDouble>(last - mid));
199  } catch (const std::exception& e) {
200  LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
201  return false;
202  }
203  return true;
204 }
205 
206 } // namespace kernel
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:421
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:851
bool exists(const GenTxid &gtxid) const
Definition: txmempool.h:678
bool DumpMempool(const CTxMemPool &pool, const fs::path &dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
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.
const util::SignalInterrupt & m_interrupt
Definition: validation.h:936
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:262
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
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.
std::function< FILE *(const fs::path &, const char *)> FopenFn
Definition: fs.h:207
ChainstateManager & m_chainman
The chainstate manager that owns this chainstate.
Definition: validation.h:510
void AddUnbroadcastTx(const uint256 &txid)
Adds a transaction to the unbroadcast set.
Definition: txmempool.h:704
Chainstate stores and provides an API to update our local knowledge of the current best chain...
Definition: validation.h:469
const std::chrono::seconds m_expiry
Definition: txmempool.h:441
#define LOCK(cs)
Definition: sync.h:258
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:54
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:70
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:301
std::set< uint256 > GetUnbroadcastTxs() const
Returns transactions in unbroadcast set.
Definition: txmempool.h:716
static int count
CTransactionRef get(const uint256 &hash) const
Definition: txmempool.cpp:865
static const uint64_t MEMPOOL_DUMP_VERSION
#define LogPrintf(...)
Definition: logging.h:237
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:121
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.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
void PrioritiseTransaction(const uint256 &hash, const CAmount &nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
Definition: txmempool.cpp:894
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:432
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it...
Definition: txmempool.h:391