Bitcoin Core  29.1.0
P2P Digital Currency
txvalidation_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-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 <policy/truc_policy.h>
11 #include <primitives/transaction.h>
12 #include <random.h>
13 #include <script/script.h>
14 #include <test/util/setup_common.h>
15 #include <test/util/txmempool.h>
16 #include <validation.h>
17 
18 #include <boost/test/unit_test.hpp>
19 
20 
21 BOOST_AUTO_TEST_SUITE(txvalidation_tests)
22 
23 
26 BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
27 {
28  CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
29  CMutableTransaction coinbaseTx;
30 
31  coinbaseTx.version = 1;
32  coinbaseTx.vin.resize(1);
33  coinbaseTx.vout.resize(1);
34  coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
35  coinbaseTx.vout[0].nValue = 1 * CENT;
36  coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
37 
38  BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
39 
40  LOCK(cs_main);
41 
42  unsigned int initialPoolSize = m_node.mempool->size();
43  const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
44 
46 
47  // Check that the transaction hasn't been added to mempool.
48  BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
49 
50  // Check that the validation state reflects the unsuccessful attempt.
51  BOOST_CHECK(result.m_state.IsInvalid());
52  BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
53  BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
54 }
55 
56 // Generate a number of random, nonexistent outpoints.
57 static inline std::vector<COutPoint> random_outpoints(size_t num_outpoints) {
58  std::vector<COutPoint> outpoints;
59  for (size_t i{0}; i < num_outpoints; ++i) {
60  outpoints.emplace_back(Txid::FromUint256(GetRandHash()), 0);
61  }
62  return outpoints;
63 }
64 
65 static inline std::vector<CPubKey> random_keys(size_t num_keys) {
66  std::vector<CPubKey> keys;
67  keys.reserve(num_keys);
68  for (size_t i{0}; i < num_keys; ++i) {
69  CKey key;
70  key.MakeNewKey(true);
71  keys.emplace_back(key.GetPubKey());
72  }
73  return keys;
74 }
75 
76 // Creates a placeholder tx (not valid) with 25 outputs. Specify the version and the inputs.
77 static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int32_t version)
78 {
80  mtx.version = version;
81  mtx.vin.resize(inputs.size());
82  mtx.vout.resize(25);
83  for (size_t i{0}; i < inputs.size(); ++i) {
84  mtx.vin[i].prevout = inputs[i];
85  }
86  for (auto i{0}; i < 25; ++i) {
87  mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
88  mtx.vout[i].nValue = 10000;
89  }
90  return MakeTransactionRef(mtx);
91 }
92 
93 static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS = 3;
94 static constexpr auto EPHEMERAL_DUST_INDEX = NUM_EPHEMERAL_TX_OUTPUTS - 1;
95 
96 // Same as make_tx but adds 2 normal outputs and 0-value dust to end of vout
97 static inline CTransactionRef make_ephemeral_tx(const std::vector<COutPoint>& inputs, int32_t version)
98 {
100  mtx.version = version;
101  mtx.vin.resize(inputs.size());
102  for (size_t i{0}; i < inputs.size(); ++i) {
103  mtx.vin[i].prevout = inputs[i];
104  }
105  mtx.vout.resize(NUM_EPHEMERAL_TX_OUTPUTS);
106  for (auto i{0}; i < NUM_EPHEMERAL_TX_OUTPUTS; ++i) {
107  mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
108  mtx.vout[i].nValue = (i == EPHEMERAL_DUST_INDEX) ? 0 : 10000;
109  }
110  return MakeTransactionRef(mtx);
111 }
112 
114 {
115  CTxMemPool& pool = *Assert(m_node.mempool);
116  LOCK2(cs_main, pool.cs);
118  CTxMemPool::setEntries empty_ancestors;
119 
120  TxValidationState child_state;
121  Wtxid child_wtxid;
122 
123  // Arbitrary non-0 feerate for these tests
124  CFeeRate dustrelay(DUST_RELAY_TX_FEE);
125 
126  // Basic transaction with dust
127  auto grandparent_tx_1 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
128  const auto dust_txid = grandparent_tx_1->GetHash();
129 
130  // Child transaction spending dust
131  auto dust_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
132 
133  // We first start with nothing "in the mempool", using package checks
134 
135  // Trivial single transaction with no dust
136  BOOST_CHECK(CheckEphemeralSpends({dust_spend}, dustrelay, pool, child_state, child_wtxid));
137  BOOST_CHECK(child_state.IsValid());
138  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
139 
140  // Now with dust, ok because the tx has no dusty parents
141  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
142  BOOST_CHECK(child_state.IsValid());
143  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
144 
145  // Dust checks pass
146  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, CFeeRate(0), pool, child_state, child_wtxid));
147  BOOST_CHECK(child_state.IsValid());
148  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
149  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, dustrelay, pool, child_state, child_wtxid));
150  BOOST_CHECK(child_state.IsValid());
151  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
152 
153  auto dust_non_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
154 
155  // Child spending non-dust only from parent should be disallowed even if dust otherwise spent
156  const auto dust_non_spend_wtxid{dust_non_spend->GetWitnessHash()};
157  BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend, dust_spend}, dustrelay, pool, child_state, child_wtxid));
158  BOOST_CHECK(!child_state.IsValid());
159  BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
160  child_state = TxValidationState();
161  child_wtxid = Wtxid();
162 
163  BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_spend, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
164  BOOST_CHECK(!child_state.IsValid());
165  BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
166  child_state = TxValidationState();
167  child_wtxid = Wtxid();
168 
169  BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
170  BOOST_CHECK(!child_state.IsValid());
171  BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
172  child_state = TxValidationState();
173  child_wtxid = Wtxid();
174 
175  auto grandparent_tx_2 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
176  const auto dust_txid_2 = grandparent_tx_2->GetHash();
177 
178  // Spend dust from one but not another is ok, as long as second grandparent has no child
179  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend}, dustrelay, pool, child_state, child_wtxid));
180  BOOST_CHECK(child_state.IsValid());
181  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
182 
183  auto dust_non_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
184  // But if we spend from the parent, it must spend dust
185  BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
186  BOOST_CHECK(!child_state.IsValid());
187  BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
188  child_state = TxValidationState();
189  child_wtxid = Wtxid();
190 
191  auto dust_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
192  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
193  BOOST_CHECK(child_state.IsValid());
194  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
195 
196  // Spending other outputs is also correct, as long as the dusty one is spent
197  const std::vector<COutPoint> all_outpoints{COutPoint(dust_txid, 0), COutPoint(dust_txid, 1), COutPoint(dust_txid, 2),
198  COutPoint(dust_txid_2, 0), COutPoint(dust_txid_2, 1), COutPoint(dust_txid_2, 2)};
199  auto dust_spend_all_outpoints = make_tx(all_outpoints, /*version=*/2);
200  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_all_outpoints}, dustrelay, pool, child_state, child_wtxid));
201  BOOST_CHECK(child_state.IsValid());
202  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
203 
204  // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with no dust
205  auto parent_with_dust = make_ephemeral_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
206  // Ok for parent to have dust
207  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
208  BOOST_CHECK(child_state.IsValid());
209  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
210  auto child_no_dust = make_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
211  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_no_dust}, dustrelay, pool, child_state, child_wtxid));
212  BOOST_CHECK(child_state.IsValid());
213  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
214 
215  // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with dust
216  auto child_with_dust = make_ephemeral_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
217  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_with_dust}, dustrelay, pool, child_state, child_wtxid));
218  BOOST_CHECK(child_state.IsValid());
219  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
220 
221  // Tests with parents in mempool
222 
223  // Nothing in mempool, this should pass for any transaction
224  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
225  BOOST_CHECK(child_state.IsValid());
226  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
227 
228  // Add first grandparent to mempool and fetch entry
229  AddToMempool(pool, entry.FromTx(grandparent_tx_1));
230 
231  // Ignores ancestors that aren't direct parents
232  BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
233  BOOST_CHECK(child_state.IsValid());
234  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
235 
236  // Valid spend of dust with grandparent in mempool
237  BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
238  BOOST_CHECK(child_state.IsValid());
239  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
240 
241  // Second grandparent in same package
242  BOOST_CHECK(CheckEphemeralSpends({parent_with_dust, grandparent_tx_2}, dustrelay, pool, child_state, child_wtxid));
243  BOOST_CHECK(child_state.IsValid());
244  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
245 
246  // Order in package doesn't matter
247  BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
248  BOOST_CHECK(child_state.IsValid());
249  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
250 
251  // Add second grandparent to mempool
252  AddToMempool(pool, entry.FromTx(grandparent_tx_2));
253 
254  // Only spends single dust out of two direct parents
255  BOOST_CHECK(!CheckEphemeralSpends({dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
256  BOOST_CHECK(!child_state.IsValid());
257  BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
258  child_state = TxValidationState();
259  child_wtxid = Wtxid();
260 
261  // Spends both parents' dust
262  BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
263  BOOST_CHECK(child_state.IsValid());
264  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
265 
266  // Now add dusty parent to mempool
267  AddToMempool(pool, entry.FromTx(parent_with_dust));
268 
269  // Passes dust checks even with non-parent ancestors
270  BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
271  BOOST_CHECK(child_state.IsValid());
272  BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
273 }
274 
276 {
277  // Test TRUC policy helper functions
278  CTxMemPool& pool = *Assert(m_node.mempool);
279  LOCK2(cs_main, pool.cs);
281  std::set<Txid> empty_conflicts_set;
282  CTxMemPool::setEntries empty_ancestors;
283 
284  auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
285  AddToMempool(pool, entry.FromTx(mempool_tx_v3));
286  auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
287  AddToMempool(pool, entry.FromTx(mempool_tx_v2));
288  // Default values.
289  CTxMemPool::Limits m_limits{};
290 
291  // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.
292  {
293  // mempool_tx_v3
294  // ^
295  // tx_v2_from_v3
296  auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);
297  auto ancestors_v2_from_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v3), m_limits)};
298  const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
299  tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
300  mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
301  auto result_v2_from_v3{SingleTRUCChecks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
302  BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
303  BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
304 
305  Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
306  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_ancestors), expected_error_str);
307  CTxMemPool::setEntries entries_mempool_v3{pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value()};
308  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, entries_mempool_v3), expected_error_str);
309 
310  // mempool_tx_v3 mempool_tx_v2
311  // ^ ^
312  // tx_v2_from_v2_and_v3
313  auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
314  auto ancestors_v2_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2_and_v3), m_limits)};
315  const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
316  tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
317  mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
318  auto result_v2_from_both{SingleTRUCChecks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};
319  BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
320  BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
321 
322  Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
323  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_ancestors), expected_error_str_2);
324  }
325 
326  // TRUC cannot spend from an unconfirmed non-TRUC transaction.
327  {
328  // mempool_tx_v2
329  // ^
330  // tx_v3_from_v2
331  auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
332  auto ancestors_v3_from_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2), m_limits)};
333  const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
334  tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
335  mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
336  auto result_v3_from_v2{SingleTRUCChecks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
337  BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
338  BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
339 
340  Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
341  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_ancestors), expected_error_str);
342  CTxMemPool::setEntries entries_mempool_v2{pool.GetIter(mempool_tx_v2->GetHash().ToUint256()).value()};
343  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, entries_mempool_v2), expected_error_str);
344 
345  // mempool_tx_v3 mempool_tx_v2
346  // ^ ^
347  // tx_v3_from_v2_and_v3
348  auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
349  auto ancestors_v3_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2_and_v3), m_limits)};
350  const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
351  tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
352  mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
353  auto result_v3_from_both{SingleTRUCChecks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};
354  BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
355  BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
356 
357  // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.
358  const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
359  tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};
360  Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};
361  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_ancestors), expected_error_str_3);
362  }
363  // V3 from V3 is ok, and non-V3 from non-V3 is ok.
364  {
365  // mempool_tx_v3
366  // ^
367  // tx_v3_from_v3
368  auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
369  auto ancestors_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v3), m_limits)};
370  BOOST_CHECK(SingleTRUCChecks(tx_v3_from_v3, *ancestors_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))
371  == std::nullopt);
372 
373  Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};
374  BOOST_CHECK(PackageTRUCChecks(tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_ancestors) == std::nullopt);
375 
376  // mempool_tx_v2
377  // ^
378  // tx_v2_from_v2
379  auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
380  auto ancestors_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2), m_limits)};
381  BOOST_CHECK(SingleTRUCChecks(tx_v2_from_v2, *ancestors_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))
382  == std::nullopt);
383 
384  Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};
385  BOOST_CHECK(PackageTRUCChecks(tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_ancestors) == std::nullopt);
386  }
387 
388  // Tx spending TRUC cannot have too many mempool ancestors
389  // Configuration where the tx has multiple direct parents.
390  {
391  Package package_multi_parents;
392  std::vector<COutPoint> mempool_outpoints;
393  mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
394  package_multi_parents.emplace_back(mempool_tx_v3);
395  for (size_t i{0}; i < 2; ++i) {
396  auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
397  AddToMempool(pool, entry.FromTx(mempool_tx));
398  mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
399  package_multi_parents.emplace_back(mempool_tx);
400  }
401  auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);
402  package_multi_parents.emplace_back(tx_v3_multi_parent);
403  auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_parent), m_limits)};
404  BOOST_CHECK_EQUAL(ancestors->size(), 3);
405  const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
406  tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
407  auto result{SingleTRUCChecks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
408  BOOST_CHECK_EQUAL(result->first, expected_error_str);
409  BOOST_CHECK_EQUAL(result->second, nullptr);
410 
411  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_ancestors),
412  expected_error_str);
413  }
414 
415  // Configuration where the tx is in a multi-generation chain.
416  {
417  Package package_multi_gen;
418  CTransactionRef middle_tx;
419  auto last_outpoint{random_outpoints(1)[0]};
420  for (size_t i{0}; i < 2; ++i) {
421  auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
422  AddToMempool(pool, entry.FromTx(mempool_tx));
423  last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
424  package_multi_gen.emplace_back(mempool_tx);
425  if (i == 1) middle_tx = mempool_tx;
426  }
427  auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);
428  package_multi_gen.emplace_back(tx_v3_multi_gen);
429  auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_gen), m_limits)};
430  const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
431  tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
432  auto result{SingleTRUCChecks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
433  BOOST_CHECK_EQUAL(result->first, expected_error_str);
434  BOOST_CHECK_EQUAL(result->second, nullptr);
435 
436  // Middle tx is what triggers a failure for the grandchild:
437  BOOST_CHECK_EQUAL(*PackageTRUCChecks(middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_ancestors), expected_error_str);
438  BOOST_CHECK(PackageTRUCChecks(tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_ancestors) == std::nullopt);
439  }
440 
441  // Tx spending TRUC cannot be too large in virtual size.
442  auto many_inputs{random_outpoints(100)};
443  many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);
444  {
445  auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);
446  const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};
447  auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child_big), m_limits)};
448  const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
449  tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};
450  auto result{SingleTRUCChecks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
451  BOOST_CHECK_EQUAL(result->first, expected_error_str);
452  BOOST_CHECK_EQUAL(result->second, nullptr);
453 
454  Package package_child_big{mempool_tx_v3, tx_v3_child_big};
455  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_ancestors),
456  expected_error_str);
457  }
458 
459  // Tx spending TRUC cannot have too many sigops.
460  // This child has 10 P2WSH multisig inputs.
461  auto multisig_outpoints{random_outpoints(10)};
462  multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
463  auto keys{random_keys(2)};
464  CScript script_multisig;
465  script_multisig << OP_1;
466  for (const auto& key : keys) {
467  script_multisig << ToByteVector(key);
468  }
469  script_multisig << OP_2 << OP_CHECKMULTISIG;
470  {
471  CMutableTransaction mtx_many_sigops = CMutableTransaction{};
472  mtx_many_sigops.version = TRUC_VERSION;
473  for (const auto& outpoint : multisig_outpoints) {
474  mtx_many_sigops.vin.emplace_back(outpoint);
475  mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());
476  }
477  mtx_many_sigops.vout.resize(1);
478  mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;
479  mtx_many_sigops.vout.back().nValue = 10000;
480  auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};
481 
482  auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_many_sigops), m_limits)};
483  // legacy uses fAccurate = false, and the maximum number of multisig keys is used
484  const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};
485  BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);
486  const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};
487  // Weight limit is not reached...
488  BOOST_CHECK(SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set, bip141_vsize) == std::nullopt);
489  // ...but sigop limit is.
490  const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
491  tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
493  auto result{SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set,
494  GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
495  BOOST_CHECK_EQUAL(result->first, expected_error_str);
496  BOOST_CHECK_EQUAL(result->second, nullptr);
497 
498  Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
499  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_ancestors),
500  expected_error_str);
501  }
502 
503  // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.
504  auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
505  {
507  auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_mempool_v3_child), m_limits)};
508  BOOST_CHECK(SingleTRUCChecks(tx_mempool_v3_child, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
509  AddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
510 
511  Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
512  BOOST_CHECK(PackageTRUCChecks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt);
513  }
514 
515  // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
516  {
517  auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3);
518 
519  // Configuration where parent already has 1 other child in mempool
520  auto ancestors_1sibling{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child2), m_limits)};
521  const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
522  mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
523  auto result_with_sibling_eviction{SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
524  BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
525  // The other mempool child is returned to allow for sibling eviction.
526  BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child);
527 
528  // If directly replacing the child, make sure there is no double-counting.
529  BOOST_CHECK(SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
530  == std::nullopt);
531 
532  Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
533  BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_ancestors),
534  expected_error_str);
535 
536  // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
537  AddToMempool(pool, entry.FromTx(tx_v3_child2));
538  auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
539  auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value();
540  BOOST_CHECK_EQUAL(entry_mempool_parent->GetCountWithDescendants(), 3);
541  auto ancestors_2siblings{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child3), m_limits)};
542 
543  auto result_2children{SingleTRUCChecks(tx_v3_child3, *ancestors_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
544  BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
545  // The other mempool child is not returned because sibling eviction is not allowed.
546  BOOST_CHECK_EQUAL(result_2children->second, nullptr);
547  }
548 
549  // Sibling eviction: parent already has 1 other child, which also has its own child (no sibling eviction allowed). This may happen as the result of a reorg.
550  {
551  auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
552  auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
553  auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
554  auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
555 
556  AddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
557  AddToMempool(pool, entry.FromTx(tx_mempool_sibling));
558  AddToMempool(pool, entry.FromTx(tx_mempool_nibling));
559 
560  auto ancestors_3gen{pool.CalculateMemPoolAncestors(entry.FromTx(tx_to_submit), m_limits)};
561  const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
562  tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
563  auto result_3gen{SingleTRUCChecks(tx_to_submit, *ancestors_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
564  BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
565  // The other mempool child is not returned because sibling eviction is not allowed.
566  BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
567  }
568 
569  // Configuration where tx has multiple generations of descendants is not tested because that is
570  // equivalent to the tx with multiple generations of ancestors.
571 }
572 
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
unsigned int GetSigOpCount(bool fAccurate) const
Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs as 20 sigops.
Definition: script.cpp:159
invalid by consensus rules
static constexpr int64_t TRUC_CHILD_MAX_VSIZE
Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC transaction...
Definition: truc_policy.h:32
static const int WITNESS_SCALE_FACTOR
Definition: consensus.h:21
Definition: txmempool.h:19
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
Options struct containing limit options for a CTxMemPool.
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:182
std::vector< CTxIn > vin
Definition: transaction.h:379
static CTransactionRef make_ephemeral_tx(const std::vector< COutPoint > &inputs, int32_t version)
std::vector< CTransactionRef > Package
A package is an ordered list of transactions.
Definition: packages.h:50
std::set< txiter, CompareIteratorByHash > setEntries
Definition: txmempool.h:396
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:347
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:33
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP
Default for -bytespersigop.
Definition: policy.h:46
static constexpr auto EPHEMERAL_DUST_INDEX
static const int MAX_PUBKEYS_PER_MULTISIG
Definition: script.h:34
static int32_t GetTransactionWeight(const CTransaction &tx)
Definition: validation.h:133
std::optional< std::pair< std::string, CTransactionRef > > SingleTRUCChecks(const CTransactionRef &ptx, const CTxMemPool::setEntries &mempool_ancestors, const std::set< Txid > &direct_conflicts, int64_t vsize)
Must be called for every transaction, even if not TRUC.
util::Result< setEntries > CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, const Limits &limits, bool fSearchForParents=true) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Try to calculate all in-mempool ancestors of entry.
Definition: txmempool.cpp:243
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
Definition: script.h:83
iterator end()
Definition: prevector.h:304
Definition: script.h:94
#define LOCK2(cs1, cs2)
Definition: sync.h:258
static std::vector< CPubKey > random_keys(size_t num_keys)
Definition: script.h:84
static decltype(CTransaction::version) constexpr TRUC_VERSION
Definition: truc_policy.h:20
static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS
#define LOCK(cs)
Definition: sync.h:257
static CTransactionRef make_tx(const std::vector< COutPoint > &inputs, int32_t version)
BOOST_AUTO_TEST_SUITE_END()
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:161
AddToMempool(pool, CTxMemPoolEntry(tx, fee, nTime, nHeight, sequence, spendsCoinbase, sigOpCost, lp))
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:140
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
Validation result for a transaction evaluated by MemPoolAccept (single or package).
Definition: validation.h:123
bool CheckEphemeralSpends(const Package &package, CFeeRate dust_relay_rate, const CTxMemPool &tx_pool, TxValidationState &out_child_state, Wtxid &out_child_wtxid)
Must be called for each transaction(package) if any dust is in the package.
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
Ensure that the mempool won&#39;t accept coinbase transactions.
static constexpr unsigned int DUST_RELAY_TX_FEE
Min feerate for defining dust.
Definition: policy.h:64
std::optional< txiter > GetIter(const uint256 &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
Definition: txmempool.cpp:987
std::vector< unsigned char > ToByteVector(const T &in)
Definition: script.h:67
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:303
auto result
Definition: common-types.h:74
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:414
transaction_identifier< true > Wtxid
Wtxid commits to all transaction fields including the witness.
static transaction_identifier FromUint256(const uint256 &id)
uint256 GetRandHash() noexcept
Generate a random uint256.
Definition: random.h:454
std::optional< std::string > PackageTRUCChecks(const CTransactionRef &ptx, int64_t vsize, const Package &package, const CTxMemPool::setEntries &mempool_ancestors)
Must be called for every transaction that is submitted within a package, even if not TRUC...
Definition: truc_policy.cpp:58
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:32
iterator begin()
Definition: prevector.h:302
A mutable version of CTransaction.
Definition: transaction.h:377
Definition: script.h:85
static std::vector< COutPoint > random_outpoints(size_t num_outpoints)
static constexpr CAmount CENT
Definition: setup_common.h:47
An encapsulated private key.
Definition: key.h:34
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: transaction.h:295
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:128
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:72
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
#define BOOST_CHECK(expr)
Definition: object.cpp:17
transaction_identifier represents the two canonical transaction identifier types (txid, wtxid).