Bitcoin ABC  0.29.3
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 <fs.h>
10 #include <logging.h>
11 #include <primitives/transaction.h>
12 #include <serialize.h>
13 #include <shutdown.h>
14 #include <streams.h>
15 #include <sync.h>
16 #include <txmempool.h>
17 #include <uint256.h>
18 #include <util/system.h>
19 #include <util/time.h>
20 #include <validation.h>
21 
22 #include <chrono>
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 static const uint64_t MEMPOOL_DUMP_VERSION = 1;
38 
39 bool LoadMempool(CTxMemPool &pool, const fs::path &load_path,
40  Chainstate &active_chainstate,
41  FopenFn mockable_fopen_function) {
42  if (load_path.empty()) {
43  return false;
44  }
45 
46  FILE *filestr{mockable_fopen_function(load_path, "rb")};
47  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
48  if (file.IsNull()) {
49  LogPrintf(
50  "Failed to open mempool file from disk. Continuing anyway.\n");
51  return false;
52  }
53 
54  int64_t count = 0;
55  int64_t expired = 0;
56  int64_t failed = 0;
57  int64_t already_there = 0;
58  int64_t unbroadcast = 0;
59  auto now = NodeClock::now();
60 
61  try {
62  uint64_t version;
63  file >> version;
64  if (version != MEMPOOL_DUMP_VERSION) {
65  return false;
66  }
67 
68  uint64_t num;
69  file >> num;
70  while (num) {
71  --num;
72  CTransactionRef tx;
73  int64_t nTime;
74  int64_t nFeeDelta;
75  file >> tx;
76  file >> nTime;
77  file >> nFeeDelta;
78 
79  Amount amountdelta = nFeeDelta * SATOSHI;
80  if (amountdelta != Amount::zero()) {
81  pool.PrioritiseTransaction(tx->GetId(), amountdelta);
82  }
83  if (nTime >
84  TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
85  LOCK(cs_main);
86  const auto &accepted =
87  AcceptToMemoryPool(active_chainstate, tx, nTime,
88  /*bypass_limits=*/false,
89  /*test_accept=*/false);
90  if (accepted.m_result_type ==
92  ++count;
93  } else {
94  // mempool may contain the transaction already, e.g. from
95  // wallet(s) having loaded it while we were processing
96  // mempool transactions; consider these as valid, instead of
97  // failed, but mark them as 'already there'
98  if (pool.exists(tx->GetId())) {
99  ++already_there;
100  } else {
101  ++failed;
102  }
103  }
104  } else {
105  ++expired;
106  }
107 
108  if (ShutdownRequested()) {
109  return false;
110  }
111  }
112  std::map<TxId, Amount> mapDeltas;
113  file >> mapDeltas;
114 
115  for (const auto &i : mapDeltas) {
116  pool.PrioritiseTransaction(i.first, i.second);
117  }
118 
119  std::set<TxId> unbroadcast_txids;
120  file >> unbroadcast_txids;
121  unbroadcast = unbroadcast_txids.size();
122  for (const auto &txid : unbroadcast_txids) {
123  // Ensure transactions were accepted to mempool then add to
124  // unbroadcast set.
125  if (pool.get(txid) != nullptr) {
126  pool.AddUnbroadcastTx(txid);
127  }
128  }
129  } catch (const std::exception &e) {
130  LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing "
131  "anyway.\n",
132  e.what());
133  return false;
134  }
135 
136  LogPrintf("Imported mempool transactions from disk: %i succeeded, %i "
137  "failed, %i expired, %i already there, %i waiting for initial "
138  "broadcast\n",
139  count, failed, expired, already_there, unbroadcast);
140  return true;
141 }
142 
143 bool DumpMempool(const CTxMemPool &pool, const fs::path &dump_path,
144  FopenFn mockable_fopen_function, bool skip_file_commit) {
145  auto start = SteadyClock::now();
146 
147  std::map<uint256, Amount> mapDeltas;
148  std::vector<TxMempoolInfo> vinfo;
149  std::set<TxId> unbroadcast_txids;
150 
151  static Mutex dump_mutex;
152  LOCK(dump_mutex);
153 
154  {
155  LOCK(pool.cs);
156  for (const auto &i : pool.mapDeltas) {
157  mapDeltas[i.first] = i.second;
158  }
159 
160  vinfo = pool.infoAll();
161  unbroadcast_txids = pool.GetUnbroadcastTxs();
162  }
163 
164  auto mid = SteadyClock::now();
165 
166  try {
167  FILE *filestr{mockable_fopen_function(dump_path + ".new", "wb")};
168  if (!filestr) {
169  return false;
170  }
171 
172  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
173 
174  uint64_t version = MEMPOOL_DUMP_VERSION;
175  file << version;
176 
177  file << uint64_t(vinfo.size());
178  for (const auto &i : vinfo) {
179  file << *(i.tx);
180  file << int64_t(count_seconds(i.m_time));
181  file << i.nFeeDelta;
182  mapDeltas.erase(i.tx->GetId());
183  }
184 
185  file << mapDeltas;
186 
187  LogPrintf("Writing %d unbroadcast transactions to disk.\n",
188  unbroadcast_txids.size());
189  file << unbroadcast_txids;
190 
191  if (!skip_file_commit && !FileCommit(file.Get())) {
192  throw std::runtime_error("FileCommit failed");
193  }
194  file.fclose();
195  if (!RenameOver(dump_path + ".new", dump_path)) {
196  throw std::runtime_error("Rename failed");
197  }
198  auto last = SteadyClock::now();
199 
200  LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
201  Ticks<SecondsDouble>(mid - start),
202  Ticks<SecondsDouble>(last - mid));
203  } catch (const std::exception &e) {
204  LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
205  return false;
206  }
207  return true;
208 }
209 
210 } // namespace kernel
static constexpr Amount SATOSHI
Definition: amount.h:143
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
Definition: streams.h:570
FILE * Get() const
Get wrapped FILE* without transfer of ownership.
Definition: streams.h:567
int fclose()
Definition: streams.h:541
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:209
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:296
const std::chrono::seconds m_expiry
Definition: txmempool.h:334
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:490
bool exists(const TxId &txid) const
Definition: txmempool.h:492
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:504
void PrioritiseTransaction(const TxId &txid, const Amount nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
Definition: txmempool.cpp:534
std::set< TxId > GetUnbroadcastTxs() const
Returns transactions in unbroadcast set.
Definition: txmempool.h:529
void AddUnbroadcastTx(const TxId &txid)
Adds a transaction to the unbroadcast set.
Definition: txmempool.h:516
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:629
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
#define LogPrintf(...)
Definition: logging.h:206
std::function< FILE *(const fs::path &, const char *)> FopenFn
Definition: fs.h:198
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
bool LoadMempool(CTxMemPool &pool, const fs::path &load_path, Chainstate &active_chainstate, FopenFn mockable_fopen_function)
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
@ SER_DISK
Definition: serialize.h:153
bool ShutdownRequested()
Returns true if a shutdown is requested, false otherwise.
Definition: shutdown.cpp:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
@ VALID
Fully validated, valid.
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:71
#define LOCK(cs)
Definition: sync.h:306
bool RenameOver(fs::path src, fs::path dest)
Definition: system.cpp:1202
bool FileCommit(FILE *file)
Definition: system.cpp:1231
static int count
Definition: tests.c:31
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:55
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.