Bitcoin Core  29.1.0
P2P Digital Currency
package_eval.cpp
Go to the documentation of this file.
1 // Copyright (c) 2023 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 <consensus/validation.h>
6 #include <node/context.h>
7 #include <node/mempool_args.h>
8 #include <node/miner.h>
9 #include <policy/truc_policy.h>
11 #include <test/fuzz/fuzz.h>
12 #include <test/fuzz/util.h>
13 #include <test/fuzz/util/mempool.h>
14 #include <test/util/mining.h>
15 #include <test/util/script.h>
16 #include <test/util/setup_common.h>
17 #include <test/util/txmempool.h>
18 #include <util/check.h>
19 #include <util/rbf.h>
20 #include <util/translation.h>
21 #include <validation.h>
22 #include <validationinterface.h>
23 
25 using node::NodeContext;
26 
27 namespace {
28 
29 const TestingSetup* g_setup;
30 std::vector<COutPoint> g_outpoints_coinbase_init_mature;
31 
32 struct MockedTxPool : public CTxMemPool {
33  void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)
34  {
35  LOCK(cs);
36  lastRollingFeeUpdate = GetTime();
37  blockSinceLastRollingFeeBump = true;
38  }
39 };
40 
41 void initialize_tx_pool()
42 {
43  static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
44  g_setup = testing_setup.get();
45 
46  BlockAssembler::Options options;
47  options.coinbase_output_script = P2WSH_EMPTY;
48 
49  for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
50  COutPoint prevout{MineBlock(g_setup->m_node, options)};
51  if (i < COINBASE_MATURITY) {
52  // Remember the txids to avoid expensive disk access later on
53  g_outpoints_coinbase_init_mature.push_back(prevout);
54  }
55  }
56  g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
57 }
58 
59 struct OutpointsUpdater final : public CValidationInterface {
60  std::set<COutPoint>& m_mempool_outpoints;
61 
62  explicit OutpointsUpdater(std::set<COutPoint>& r)
63  : m_mempool_outpoints{r} {}
64 
65  void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override
66  {
67  // for coins spent we always want to be able to rbf so they're not removed
68 
69  // outputs from this tx can now be spent
70  for (uint32_t index{0}; index < tx.info.m_tx->vout.size(); ++index) {
71  m_mempool_outpoints.insert(COutPoint{tx.info.m_tx->GetHash(), index});
72  }
73  }
74 
75  void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
76  {
77  // outpoints spent by this tx are now available
78  for (const auto& input : tx->vin) {
79  // Could already exist if this was a replacement
80  m_mempool_outpoints.insert(input.prevout);
81  }
82  // outpoints created by this tx no longer exist
83  for (uint32_t index{0}; index < tx->vout.size(); ++index) {
84  m_mempool_outpoints.erase(COutPoint{tx->GetHash(), index});
85  }
86  }
87 };
88 
89 struct TransactionsDelta final : public CValidationInterface {
90  std::set<CTransactionRef>& m_added;
91 
92  explicit TransactionsDelta(std::set<CTransactionRef>& a)
93  : m_added{a} {}
94 
95  void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override
96  {
97  // Transactions may be entered and booted any number of times
98  m_added.insert(tx.info.m_tx);
99  }
100 
101  void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
102  {
103  // Transactions may be entered and booted any number of times
104  m_added.erase(tx);
105  }
106 };
107 
108 void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)
109 {
110  const auto time = ConsumeTime(fuzzed_data_provider,
111  chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
112  std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());
113  SetMockTime(time);
114 }
115 
116 std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
117 {
118  // Take the default options for tests...
120 
121 
122  // ...override specific options for this specific fuzz suite
123  mempool_opts.limits.ancestor_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50);
124  mempool_opts.limits.ancestor_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000;
125  mempool_opts.limits.descendant_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50);
126  mempool_opts.limits.descendant_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000;
127  mempool_opts.max_size_bytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200) * 1'000'000;
128  mempool_opts.expiry = std::chrono::hours{fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)};
129  // Only interested in 2 cases: sigop cost 0 or when single legacy sigop cost is >> 1KvB
130  nBytesPerSigOp = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 1) * 10'000;
131 
132  mempool_opts.check_ratio = 1;
133  mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
134 
135  bilingual_str error;
136  // ...and construct a CTxMemPool from it
137  auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
138  // ... ignore the error since it might be beneficial to fuzz even when the
139  // mempool size is unreasonably small
140  Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
141  return mempool;
142 }
143 
144 std::unique_ptr<CTxMemPool> MakeEphemeralMempool(const NodeContext& node)
145 {
146  // Take the default options for tests...
147  CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
148 
149  mempool_opts.check_ratio = 1;
150 
151  // Require standardness rules otherwise ephemeral dust is no-op
152  mempool_opts.require_standard = true;
153 
154  // And set minrelay to 0 to allow ephemeral parent tx even with non-TRUC
155  mempool_opts.min_relay_feerate = CFeeRate(0);
156 
157  bilingual_str error;
158  // ...and construct a CTxMemPool from it
159  auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
160  Assert(error.empty());
161  return mempool;
162 }
163 
164 // Scan mempool for a tx that has spent dust and return a
165 // prevout of the child that isn't the dusty parent itself.
166 // This is used to double-spend the child out of the mempool,
167 // leaving the parent childless.
168 // This assumes CheckMempoolEphemeralInvariants has passed for tx_pool.
169 std::optional<COutPoint> GetChildEvictingPrevout(const CTxMemPool& tx_pool)
170 {
171  LOCK(tx_pool.cs);
172  for (const auto& tx_info : tx_pool.infoAll()) {
173  const auto& entry = *Assert(tx_pool.GetEntry(tx_info.tx->GetHash()));
174  std::vector<uint32_t> dust_indexes{GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate)};
175  if (!dust_indexes.empty()) {
176  const auto& children = entry.GetMemPoolChildrenConst();
177  if (!children.empty()) {
178  Assert(children.size() == 1);
179  // Find an input that doesn't spend from parent's txid
180  const auto& only_child = children.begin()->get().GetTx();
181  for (const auto& tx_input : only_child.vin) {
182  if (tx_input.prevout.hash != tx_info.tx->GetHash()) {
183  return tx_input.prevout;
184  }
185  }
186  }
187  }
188  }
189 
190  return std::nullopt;
191 }
192 
193 FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
194 {
196  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
197  const auto& node = g_setup->m_node;
198  auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
199 
200  MockTime(fuzzed_data_provider, chainstate);
201 
202  // All RBF-spendable outpoints outside of the unsubmitted package
203  std::set<COutPoint> mempool_outpoints;
204  std::map<COutPoint, CAmount> outpoints_value;
205  for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
206  Assert(mempool_outpoints.insert(outpoint).second);
207  outpoints_value[outpoint] = 50 * COIN;
208  }
209 
210  auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
211  node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
212 
213  auto tx_pool_{MakeEphemeralMempool(node)};
214  MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
215 
216  chainstate.SetMempool(&tx_pool);
217 
218  LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 300)
219  {
220  Assert(!mempool_outpoints.empty());
221 
222  std::vector<CTransactionRef> txs;
223 
224  // Find something we may want to double-spend with two input single tx
225  std::optional<COutPoint> outpoint_to_rbf{fuzzed_data_provider.ConsumeBool() ? GetChildEvictingPrevout(tx_pool) : std::nullopt};
226 
227  // Make small packages
228  const auto num_txs = outpoint_to_rbf ? 1 : (size_t) fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
229 
230  std::set<COutPoint> package_outpoints;
231  while (txs.size() < num_txs) {
232  // Create transaction to add to the mempool
233  txs.emplace_back([&] {
234  CMutableTransaction tx_mut;
236  tx_mut.nLockTime = 0;
237  // Last transaction in a package needs to be a child of parents to get further in validation
238  // so the last transaction to be generated(in a >1 package) must spend all package-made outputs
239  // Note that this test currently only spends package outputs in last transaction.
240  bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
241  const auto num_in = outpoint_to_rbf ? 2 :
242  last_tx ? fuzzed_data_provider.ConsumeIntegralInRange<int>(package_outpoints.size()/2 + 1, package_outpoints.size()) :
243  fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
244  const auto num_out = outpoint_to_rbf ? 1 : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
245 
246  auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
247 
248  Assert((int)outpoints.size() >= num_in && num_in > 0);
249 
250  CAmount amount_in{0};
251  for (int i = 0; i < num_in; ++i) {
252  // Pop random outpoint. We erase them to avoid double-spending
253  // while in this loop, but later add them back (unless last_tx).
254  auto pop = outpoints.begin();
255  std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints.size() - 1));
256  auto outpoint = *pop;
257 
258  if (i == 0 && outpoint_to_rbf) {
259  outpoint = *outpoint_to_rbf;
260  outpoints.erase(outpoint);
261  } else {
262  outpoints.erase(pop);
263  }
264  // no need to update or erase from outpoints_value
265  amount_in += outpoints_value.at(outpoint);
266 
267  // Create input
268  CTxIn in;
269  in.prevout = outpoint;
271 
272  tx_mut.vin.push_back(in);
273  }
274 
275  const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in);
276  const auto amount_out = (amount_in - amount_fee) / num_out;
277  for (int i = 0; i < num_out; ++i) {
278  tx_mut.vout.emplace_back(amount_out, P2WSH_EMPTY);
279  }
280 
281  // Note output amounts can naturally drop to dust on their own.
282  if (!outpoint_to_rbf && fuzzed_data_provider.ConsumeBool()) {
283  uint32_t dust_index = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_out);
284  tx_mut.vout.insert(tx_mut.vout.begin() + dust_index, CTxOut(0, P2WSH_EMPTY));
285  }
286 
287  auto tx = MakeTransactionRef(tx_mut);
288  // Restore previously removed outpoints, except in-package outpoints (to allow RBF)
289  if (!last_tx) {
290  for (const auto& in : tx->vin) {
291  Assert(outpoints.insert(in.prevout).second);
292  }
293  // Cache the in-package outpoints being made
294  for (size_t i = 0; i < tx->vout.size(); ++i) {
295  package_outpoints.emplace(tx->GetHash(), i);
296  }
297  }
298  // We need newly-created values for the duration of this run
299  for (size_t i = 0; i < tx->vout.size(); ++i) {
300  outpoints_value[COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
301  }
302  return tx;
303  }());
304  }
305 
306  if (fuzzed_data_provider.ConsumeBool()) {
307  const auto& txid = fuzzed_data_provider.ConsumeBool() ?
308  txs.back()->GetHash() :
309  PickValue(fuzzed_data_provider, mempool_outpoints).hash;
310  const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
311  // We only prioritise out of mempool transactions since PrioritiseTransaction doesn't
312  // filter for ephemeral dust
313  if (tx_pool.exists(GenTxid::Txid(txid))) {
314  const auto tx_info{tx_pool.info(GenTxid::Txid(txid))};
315  if (GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate).empty()) {
316  tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);
317  }
318  }
319  }
320 
321  auto single_submit = txs.size() == 1;
322 
323  const auto result_package = WITH_LOCK(::cs_main,
324  return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
325 
326  const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
327  /*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit));
328 
329  if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
330  // We don't know anything about the validity since transactions were randomly generated, so
331  // just use result_package.m_state here. This makes the expect_valid check meaningless, but
332  // we can still verify that the contents of m_tx_results are consistent with m_state.
333  const bool expect_valid{result_package.m_state.IsValid()};
334  Assert(!CheckPackageMempoolAcceptResult(txs, result_package, expect_valid, &tx_pool));
335  }
336 
337  node.validation_signals->SyncWithValidationInterfaceQueue();
338 
340  }
341 
342  node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
343 
344  WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
345 }
346 
347 
348 FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
349 {
351  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
352  const auto& node = g_setup->m_node;
353  auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
354 
355  MockTime(fuzzed_data_provider, chainstate);
356 
357  // All RBF-spendable outpoints outside of the unsubmitted package
358  std::set<COutPoint> mempool_outpoints;
359  std::map<COutPoint, CAmount> outpoints_value;
360  for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
361  Assert(mempool_outpoints.insert(outpoint).second);
362  outpoints_value[outpoint] = 50 * COIN;
363  }
364 
365  auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
366  node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
367 
368  auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
369  MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
370 
371  chainstate.SetMempool(&tx_pool);
372 
373  LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 300)
374  {
375  Assert(!mempool_outpoints.empty());
376 
377  std::vector<CTransactionRef> txs;
378 
379  // Make packages of 1-to-26 transactions
380  const auto num_txs = (size_t) fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 26);
381  std::set<COutPoint> package_outpoints;
382  while (txs.size() < num_txs) {
383  // Create transaction to add to the mempool
384  txs.emplace_back([&] {
385  CMutableTransaction tx_mut;
386  tx_mut.version = fuzzed_data_provider.ConsumeBool() ? TRUC_VERSION : CTransaction::CURRENT_VERSION;
387  tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
388  // Last transaction in a package needs to be a child of parents to get further in validation
389  // so the last transaction to be generated(in a >1 package) must spend all package-made outputs
390  // Note that this test currently only spends package outputs in last transaction.
391  bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
392  const auto num_in = last_tx ? package_outpoints.size() : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size());
393  auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size() * 2);
394 
395  auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
396 
397  Assert(!outpoints.empty());
398 
399  CAmount amount_in{0};
400  for (size_t i = 0; i < num_in; ++i) {
401  // Pop random outpoint. We erase them to avoid double-spending
402  // while in this loop, but later add them back (unless last_tx).
403  auto pop = outpoints.begin();
404  std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints.size() - 1));
405  const auto outpoint = *pop;
406  outpoints.erase(pop);
407  // no need to update or erase from outpoints_value
408  amount_in += outpoints_value.at(outpoint);
409 
410  // Create input
411  const auto sequence = ConsumeSequence(fuzzed_data_provider);
412  const auto script_sig = CScript{};
413  const auto script_wit_stack = fuzzed_data_provider.ConsumeBool() ? P2WSH_EMPTY_TRUE_STACK : P2WSH_EMPTY_TWO_STACK;
414 
415  CTxIn in;
416  in.prevout = outpoint;
417  in.nSequence = sequence;
418  in.scriptSig = script_sig;
419  in.scriptWitness.stack = script_wit_stack;
420 
421  tx_mut.vin.push_back(in);
422  }
423 
424  // Duplicate an input
425  bool dup_input = fuzzed_data_provider.ConsumeBool();
426  if (dup_input) {
427  tx_mut.vin.push_back(tx_mut.vin.back());
428  }
429 
430  // Refer to a non-existent input
431  if (fuzzed_data_provider.ConsumeBool()) {
432  tx_mut.vin.emplace_back();
433  }
434 
435  // Make a p2pk output to make sigops adjusted vsize to violate TRUC rules, potentially, which is never spent
436  if (last_tx && amount_in > 1000 && fuzzed_data_provider.ConsumeBool()) {
437  tx_mut.vout.emplace_back(1000, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG);
438  // Don't add any other outputs.
439  num_out = 1;
440  amount_in -= 1000;
441  }
442 
443  const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in);
444  const auto amount_out = (amount_in - amount_fee) / num_out;
445  for (int i = 0; i < num_out; ++i) {
446  tx_mut.vout.emplace_back(amount_out, P2WSH_EMPTY);
447  }
448  auto tx = MakeTransactionRef(tx_mut);
449  // Restore previously removed outpoints, except in-package outpoints
450  if (!last_tx) {
451  for (const auto& in : tx->vin) {
452  // It's a fake input, or a new input, or a duplicate
453  Assert(in == CTxIn() || outpoints.insert(in.prevout).second || dup_input);
454  }
455  // Cache the in-package outpoints being made
456  for (size_t i = 0; i < tx->vout.size(); ++i) {
457  package_outpoints.emplace(tx->GetHash(), i);
458  }
459  }
460  // We need newly-created values for the duration of this run
461  for (size_t i = 0; i < tx->vout.size(); ++i) {
462  outpoints_value[COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
463  }
464  return tx;
465  }());
466  }
467 
468  if (fuzzed_data_provider.ConsumeBool()) {
469  MockTime(fuzzed_data_provider, chainstate);
470  }
471  if (fuzzed_data_provider.ConsumeBool()) {
472  tx_pool.RollingFeeUpdate();
473  }
474  if (fuzzed_data_provider.ConsumeBool()) {
475  const auto& txid = fuzzed_data_provider.ConsumeBool() ?
476  txs.back()->GetHash() :
477  PickValue(fuzzed_data_provider, mempool_outpoints).hash;
478  const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
479  tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);
480  }
481 
482  // Remember all added transactions
483  std::set<CTransactionRef> added;
484  auto txr = std::make_shared<TransactionsDelta>(added);
485  node.validation_signals->RegisterSharedValidationInterface(txr);
486 
487  // When there are multiple transactions in the package, we call ProcessNewPackage(txs, test_accept=false)
488  // and AcceptToMemoryPool(txs.back(), test_accept=true). When there is only 1 transaction, we might flip it
489  // (the package is a test accept and ATMP is a submission).
490  auto single_submit = txs.size() == 1 && fuzzed_data_provider.ConsumeBool();
491 
492  // Exercise client_maxfeerate logic
493  std::optional<CFeeRate> client_maxfeerate{};
494  if (fuzzed_data_provider.ConsumeBool()) {
495  client_maxfeerate = CFeeRate(fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1, 50 * COIN), 100);
496  }
497 
498  const auto result_package = WITH_LOCK(::cs_main,
499  return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, client_maxfeerate));
500 
501  // Always set bypass_limits to false because it is not supported in ProcessNewPackage and
502  // can be a source of divergence.
503  const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
504  /*bypass_limits=*/false, /*test_accept=*/!single_submit));
505  const bool passed = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
506 
507  node.validation_signals->SyncWithValidationInterfaceQueue();
508  node.validation_signals->UnregisterSharedValidationInterface(txr);
509 
510  // There is only 1 transaction in the package. We did a test-package-accept and a ATMP
511  if (single_submit) {
512  Assert(passed != added.empty());
513  Assert(passed == res.m_state.IsValid());
514  if (passed) {
515  Assert(added.size() == 1);
516  Assert(txs.back() == *added.begin());
517  }
518  } else if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
519  // We don't know anything about the validity since transactions were randomly generated, so
520  // just use result_package.m_state here. This makes the expect_valid check meaningless, but
521  // we can still verify that the contents of m_tx_results are consistent with m_state.
522  const bool expect_valid{result_package.m_state.IsValid()};
523  Assert(!CheckPackageMempoolAcceptResult(txs, result_package, expect_valid, &tx_pool));
524  } else {
525  // This is empty if it fails early checks, or "full" if transactions are looked at deeper
526  Assert(result_package.m_tx_results.size() == txs.size() || result_package.m_tx_results.empty());
527  }
528 
530 
531  // Dust checks only make sense when dust is enforced
532  if (tx_pool.m_opts.require_standard) {
534  }
535  }
536 
537  node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
538 
539  WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
540 }
541 } // namespace
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:611
virtual void TransactionRemovedFromMempool(const CTransactionRef &tx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
Notifies listeners of a transaction leaving mempool.
Generate a new block, without valid proof-of-work.
Definition: miner.h:143
std::vector< TxMempoolInfo > infoAll() const
Definition: txmempool.cpp:863
The package itself is invalid (e.g. too many transactions).
std::vector< CTxIn > vin
Definition: transaction.h:379
CScriptWitness scriptWitness
Only serialized through CTransaction.
Definition: transaction.h:72
MemPoolRemovalReason
Reason why a transaction was removed from the mempool, this is passed to the notification signal...
int Height() const
Return the maximal height in the chain.
Definition: chain.h:462
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule) ...
Definition: consensus.h:19
std::vector< std::vector< unsigned char > > stack
Definition: script.h:588
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
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.
uint32_t nTime
Definition: chain.h:189
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:585
virtual void TransactionAddedToMempool(const NewMempoolTransactionInfo &tx, uint64_t mempool_sequence)
Notifies listeners of a transaction having been added to mempool.
Implement this to subscribe to events generated in validation and mempool.
static const std::vector< std::vector< uint8_t > > P2WSH_EMPTY_TWO_STACK
Definition: script.h:31
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:40
static const uint32_t CURRENT_VERSION
Definition: transaction.h:299
NodeContext struct containing references to chain state and connection state.
Definition: context.h:56
static decltype(CTransaction::version) constexpr TRUC_VERSION
Definition: truc_policy.h:20
Chainstate stores and provides an API to update our local knowledge of the current best chain...
Definition: validation.h:504
static void pool cs
An input of a transaction.
Definition: transaction.h:66
COutPoint MineBlock(const NodeContext &node, const node::BlockAssembler::Options &assembler_options)
Returns the generated coin.
Definition: mining.cpp:64
#define LOCK(cs)
Definition: sync.h:257
An output of a transaction.
Definition: transaction.h:149
std::optional< std::string > CheckPackageMempoolAcceptResult(const Package &txns, const PackageMempoolAcceptResult &result, bool expect_valid, const CTxMemPool *mempool)
Check expected properties for every PackageMempoolAcceptResult, regardless of value.
Definition: txmempool.cpp:43
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:28
std::vector< CTxOut > vout
Definition: transaction.h:380
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:301
int64_t GetMedianTimePast() const
Definition: chain.h:278
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
Definition: messages.h:20
std::vector< uint32_t > GetDust(const CTransaction &tx, CFeeRate dust_relay_rate)
Get the vout index numbers of all dust outputs.
Definition: policy.cpp:70
CScript scriptSig
Definition: transaction.h:70
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
const CTxMemPoolEntry * GetEntry(const Txid &txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:877
static const CScript P2WSH_EMPTY
Definition: script.h:22
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
void CheckMempoolTRUCInvariants(const CTxMemPool &tx_pool)
For every transaction in tx_pool, check TRUC invariants:
Definition: txmempool.cpp:181
unsigned int nBytesPerSigOp
Definition: settings.cpp:10
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:303
static const std::vector< std::vector< uint8_t > > P2WSH_EMPTY_TRUE_STACK
Definition: script.h:30
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:414
uint32_t nSequence
Definition: transaction.h:71
PackageMempoolAcceptResult ProcessNewPackage(Chainstate &active_chainstate, CTxMemPool &pool, const Package &package, bool test_accept, const std::optional< CFeeRate > &client_maxfeerate)
Validate (and maybe submit) a package to the mempool.
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:433
const CTransactionRef m_tx
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:32
A mutable version of CTransaction.
Definition: transaction.h:377
Options struct containing options for constructing a CTxMemPool.
void CheckMempoolEphemeralInvariants(const CTxMemPool &tx_pool)
Check that we never get into a state where an ephemeral dust transaction would be mined without the s...
Definition: txmempool.cpp:144
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition: util.h:47
int64_t ancestor_count
The maximum allowed number of transactions in a package including the entry and its ancestors...
T ConsumeIntegralInRange(T min, T max)
uint64_t sequence
#define FUZZ_TARGET(...)
Definition: fuzz.h:35
Seed with a compile time constant of zeros.
int64_t GetTime()
DEPRECATED, see GetTime.
Definition: time.cpp:76
COutPoint prevout
Definition: transaction.h:69
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
Testing setup that configures a complete environment.
Definition: setup_common.h:121
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:434
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it...
Definition: txmempool.h:390
#define Assert(val)
Identity function.
Definition: check.h:85
uint32_t ConsumeSequence(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:155
const Options m_opts
Definition: txmempool.h:439
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node)
Definition: txmempool.cpp:20
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15