Bitcoin Core  22.99.0
P2P Digital Currency
txpackage_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021 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 <key_io.h>
7 #include <policy/packages.h>
8 #include <policy/policy.h>
10 #include <script/script.h>
11 #include <script/standard.h>
12 #include <test/util/setup_common.h>
13 #include <validation.h>
14 
15 #include <boost/test/unit_test.hpp>
16 
17 BOOST_AUTO_TEST_SUITE(txpackage_tests)
18 
19 // Create placeholder transactions that have no meaning.
20 inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
21 {
23  mtx.vin.resize(num_inputs);
24  mtx.vout.resize(num_outputs);
25  auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256());
26  for (size_t i{0}; i < num_inputs; ++i) {
27  mtx.vin[i].prevout.hash = InsecureRand256();
28  mtx.vin[i].prevout.n = 0;
29  mtx.vin[i].scriptSig = random_script;
30  }
31  for (size_t o{0}; o < num_outputs; ++o) {
32  mtx.vout[o].nValue = 1 * CENT;
33  mtx.vout[o].scriptPubKey = random_script;
34  }
35  return MakeTransactionRef(mtx);
36 }
37 
38 BOOST_FIXTURE_TEST_CASE(package_sanitization_tests, TestChain100Setup)
39 {
40  // Packages can't have more than 25 transactions.
41  Package package_too_many;
42  package_too_many.reserve(MAX_PACKAGE_COUNT + 1);
43  for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {
44  package_too_many.emplace_back(create_placeholder_tx(1, 1));
45  }
46  PackageValidationState state_too_many;
47  BOOST_CHECK(!CheckPackage(package_too_many, state_too_many));
49  BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");
50 
51  // Packages can't have a total size of more than 101KvB.
52  CTransactionRef large_ptx = create_placeholder_tx(150, 150);
53  Package package_too_large;
54  auto size_large = GetVirtualTransactionSize(*large_ptx);
55  size_t total_size{0};
56  while (total_size <= MAX_PACKAGE_SIZE * 1000) {
57  package_too_large.push_back(large_ptx);
58  total_size += size_large;
59  }
60  BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
61  PackageValidationState state_too_large;
62  BOOST_CHECK(!CheckPackage(package_too_large, state_too_large));
64  BOOST_CHECK_EQUAL(state_too_large.GetRejectReason(), "package-too-large");
65 }
66 
67 BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
68 {
69  LOCK(cs_main);
70  unsigned int initialPoolSize = m_node.mempool->size();
71 
72  // Parent and Child Package
73  CKey parent_key;
74  parent_key.MakeNewKey(true);
75  CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
76  auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[0], /*input_vout=*/0,
77  /*input_height=*/ 0, /*input_signing_key=*/coinbaseKey,
78  /*output_destination=*/ parent_locking_script,
79  /*output_amount=*/ CAmount(49 * COIN), /*submit=*/false);
80  CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
81 
82  CKey child_key;
83  child_key.MakeNewKey(true);
84  CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
85  auto mtx_child = CreateValidMempoolTransaction(/*input_transaction=*/ tx_parent, /*input_vout=*/0,
86  /*input_height=*/ 101, /*input_signing_key=*/parent_key,
87  /*output_destination=*/child_locking_script,
88  /*output_amount=*/ CAmount(48 * COIN), /*submit=*/false);
89  CTransactionRef tx_child = MakeTransactionRef(mtx_child);
90  const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /*test_accept=*/true);
91  BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(),
92  "Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason());
93  auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
94  auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
95  BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end());
96  BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(),
97  "Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason());
98  BOOST_CHECK(it_child != result_parent_child.m_tx_results.end());
99  BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(),
100  "Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason());
101 
102 
103  // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
104  CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
106  auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /*test_accept=*/true);
107  BOOST_CHECK(result_single_large.m_state.IsInvalid());
108  BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
109  BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
110  auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash());
111  BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end());
112  BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size");
113 
114  // Check that mempool size hasn't changed.
115  BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
116 }
117 
118 BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup)
119 {
120  // The signatures won't be verified so we can just use a placeholder
121  CKey placeholder_key;
122  placeholder_key.MakeNewKey(true);
123  CScript spk = GetScriptForDestination(PKHash(placeholder_key.GetPubKey()));
124  CKey placeholder_key_2;
125  placeholder_key_2.MakeNewKey(true);
126  CScript spk2 = GetScriptForDestination(PKHash(placeholder_key_2.GetPubKey()));
127 
128  // Parent and Child Package
129  {
130  auto mtx_parent = CreateValidMempoolTransaction(m_coinbase_txns[0], 0, 0, coinbaseKey, spk,
131  CAmount(49 * COIN), /* submit */ false);
132  CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
133 
134  auto mtx_child = CreateValidMempoolTransaction(tx_parent, 0, 101, placeholder_key, spk2,
135  CAmount(48 * COIN), /* submit */ false);
136  CTransactionRef tx_child = MakeTransactionRef(mtx_child);
137 
139  BOOST_CHECK(CheckPackage({tx_parent, tx_child}, state));
140  BOOST_CHECK(!CheckPackage({tx_child, tx_parent}, state));
142  BOOST_CHECK_EQUAL(state.GetRejectReason(), "package-not-sorted");
143  BOOST_CHECK(IsChildWithParents({tx_parent, tx_child}));
144  }
145 
146  // 24 Parents and 1 Child
147  {
148  Package package;
149  CMutableTransaction child;
150  for (int i{0}; i < 24; ++i) {
151  auto parent = MakeTransactionRef(CreateValidMempoolTransaction(m_coinbase_txns[i + 1],
152  0, 0, coinbaseKey, spk, CAmount(48 * COIN), false));
153  package.emplace_back(parent);
154  child.vin.push_back(CTxIn(COutPoint(parent->GetHash(), 0)));
155  }
156  child.vout.push_back(CTxOut(47 * COIN, spk2));
157 
158  // The child must be in the package.
159  BOOST_CHECK(!IsChildWithParents(package));
160 
161  // The parents can be in any order.
162  FastRandomContext rng;
163  Shuffle(package.begin(), package.end(), rng);
164  package.push_back(MakeTransactionRef(child));
165 
167  BOOST_CHECK(CheckPackage(package, state));
169 
170  package.erase(package.begin());
172 
173  // The package cannot have unrelated transactions.
174  package.insert(package.begin(), m_coinbase_txns[0]);
175  BOOST_CHECK(!IsChildWithParents(package));
176  }
177 
178  // 2 Parents and 1 Child where one parent depends on the other.
179  {
180  CMutableTransaction mtx_parent;
181  mtx_parent.vin.push_back(CTxIn(COutPoint(m_coinbase_txns[0]->GetHash(), 0)));
182  mtx_parent.vout.push_back(CTxOut(20 * COIN, spk));
183  mtx_parent.vout.push_back(CTxOut(20 * COIN, spk2));
184  CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
185 
186  CMutableTransaction mtx_parent_also_child;
187  mtx_parent_also_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 0)));
188  mtx_parent_also_child.vout.push_back(CTxOut(20 * COIN, spk));
189  CTransactionRef tx_parent_also_child = MakeTransactionRef(mtx_parent_also_child);
190 
191  CMutableTransaction mtx_child;
192  mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 1)));
193  mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent_also_child->GetHash(), 0)));
194  mtx_child.vout.push_back(CTxOut(39 * COIN, spk));
195  CTransactionRef tx_child = MakeTransactionRef(mtx_child);
196 
198  BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child}));
199  BOOST_CHECK(IsChildWithParents({tx_parent, tx_child}));
200  BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child, tx_child}));
201  // IsChildWithParents does not detect unsorted parents.
202  BOOST_CHECK(IsChildWithParents({tx_parent_also_child, tx_parent, tx_child}));
203  BOOST_CHECK(CheckPackage({tx_parent, tx_parent_also_child, tx_child}, state));
204  BOOST_CHECK(!CheckPackage({tx_parent_also_child, tx_parent, tx_child}, state));
206  BOOST_CHECK_EQUAL(state.GetRejectReason(), "package-not-sorted");
207  }
208 }
209 
210 BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
211 {
212  LOCK(cs_main);
213  unsigned int expected_pool_size = m_node.mempool->size();
214  CKey parent_key;
215  parent_key.MakeNewKey(true);
216  CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
217 
218  // Unrelated transactions are not allowed in package submission.
219  Package package_unrelated;
220  for (size_t i{0}; i < 10; ++i) {
221  auto mtx = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[i + 25], /* vout */ 0,
222  /* input_height */ 0, /* input_signing_key */ coinbaseKey,
223  /* output_destination */ parent_locking_script,
224  /* output_amount */ CAmount(49 * COIN), /* submit */ false);
225  package_unrelated.emplace_back(MakeTransactionRef(mtx));
226  }
227  auto result_unrelated_submit = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
228  package_unrelated, /* test_accept */ false);
229  BOOST_CHECK(result_unrelated_submit.m_state.IsInvalid());
230  BOOST_CHECK_EQUAL(result_unrelated_submit.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
231  BOOST_CHECK_EQUAL(result_unrelated_submit.m_state.GetRejectReason(), "package-not-child-with-parents");
232  BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
233 
234  // Parent and Child (and Grandchild) Package
235  Package package_parent_child;
236  Package package_3gen;
237  auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0,
238  /* input_height */ 0, /* input_signing_key */ coinbaseKey,
239  /* output_destination */ parent_locking_script,
240  /* output_amount */ CAmount(49 * COIN), /* submit */ false);
241  CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
242  package_parent_child.push_back(tx_parent);
243  package_3gen.push_back(tx_parent);
244 
245  CKey child_key;
246  child_key.MakeNewKey(true);
247  CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
248  auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0,
249  /* input_height */ 101, /* input_signing_key */ parent_key,
250  /* output_destination */ child_locking_script,
251  /* output_amount */ CAmount(48 * COIN), /* submit */ false);
252  CTransactionRef tx_child = MakeTransactionRef(mtx_child);
253  package_parent_child.push_back(tx_child);
254  package_3gen.push_back(tx_child);
255 
256  CKey grandchild_key;
257  grandchild_key.MakeNewKey(true);
258  CScript grandchild_locking_script = GetScriptForDestination(PKHash(grandchild_key.GetPubKey()));
259  auto mtx_grandchild = CreateValidMempoolTransaction(/* input_transaction */ tx_child, /* vout */ 0,
260  /* input_height */ 101, /* input_signing_key */ child_key,
261  /* output_destination */ grandchild_locking_script,
262  /* output_amount */ CAmount(47 * COIN), /* submit */ false);
263  CTransactionRef tx_grandchild = MakeTransactionRef(mtx_grandchild);
264  package_3gen.push_back(tx_grandchild);
265 
266  // 3 Generations is not allowed.
267  {
268  auto result_3gen_submit = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
269  package_3gen, /* test_accept */ false);
270  BOOST_CHECK(result_3gen_submit.m_state.IsInvalid());
271  BOOST_CHECK_EQUAL(result_3gen_submit.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
272  BOOST_CHECK_EQUAL(result_3gen_submit.m_state.GetRejectReason(), "package-not-child-with-parents");
273  BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
274  }
275 
276  // Child with missing parent.
277  mtx_child.vin.push_back(CTxIn(COutPoint(package_unrelated[0]->GetHash(), 0)));
278  Package package_missing_parent;
279  package_missing_parent.push_back(tx_parent);
280  package_missing_parent.push_back(MakeTransactionRef(mtx_child));
281  {
282  const auto result_missing_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
283  package_missing_parent, /* test_accept */ false);
284  BOOST_CHECK(result_missing_parent.m_state.IsInvalid());
285  BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
286  BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetRejectReason(), "package-not-child-with-unconfirmed-parents");
287  BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
288 
289  }
290 
291  // Submit package with parent + child.
292  {
293  const auto submit_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
294  package_parent_child, /* test_accept */ false);
295  expected_pool_size += 2;
296  BOOST_CHECK_MESSAGE(submit_parent_child.m_state.IsValid(),
297  "Package validation unexpectedly failed: " << submit_parent_child.m_state.GetRejectReason());
298  auto it_parent = submit_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
299  auto it_child = submit_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
300  BOOST_CHECK(it_parent != submit_parent_child.m_tx_results.end());
301  BOOST_CHECK(it_parent->second.m_state.IsValid());
302  BOOST_CHECK(it_child != submit_parent_child.m_tx_results.end());
303  BOOST_CHECK(it_child->second.m_state.IsValid());
304 
305  BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
306  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_parent->GetHash())));
307  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_child->GetHash())));
308  }
309 
310  // Already-in-mempool transactions should be detected and de-duplicated.
311  {
312  const auto submit_deduped = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
313  package_parent_child, /* test_accept */ false);
314  BOOST_CHECK_MESSAGE(submit_deduped.m_state.IsValid(),
315  "Package validation unexpectedly failed: " << submit_deduped.m_state.GetRejectReason());
316  auto it_parent_deduped = submit_deduped.m_tx_results.find(tx_parent->GetWitnessHash());
317  auto it_child_deduped = submit_deduped.m_tx_results.find(tx_child->GetWitnessHash());
318  BOOST_CHECK(it_parent_deduped != submit_deduped.m_tx_results.end());
319  BOOST_CHECK(it_parent_deduped->second.m_state.IsValid());
320  BOOST_CHECK(it_parent_deduped->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
321  BOOST_CHECK(it_child_deduped != submit_deduped.m_tx_results.end());
322  BOOST_CHECK(it_child_deduped->second.m_state.IsValid());
323  BOOST_CHECK(it_child_deduped->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
324 
325  BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
326  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_parent->GetHash())));
327  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_child->GetHash())));
328  }
329 }
330 
331 // Tests for packages containing transactions that have same-txid-different-witness equivalents in
332 // the mempool.
333 BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
334 {
335  // Mine blocks to mature coinbases.
336  mineBlocks(5);
337  LOCK(cs_main);
338 
339  // Transactions with a same-txid-different-witness transaction in the mempool should be ignored,
340  // and the mempool entry's wtxid returned.
341  CScript witnessScript = CScript() << OP_DROP << OP_TRUE;
342  CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
343  auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[0], /*vout=*/ 0,
344  /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
345  /*output_destination=*/ scriptPubKey,
346  /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ false);
347  CTransactionRef ptx_parent = MakeTransactionRef(mtx_parent);
348 
349  // Make two children with the same txid but different witnesses.
350  CScriptWitness witness1;
351  witness1.stack.push_back(std::vector<unsigned char>(1));
352  witness1.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
353 
354  CScriptWitness witness2(witness1);
355  witness2.stack.push_back(std::vector<unsigned char>(2));
356  witness2.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
357 
358  CKey child_key;
359  child_key.MakeNewKey(true);
360  CScript child_locking_script = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));
361  CMutableTransaction mtx_child1;
362  mtx_child1.nVersion = 1;
363  mtx_child1.vin.resize(1);
364  mtx_child1.vin[0].prevout.hash = ptx_parent->GetHash();
365  mtx_child1.vin[0].prevout.n = 0;
366  mtx_child1.vin[0].scriptSig = CScript();
367  mtx_child1.vin[0].scriptWitness = witness1;
368  mtx_child1.vout.resize(1);
369  mtx_child1.vout[0].nValue = CAmount(48 * COIN);
370  mtx_child1.vout[0].scriptPubKey = child_locking_script;
371 
372  CMutableTransaction mtx_child2{mtx_child1};
373  mtx_child2.vin[0].scriptWitness = witness2;
374 
375  CTransactionRef ptx_child1 = MakeTransactionRef(mtx_child1);
376  CTransactionRef ptx_child2 = MakeTransactionRef(mtx_child2);
377 
378  // child1 and child2 have the same txid
379  BOOST_CHECK_EQUAL(ptx_child1->GetHash(), ptx_child2->GetHash());
380  // child1 and child2 have different wtxids
381  BOOST_CHECK(ptx_child1->GetWitnessHash() != ptx_child2->GetWitnessHash());
382 
383  // Try submitting Package1{parent, child1} and Package2{parent, child2} where the children are
384  // same-txid-different-witness.
385  {
386  const auto submit_witness1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
387  {ptx_parent, ptx_child1}, /*test_accept=*/ false);
388  BOOST_CHECK_MESSAGE(submit_witness1.m_state.IsValid(),
389  "Package validation unexpectedly failed: " << submit_witness1.m_state.GetRejectReason());
390  auto it_parent1 = submit_witness1.m_tx_results.find(ptx_parent->GetWitnessHash());
391  auto it_child1 = submit_witness1.m_tx_results.find(ptx_child1->GetWitnessHash());
392  BOOST_CHECK(it_parent1 != submit_witness1.m_tx_results.end());
393  BOOST_CHECK_MESSAGE(it_parent1->second.m_state.IsValid(),
394  "Transaction unexpectedly failed: " << it_parent1->second.m_state.GetRejectReason());
395  BOOST_CHECK(it_child1 != submit_witness1.m_tx_results.end());
396  BOOST_CHECK_MESSAGE(it_child1->second.m_state.IsValid(),
397  "Transaction unexpectedly failed: " << it_child1->second.m_state.GetRejectReason());
398 
399  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent->GetHash())));
400  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child1->GetHash())));
401 
402  const auto submit_witness2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
403  {ptx_parent, ptx_child2}, /*test_accept=*/ false);
404  BOOST_CHECK_MESSAGE(submit_witness2.m_state.IsValid(),
405  "Package validation unexpectedly failed: " << submit_witness2.m_state.GetRejectReason());
406  auto it_parent2_deduped = submit_witness2.m_tx_results.find(ptx_parent->GetWitnessHash());
407  auto it_child2 = submit_witness2.m_tx_results.find(ptx_child2->GetWitnessHash());
408  BOOST_CHECK(it_parent2_deduped != submit_witness2.m_tx_results.end());
409  BOOST_CHECK(it_parent2_deduped->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
410  BOOST_CHECK(it_child2 != submit_witness2.m_tx_results.end());
411  BOOST_CHECK(it_child2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
412  BOOST_CHECK_EQUAL(ptx_child1->GetWitnessHash(), it_child2->second.m_other_wtxid.value());
413 
414  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child2->GetHash())));
415  BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_child2->GetWitnessHash())));
416  }
417 
418  // Try submitting Package1{child2, grandchild} where child2 is same-txid-different-witness as
419  // the in-mempool transaction, child1. Since child1 exists in the mempool and its outputs are
420  // available, child2 should be ignored and grandchild should be accepted.
421  //
422  // This tests a potential censorship vector in which an attacker broadcasts a competing package
423  // where a parent's witness is mutated. The honest package should be accepted despite the fact
424  // that we don't allow witness replacement.
425  CKey grandchild_key;
426  grandchild_key.MakeNewKey(true);
427  CScript grandchild_locking_script = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));
428  auto mtx_grandchild = CreateValidMempoolTransaction(/*input_transaction=*/ ptx_child2, /* vout=*/ 0,
429  /*input_height=*/ 0, /*input_signing_key=*/ child_key,
430  /*output_destination=*/ grandchild_locking_script,
431  /*output_amount=*/ CAmount(47 * COIN), /*submit=*/ false);
432  CTransactionRef ptx_grandchild = MakeTransactionRef(mtx_grandchild);
433 
434  // We already submitted child1 above.
435  {
436  const auto submit_spend_ignored = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
437  {ptx_child2, ptx_grandchild}, /*test_accept=*/ false);
438  BOOST_CHECK_MESSAGE(submit_spend_ignored.m_state.IsValid(),
439  "Package validation unexpectedly failed: " << submit_spend_ignored.m_state.GetRejectReason());
440  auto it_child2_ignored = submit_spend_ignored.m_tx_results.find(ptx_child2->GetWitnessHash());
441  auto it_grandchild = submit_spend_ignored.m_tx_results.find(ptx_grandchild->GetWitnessHash());
442  BOOST_CHECK(it_child2_ignored != submit_spend_ignored.m_tx_results.end());
443  BOOST_CHECK(it_child2_ignored->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
444  BOOST_CHECK(it_grandchild != submit_spend_ignored.m_tx_results.end());
445  BOOST_CHECK(it_grandchild->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
446 
447  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child2->GetHash())));
448  BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_child2->GetWitnessHash())));
449  BOOST_CHECK(m_node.mempool->exists(GenTxid::Wtxid(ptx_grandchild->GetWitnessHash())));
450  }
451 
452  // A package Package{parent1, parent2, parent3, child} where the parents are a mixture of
453  // identical-tx-in-mempool, same-txid-different-witness-in-mempool, and new transactions.
454  Package package_mixed;
455 
456  // Give all the parents anyone-can-spend scripts so we don't have to deal with signing the child.
457  CScript acs_script = CScript() << OP_TRUE;
458  CScript acs_spk = GetScriptForDestination(WitnessV0ScriptHash(acs_script));
459  CScriptWitness acs_witness;
460  acs_witness.stack.push_back(std::vector<unsigned char>(acs_script.begin(), acs_script.end()));
461 
462  // parent1 will already be in the mempool
463  auto mtx_parent1 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[1], /*vout=*/ 0,
464  /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
465  /*output_destination=*/ acs_spk,
466  /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ true);
467  CTransactionRef ptx_parent1 = MakeTransactionRef(mtx_parent1);
468  package_mixed.push_back(ptx_parent1);
469 
470  // parent2 will have a same-txid-different-witness tx already in the mempool
471  CScript grandparent2_script = CScript() << OP_DROP << OP_TRUE;
472  CScript grandparent2_spk = GetScriptForDestination(WitnessV0ScriptHash(grandparent2_script));
473  CScriptWitness parent2_witness1;
474  parent2_witness1.stack.push_back(std::vector<unsigned char>(1));
475  parent2_witness1.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end()));
476  CScriptWitness parent2_witness2;
477  parent2_witness2.stack.push_back(std::vector<unsigned char>(2));
478  parent2_witness2.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end()));
479 
480  // Create grandparent2 creating an output with multiple spending paths. Submit to mempool.
481  auto mtx_grandparent2 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[2], /* vout=*/ 0,
482  /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
483  /*output_destination=*/ grandparent2_spk,
484  /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ true);
485  CTransactionRef ptx_grandparent2 = MakeTransactionRef(mtx_grandparent2);
486 
487  CMutableTransaction mtx_parent2_v1;
488  mtx_parent2_v1.nVersion = 1;
489  mtx_parent2_v1.vin.resize(1);
490  mtx_parent2_v1.vin[0].prevout.hash = ptx_grandparent2->GetHash();
491  mtx_parent2_v1.vin[0].prevout.n = 0;
492  mtx_parent2_v1.vin[0].scriptSig = CScript();
493  mtx_parent2_v1.vin[0].scriptWitness = parent2_witness1;
494  mtx_parent2_v1.vout.resize(1);
495  mtx_parent2_v1.vout[0].nValue = CAmount(48 * COIN);
496  mtx_parent2_v1.vout[0].scriptPubKey = acs_spk;
497 
498  CMutableTransaction mtx_parent2_v2{mtx_parent2_v1};
499  mtx_parent2_v2.vin[0].scriptWitness = parent2_witness2;
500 
501  CTransactionRef ptx_parent2_v1 = MakeTransactionRef(mtx_parent2_v1);
502  CTransactionRef ptx_parent2_v2 = MakeTransactionRef(mtx_parent2_v2);
503  // Put parent2_v1 in the package, submit parent2_v2 to the mempool.
504  const MempoolAcceptResult parent2_v2_result = m_node.chainman->ProcessTransaction(ptx_parent2_v2);
506  package_mixed.push_back(ptx_parent2_v1);
507 
508  // parent3 will be a new transaction
509  auto mtx_parent3 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[3], /*vout=*/ 0,
510  /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
511  /*output_destination=*/ acs_spk,
512  /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ false);
513  CTransactionRef ptx_parent3 = MakeTransactionRef(mtx_parent3);
514  package_mixed.push_back(ptx_parent3);
515 
516  // child spends parent1, parent2, and parent3
517  CKey mixed_grandchild_key;
518  mixed_grandchild_key.MakeNewKey(true);
519  CScript mixed_child_spk = GetScriptForDestination(WitnessV0KeyHash(mixed_grandchild_key.GetPubKey()));
520 
521  CMutableTransaction mtx_mixed_child;
522  mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent1->GetHash(), 0)));
523  mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent2_v1->GetHash(), 0)));
524  mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent3->GetHash(), 0)));
525  mtx_mixed_child.vin[0].scriptWitness = acs_witness;
526  mtx_mixed_child.vin[1].scriptWitness = acs_witness;
527  mtx_mixed_child.vin[2].scriptWitness = acs_witness;
528  mtx_mixed_child.vout.push_back(CTxOut(145 * COIN, mixed_child_spk));
529  CTransactionRef ptx_mixed_child = MakeTransactionRef(mtx_mixed_child);
530  package_mixed.push_back(ptx_mixed_child);
531 
532  // Submit package:
533  // parent1 should be ignored
534  // parent2_v1 should be ignored (and v2 wtxid returned)
535  // parent3 should be accepted
536  // child should be accepted
537  {
538  const auto mixed_result = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_mixed, false);
539  BOOST_CHECK_MESSAGE(mixed_result.m_state.IsValid(), mixed_result.m_state.GetRejectReason());
540  auto it_parent1 = mixed_result.m_tx_results.find(ptx_parent1->GetWitnessHash());
541  auto it_parent2 = mixed_result.m_tx_results.find(ptx_parent2_v1->GetWitnessHash());
542  auto it_parent3 = mixed_result.m_tx_results.find(ptx_parent3->GetWitnessHash());
543  auto it_child = mixed_result.m_tx_results.find(ptx_mixed_child->GetWitnessHash());
544  BOOST_CHECK(it_parent1 != mixed_result.m_tx_results.end());
545  BOOST_CHECK(it_parent2 != mixed_result.m_tx_results.end());
546  BOOST_CHECK(it_parent3 != mixed_result.m_tx_results.end());
547  BOOST_CHECK(it_child != mixed_result.m_tx_results.end());
548 
549  BOOST_CHECK(it_parent1->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
550  BOOST_CHECK(it_parent2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
551  BOOST_CHECK(it_parent3->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
552  BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
553  BOOST_CHECK_EQUAL(ptx_parent2_v2->GetWitnessHash(), it_parent2->second.m_other_wtxid.value());
554 
555  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent1->GetHash())));
556  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent2_v1->GetHash())));
557  BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_parent2_v1->GetWitnessHash())));
558  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent3->GetHash())));
559  BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_mixed_child->GetHash())));
560  }
561 }
CTxIn
An input of a transaction.
Definition: transaction.h:65
policy.h
CMutableTransaction::vin
std::vector< CTxIn > vin
Definition: transaction.h:346
BOOST_FIXTURE_TEST_CASE
BOOST_FIXTURE_TEST_CASE(package_sanitization_tests, TestChain100Setup)
Definition: txpackage_tests.cpp:38
BOOST_AUTO_TEST_SUITE
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
PackageValidationResult::PCKG_TX
@ PCKG_TX
At least one tx is invalid.
CKey::MakeNewKey
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:160
key_io.h
setup_common.h
transaction.h
MempoolAcceptResult::m_result_type
const ResultType m_result_type
Result type.
Definition: validation.h:169
GetScriptForDestination
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:310
CMutableTransaction::nVersion
int32_t nVersion
Definition: transaction.h:348
InsecureRand256
static uint256 InsecureRand256()
Definition: setup_common.h:73
m_node
node::NodeContext m_node
Definition: bitcoin-gui.cpp:36
validation.h
GenTxid::Wtxid
static GenTxid Wtxid(const uint256 &hash)
Definition: transaction.h:398
MakeTransactionRef
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:387
create_placeholder_tx
CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
Definition: txpackage_tests.cpp:20
GenTxid::Txid
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:397
CTransactionRef
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:386
node::NodeContext::mempool
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:45
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
WitnessV0KeyHash
Definition: standard.h:109
CScriptWitness
Definition: script.h:557
packages.h
prevector::end
iterator end()
Definition: prevector.h:292
PackageValidationResult::PCKG_POLICY
@ PCKG_POLICY
The package itself is invalid (e.g. too many transactions).
ToByteVector
std::vector< unsigned char > ToByteVector(const T &in)
Definition: script.h:60
CTxOut
An output of a transaction.
Definition: transaction.h:128
PackageValidationState
Definition: packages.h:35
CENT
static constexpr CAmount CENT
Definition: setup_common.h:78
CAmount
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
standard.h
Shuffle
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:243
MempoolAcceptResult::ResultType::MEMPOOL_ENTRY
@ MEMPOOL_ENTRY
Invalid.
TestChain100Setup
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:124
CKey::GetPubKey
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
CScript
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:405
script.h
MAX_PACKAGE_COUNT
static constexpr uint32_t MAX_PACKAGE_COUNT
Default maximum number of transactions in a package.
Definition: packages.h:15
ValidationState::GetResult
Result GetResult() const
Definition: validation.h:123
OP_TRUE
@ OP_TRUE
Definition: script.h:77
MempoolAcceptResult::ResultType::VALID
@ VALID
CMutableTransaction::vout
std::vector< CTxOut > vout
Definition: transaction.h:347
PKHash
Definition: standard.h:79
CKey
An encapsulated private key.
Definition: key.h:26
IsChildWithParents
bool IsChildWithParents(const Package &package)
Context-free check that a package is exactly one child and its parents; not all parents need to be pr...
Definition: packages.cpp:64
LOCK
#define LOCK(cs)
Definition: sync.h:226
MempoolAcceptResult
Validation result for a single transaction mempool acceptance.
Definition: validation.h:160
prevector::begin
iterator begin()
Definition: prevector.h:290
node::NodeContext::chainman
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:48
Package
std::vector< CTransactionRef > Package
A package is an ordered list of transactions.
Definition: packages.h:33
ValidationState::GetRejectReason
std::string GetRejectReason() const
Definition: validation.h:124
CheckPackage
bool CheckPackage(const Package &txns, PackageValidationState &state)
Context-free package policy checks:
Definition: packages.cpp:14
COutPoint
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:26
MAX_PACKAGE_SIZE
static constexpr uint32_t MAX_PACKAGE_SIZE
Default maximum total virtual size of transactions in a package in KvB.
Definition: packages.h:17
COIN
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
OP_DROP
@ OP_DROP
Definition: script.h:117
CMutableTransaction
A mutable version of CTransaction.
Definition: transaction.h:344
WitnessV0ScriptHash
Definition: standard.h:102
cs_main
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:138
MempoolAcceptResult::ResultType::DIFFERENT_WITNESS
@ DIFFERENT_WITNESS
Valid, transaction was already in the mempool.
GetVirtualTransactionSize
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Compute the virtual transaction size (weight reinterpreted as bytes).
Definition: policy.cpp:282
FastRandomContext
Fast randomness source.
Definition: random.h:131
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition: object.cpp:17
CScriptWitness::stack
std::vector< std::vector< unsigned char > > stack
Definition: script.h:561
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
ProcessNewPackage
PackageMempoolAcceptResult ProcessNewPackage(CChainState &active_chainstate, CTxMemPool &pool, const Package &package, bool test_accept)
Validate (and maybe submit) a package to the mempool.
Definition: validation.cpp:1369