txvalidation_tests.cpp raw

   1  // Copyright (c) 2017-present 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>
   9  #include <policy/ephemeral_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/common.h>
  15  #include <test/util/setup_common.h>
  16  #include <test/util/txmempool.h>
  17  #include <validation.h>
  18  
  19  #include <boost/test/unit_test.hpp>
  20  
  21  
  22  BOOST_AUTO_TEST_SUITE(txvalidation_tests)
  23  
  24  /**
  25   * Ensure that the mempool won't accept coinbase transactions.
  26   */
  27  BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
  28  {
  29      CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
  30      CMutableTransaction coinbaseTx;
  31  
  32      coinbaseTx.version = 1;
  33      coinbaseTx.vin.resize(1);
  34      coinbaseTx.vout.resize(1);
  35      coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
  36      coinbaseTx.vout[0].nValue = 1 * CENT;
  37      coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
  38  
  39      BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
  40  
  41      LOCK(cs_main);
  42  
  43      unsigned int initialPoolSize = m_node.mempool->size();
  44      const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
  45  
  46      BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
  47  
  48      // Check that the transaction hasn't been added to mempool.
  49      BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
  50  
  51      // Check that the validation state reflects the unsuccessful attempt.
  52      BOOST_CHECK(result.m_state.IsInvalid());
  53      BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
  54      BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
  55  }
  56  
  57  // Generate a number of random, nonexistent outpoints.
  58  static inline std::vector<COutPoint> random_outpoints(size_t num_outpoints) {
  59      std::vector<COutPoint> outpoints;
  60      for (size_t i{0}; i < num_outpoints; ++i) {
  61          outpoints.emplace_back(Txid::FromUint256(GetRandHash()), 0);
  62      }
  63      return outpoints;
  64  }
  65  
  66  static inline std::vector<CPubKey> random_keys(size_t num_keys) {
  67      std::vector<CPubKey> keys;
  68      keys.reserve(num_keys);
  69      for (size_t i{0}; i < num_keys; ++i) {
  70          CKey key;
  71          key.MakeNewKey(true);
  72          keys.emplace_back(key.GetPubKey());
  73      }
  74      return keys;
  75  }
  76  
  77  // Creates a placeholder tx (not valid) with 25 outputs. Specify the version and the inputs.
  78  static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int32_t version)
  79  {
  80      CMutableTransaction mtx = CMutableTransaction{};
  81      mtx.version = version;
  82      mtx.vin.resize(inputs.size());
  83      mtx.vout.resize(25);
  84      for (size_t i{0}; i < inputs.size(); ++i) {
  85          mtx.vin[i].prevout = inputs[i];
  86      }
  87      for (auto i{0}; i < 25; ++i) {
  88          mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
  89          mtx.vout[i].nValue = 10000;
  90      }
  91      return MakeTransactionRef(mtx);
  92  }
  93  
  94  static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS = 3;
  95  static constexpr auto EPHEMERAL_DUST_INDEX = NUM_EPHEMERAL_TX_OUTPUTS - 1;
  96  
  97  // Same as make_tx but adds 2 normal outputs and 0-value dust to end of vout
  98  static inline CTransactionRef make_ephemeral_tx(const std::vector<COutPoint>& inputs, int32_t version)
  99  {
 100      CMutableTransaction mtx = CMutableTransaction{};
 101      mtx.version = version;
 102      mtx.vin.resize(inputs.size());
 103      for (size_t i{0}; i < inputs.size(); ++i) {
 104          mtx.vin[i].prevout = inputs[i];
 105      }
 106      mtx.vout.resize(NUM_EPHEMERAL_TX_OUTPUTS);
 107      for (auto i{0}; i < NUM_EPHEMERAL_TX_OUTPUTS; ++i) {
 108          mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
 109          mtx.vout[i].nValue = (i == EPHEMERAL_DUST_INDEX) ? 0 : 10000;
 110      }
 111      return MakeTransactionRef(mtx);
 112  }
 113  
 114  BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
 115  {
 116      CTxMemPool& pool = *Assert(m_node.mempool);
 117      LOCK2(cs_main, pool.cs);
 118      TestMemPoolEntryHelper entry;
 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      TryAddToMempool(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      TryAddToMempool(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      TryAddToMempool(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  
 275  BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
 276  {
 277      // Test TRUC policy helper functions
 278      CTxMemPool& pool = *Assert(m_node.mempool);
 279      LOCK2(cs_main, pool.cs);
 280      TestMemPoolEntryHelper entry;
 281      std::set<Txid> empty_conflicts_set;
 282      std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> empty_parents;
 283  
 284      auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
 285      TryAddToMempool(pool, entry.FromTx(mempool_tx_v3));
 286      auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
 287      TryAddToMempool(pool, entry.FromTx(mempool_tx_v2));
 288  
 289      // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.
 290      {
 291          // mempool_tx_v3
 292          //      ^
 293          // tx_v2_from_v3
 294          auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);
 295          auto parents_v2_from_v3 = pool.GetParents(entry.FromTx(tx_v2_from_v3));
 296          const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
 297              tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
 298              mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
 299          auto result_v2_from_v3{SingleTRUCChecks(pool, tx_v2_from_v3, parents_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
 300          BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
 301          BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
 302  
 303          Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
 304          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_parents), expected_error_str);
 305          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, parents_v2_from_v3), expected_error_str);
 306  
 307          // mempool_tx_v3  mempool_tx_v2
 308          //            ^    ^
 309          //    tx_v2_from_v2_and_v3
 310          auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
 311          auto parents_2_from_both{pool.GetParents(entry.FromTx(tx_v2_from_v2_and_v3))};
 312          const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
 313              tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
 314              mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
 315          auto result_v2_from_both{SingleTRUCChecks(pool, tx_v2_from_v2_and_v3, parents_2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};
 316          BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
 317          BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
 318  
 319          Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
 320          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_parents), expected_error_str_2);
 321      }
 322  
 323      // TRUC cannot spend from an unconfirmed non-TRUC transaction.
 324      {
 325          // mempool_tx_v2
 326          //      ^
 327          // tx_v3_from_v2
 328          auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
 329          auto parents_v3_from_v2{pool.GetParents(entry.FromTx(tx_v3_from_v2))};
 330          const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
 331              tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
 332              mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
 333          auto result_v3_from_v2{SingleTRUCChecks(pool, tx_v3_from_v2, parents_v3_from_v2,  empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
 334          BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
 335          BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
 336  
 337          Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
 338          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_parents), expected_error_str);
 339          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, parents_v3_from_v2), expected_error_str);
 340  
 341          // mempool_tx_v3  mempool_tx_v2
 342          //            ^    ^
 343          //    tx_v3_from_v2_and_v3
 344          auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
 345          auto parents_v3_from_both{pool.GetParents(entry.FromTx(tx_v3_from_v2_and_v3))};
 346          const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
 347              tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
 348              mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
 349          auto result_v3_from_both{SingleTRUCChecks(pool, tx_v3_from_v2_and_v3, parents_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};
 350          BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
 351          BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
 352  
 353          // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.
 354          const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
 355              tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};
 356          Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};
 357          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_parents), expected_error_str_3);
 358      }
 359      // V3 from V3 is ok, and non-V3 from non-V3 is ok.
 360      {
 361          // mempool_tx_v3
 362          //      ^
 363          // tx_v3_from_v3
 364          auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
 365          auto parents_v3{pool.GetParents(entry.FromTx(tx_v3_from_v3))};
 366          BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_from_v3, parents_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))
 367                      == std::nullopt);
 368  
 369          Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};
 370          BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_parents) == std::nullopt);
 371  
 372          // mempool_tx_v2
 373          //      ^
 374          // tx_v2_from_v2
 375          auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
 376          auto parents_v2{pool.GetParents(entry.FromTx(tx_v2_from_v2))};
 377          BOOST_CHECK(SingleTRUCChecks(pool, tx_v2_from_v2, parents_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))
 378                      == std::nullopt);
 379  
 380          Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};
 381          BOOST_CHECK(PackageTRUCChecks(pool, tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_parents) == std::nullopt);
 382      }
 383  
 384      // Tx spending TRUC cannot have too many mempool ancestors
 385      // Configuration where the tx has multiple direct parents.
 386      {
 387          Package package_multi_parents;
 388          std::vector<COutPoint> mempool_outpoints;
 389          mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
 390          package_multi_parents.emplace_back(mempool_tx_v3);
 391          for (size_t i{0}; i < 2; ++i) {
 392              auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
 393              TryAddToMempool(pool, entry.FromTx(mempool_tx));
 394              mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
 395              package_multi_parents.emplace_back(mempool_tx);
 396          }
 397          auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);
 398          package_multi_parents.emplace_back(tx_v3_multi_parent);
 399          auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_parent))};
 400          BOOST_CHECK_EQUAL(parents.size(), 3);
 401          const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
 402              tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
 403          auto result{SingleTRUCChecks(pool, tx_v3_multi_parent, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
 404          BOOST_CHECK_EQUAL(result->first, expected_error_str);
 405          BOOST_CHECK_EQUAL(result->second, nullptr);
 406  
 407          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_parents),
 408                            expected_error_str);
 409      }
 410  
 411      // Configuration where the tx is in a multi-generation chain.
 412      {
 413          Package package_multi_gen;
 414          CTransactionRef middle_tx;
 415          auto last_outpoint{random_outpoints(1)[0]};
 416          for (size_t i{0}; i < 2; ++i) {
 417              auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
 418              TryAddToMempool(pool, entry.FromTx(mempool_tx));
 419              last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
 420              package_multi_gen.emplace_back(mempool_tx);
 421              if (i == 1) middle_tx = mempool_tx;
 422          }
 423          auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);
 424          package_multi_gen.emplace_back(tx_v3_multi_gen);
 425          auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_gen))};
 426          const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
 427              tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
 428          auto result{SingleTRUCChecks(pool, tx_v3_multi_gen, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
 429          BOOST_CHECK_EQUAL(result->first, expected_error_str);
 430          BOOST_CHECK_EQUAL(result->second, nullptr);
 431  
 432          // Middle tx is what triggers a failure for the grandchild:
 433          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_parents), expected_error_str);
 434          BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_parents) == std::nullopt);
 435      }
 436  
 437      // Tx spending TRUC cannot be too large in virtual size.
 438      auto many_inputs{random_outpoints(100)};
 439      many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);
 440      {
 441          auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);
 442          const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};
 443          auto parents{pool.GetParents(entry.FromTx(tx_v3_child_big))};
 444          const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
 445              tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};
 446          auto result{SingleTRUCChecks(pool, tx_v3_child_big, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
 447          BOOST_CHECK_EQUAL(result->first, expected_error_str);
 448          BOOST_CHECK_EQUAL(result->second, nullptr);
 449  
 450          Package package_child_big{mempool_tx_v3, tx_v3_child_big};
 451          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_parents),
 452                            expected_error_str);
 453      }
 454  
 455      // Tx spending TRUC cannot have too many sigops.
 456      // This child has 10 P2WSH multisig inputs.
 457      auto multisig_outpoints{random_outpoints(10)};
 458      multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
 459      auto keys{random_keys(2)};
 460      CScript script_multisig;
 461      script_multisig << OP_1;
 462      for (const auto& key : keys) {
 463          script_multisig << ToByteVector(key);
 464      }
 465      script_multisig << OP_2 << OP_CHECKMULTISIG;
 466      {
 467          CMutableTransaction mtx_many_sigops = CMutableTransaction{};
 468          mtx_many_sigops.version = TRUC_VERSION;
 469          for (const auto& outpoint : multisig_outpoints) {
 470              mtx_many_sigops.vin.emplace_back(outpoint);
 471              mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());
 472          }
 473          mtx_many_sigops.vout.resize(1);
 474          mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;
 475          mtx_many_sigops.vout.back().nValue = 10000;
 476          auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};
 477  
 478          auto parents{pool.GetParents(entry.FromTx(tx_many_sigops))};
 479          // legacy uses fAccurate = false, and the maximum number of multisig keys is used
 480          const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};
 481          BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);
 482          const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};
 483          // Weight limit is not reached...
 484          BOOST_CHECK(SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set, bip141_vsize) == std::nullopt);
 485          // ...but sigop limit is.
 486          const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
 487              tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
 488              total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, TRUC_CHILD_MAX_VSIZE)};
 489          auto result{SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set,
 490                                          GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
 491          BOOST_CHECK_EQUAL(result->first, expected_error_str);
 492          BOOST_CHECK_EQUAL(result->second, nullptr);
 493  
 494          Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
 495          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_parents),
 496                            expected_error_str);
 497      }
 498  
 499      // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.
 500      auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
 501      {
 502          BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR);
 503          auto parents{pool.GetParents(entry.FromTx(tx_mempool_v3_child))};
 504          BOOST_CHECK(SingleTRUCChecks(pool, tx_mempool_v3_child, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
 505          TryAddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
 506  
 507          Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
 508          BOOST_CHECK(PackageTRUCChecks(pool, tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_parents) == std::nullopt);
 509      }
 510  
 511      // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
 512      {
 513          auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3);
 514          // Configuration where parent already has 1 other child in mempool
 515          auto parents_1sibling{pool.GetParents(entry.FromTx(tx_v3_child2))};
 516          const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
 517              mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
 518          auto result_with_sibling_eviction{SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
 519          BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
 520          // The other mempool child is returned to allow for sibling eviction.
 521          BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child);
 522  
 523          // If directly replacing the child, make sure there is no double-counting.
 524          BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
 525                      == std::nullopt);
 526  
 527          Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
 528          BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_parents),
 529                            expected_error_str);
 530  
 531          // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
 532          TryAddToMempool(pool, entry.FromTx(tx_v3_child2));
 533          auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
 534          auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash()).value();
 535          BOOST_CHECK_EQUAL(pool.GetDescendantCount(entry_mempool_parent), 3);
 536          auto parents_2siblings{pool.GetParents(entry.FromTx(tx_v3_child3))};
 537  
 538          auto result_2children{SingleTRUCChecks(pool, tx_v3_child3, parents_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
 539          BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
 540          // The other mempool child is not returned because sibling eviction is not allowed.
 541          BOOST_CHECK_EQUAL(result_2children->second, nullptr);
 542      }
 543  
 544      // 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.
 545      {
 546          auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
 547          auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
 548          auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
 549          auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
 550  
 551          TryAddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
 552          TryAddToMempool(pool, entry.FromTx(tx_mempool_sibling));
 553          TryAddToMempool(pool, entry.FromTx(tx_mempool_nibling));
 554  
 555          auto parents_3gen{pool.GetParents(entry.FromTx(tx_to_submit))};
 556          const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
 557              tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
 558          auto result_3gen{SingleTRUCChecks(pool, tx_to_submit, parents_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
 559          BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
 560          // The other mempool child is not returned because sibling eviction is not allowed.
 561          BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
 562      }
 563  
 564      // Configuration where tx has multiple generations of descendants is not tested because that is
 565      // equivalent to the tx with multiple generations of ancestors.
 566  }
 567  
 568  BOOST_AUTO_TEST_SUITE_END()
 569