LCOV - code coverage report
Current view: top level - src/test - txvalidation_tests.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 99.5 % 367 365
Test Date: 2026-03-16 05:20:51 Functions: 100.0 % 10 10
Branches: 48.9 % 2072 1014

             Branch data     Line data    Source code
       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   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
      28                 :             : {
      29   [ +  -  +  -  :           2 :     CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
                   +  - ]
      30         [ +  - ]:           1 :     CMutableTransaction coinbaseTx;
      31                 :             : 
      32                 :           1 :     coinbaseTx.version = 1;
      33         [ +  - ]:           1 :     coinbaseTx.vin.resize(1);
      34         [ +  - ]:           1 :     coinbaseTx.vout.resize(1);
      35   [ +  -  +  - ]:           1 :     coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
      36                 :           1 :     coinbaseTx.vout[0].nValue = 1 * CENT;
      37                 :           1 :     coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
      38                 :             : 
      39   [ +  -  +  -  :           2 :     BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
             +  -  +  - ]
      40                 :             : 
      41         [ +  - ]:           1 :     LOCK(cs_main);
      42                 :             : 
      43         [ +  - ]:           1 :     unsigned int initialPoolSize = m_node.mempool->size();
      44   [ +  -  +  - ]:           2 :     const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
      45                 :             : 
      46   [ +  -  +  -  :           2 :     BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
                   +  - ]
      47                 :             : 
      48                 :             :     // Check that the transaction hasn't been added to mempool.
      49   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
                   +  - ]
      50                 :             : 
      51                 :             :     // Check that the validation state reflects the unsuccessful attempt.
      52   [ +  -  +  -  :           2 :     BOOST_CHECK(result.m_state.IsInvalid());
                   +  - ]
      53   [ +  -  -  +  :           2 :     BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
                   +  - ]
      54   [ +  -  +  - ]:           2 :     BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
      55         [ +  - ]:           3 : }
      56                 :             : 
      57                 :             : // Generate a number of random, nonexistent outpoints.
      58                 :          10 : static inline std::vector<COutPoint> random_outpoints(size_t num_outpoints) {
      59                 :          10 :     std::vector<COutPoint> outpoints;
      60         [ +  + ]:         129 :     for (size_t i{0}; i < num_outpoints; ++i) {
      61         [ +  - ]:         119 :         outpoints.emplace_back(Txid::FromUint256(GetRandHash()), 0);
      62                 :             :     }
      63                 :          10 :     return outpoints;
      64                 :           0 : }
      65                 :             : 
      66                 :           1 : static inline std::vector<CPubKey> random_keys(size_t num_keys) {
      67                 :           1 :     std::vector<CPubKey> keys;
      68         [ +  - ]:           1 :     keys.reserve(num_keys);
      69         [ +  + ]:           3 :     for (size_t i{0}; i < num_keys; ++i) {
      70                 :           2 :         CKey key;
      71         [ +  - ]:           2 :         key.MakeNewKey(true);
      72   [ +  -  +  - ]:           2 :         keys.emplace_back(key.GetPubKey());
      73                 :           2 :     }
      74                 :           1 :     return keys;
      75                 :           0 : }
      76                 :             : 
      77                 :             : // Creates a placeholder tx (not valid) with 25 outputs. Specify the version and the inputs.
      78                 :          28 : static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int32_t version)
      79                 :             : {
      80                 :          28 :     CMutableTransaction mtx = CMutableTransaction{};
      81                 :          28 :     mtx.version = version;
      82   [ -  +  +  - ]:          28 :     mtx.vin.resize(inputs.size());
      83         [ +  - ]:          28 :     mtx.vout.resize(25);
      84   [ -  +  +  + ]:         168 :     for (size_t i{0}; i < inputs.size(); ++i) {
      85                 :         140 :         mtx.vin[i].prevout = inputs[i];
      86                 :             :     }
      87         [ +  + ]:         728 :     for (auto i{0}; i < 25; ++i) {
      88         [ +  - ]:         700 :         mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
      89                 :         700 :         mtx.vout[i].nValue = 10000;
      90                 :             :     }
      91         [ +  - ]:          56 :     return MakeTransactionRef(mtx);
      92                 :          28 : }
      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                 :           4 : static inline CTransactionRef make_ephemeral_tx(const std::vector<COutPoint>& inputs, int32_t version)
      99                 :             : {
     100                 :           4 :     CMutableTransaction mtx = CMutableTransaction{};
     101                 :           4 :     mtx.version = version;
     102   [ -  +  +  - ]:           4 :     mtx.vin.resize(inputs.size());
     103   [ -  +  +  + ]:           9 :     for (size_t i{0}; i < inputs.size(); ++i) {
     104                 :           5 :         mtx.vin[i].prevout = inputs[i];
     105                 :             :     }
     106         [ +  - ]:           4 :     mtx.vout.resize(NUM_EPHEMERAL_TX_OUTPUTS);
     107         [ +  + ]:          16 :     for (auto i{0}; i < NUM_EPHEMERAL_TX_OUTPUTS; ++i) {
     108         [ +  - ]:          12 :         mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
     109         [ +  + ]:          20 :         mtx.vout[i].nValue = (i == EPHEMERAL_DUST_INDEX) ? 0 : 10000;
     110                 :             :     }
     111         [ +  - ]:           8 :     return MakeTransactionRef(mtx);
     112                 :           4 : }
     113                 :             : 
     114   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     115                 :             : {
     116         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     117         [ +  - ]:           1 :     LOCK2(cs_main, pool.cs);
     118                 :           1 :     TestMemPoolEntryHelper entry;
     119                 :             : 
     120         [ +  - ]:           1 :     TxValidationState child_state;
     121         [ +  - ]:           1 :     Wtxid child_wtxid;
     122                 :             : 
     123                 :             :     // Arbitrary non-0 feerate for these tests
     124         [ +  - ]:           1 :     CFeeRate dustrelay(DUST_RELAY_TX_FEE);
     125                 :             : 
     126                 :             :     // Basic transaction with dust
     127   [ +  -  +  - ]:           1 :     auto grandparent_tx_1 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
     128         [ +  - ]:           1 :     const auto dust_txid = grandparent_tx_1->GetHash();
     129                 :             : 
     130                 :             :     // Child transaction spending dust
     131   [ +  -  +  -  :           2 :     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   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({dust_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     137   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     138   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     139                 :             : 
     140                 :             :     // Now with dust, ok because the tx has no dusty parents
     141   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     142   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     143   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     144                 :             : 
     145                 :             :     // Dust checks pass
     146   [ +  -  +  -  :           5 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, CFeeRate(0), pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     147   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     148   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     149   [ +  -  +  -  :           5 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     150   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     151   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     152                 :             : 
     153   [ +  -  +  -  :           2 :     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         [ +  - ]:           1 :     const auto dust_non_spend_wtxid{dust_non_spend->GetWitnessHash()};
     157   [ +  -  +  -  :           6 :     BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend, dust_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     158   [ +  -  +  -  :           2 :     BOOST_CHECK(!child_state.IsValid());
                   +  - ]
     159   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
     160                 :           1 :     child_state = TxValidationState();
     161         [ +  - ]:           1 :     child_wtxid = Wtxid();
     162                 :             : 
     163   [ +  -  +  -  :           6 :     BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_spend, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     164   [ +  -  +  -  :           2 :     BOOST_CHECK(!child_state.IsValid());
                   +  - ]
     165   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
     166                 :           1 :     child_state = TxValidationState();
     167         [ +  - ]:           1 :     child_wtxid = Wtxid();
     168                 :             : 
     169   [ +  -  +  -  :           5 :     BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     170   [ +  -  +  -  :           2 :     BOOST_CHECK(!child_state.IsValid());
                   +  - ]
     171   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
     172                 :           1 :     child_state = TxValidationState();
     173         [ +  - ]:           1 :     child_wtxid = Wtxid();
     174                 :             : 
     175   [ +  -  +  - ]:           1 :     auto grandparent_tx_2 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
     176         [ +  - ]:           1 :     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   [ +  -  +  -  :           6 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     180   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     181   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     182                 :             : 
     183   [ +  -  +  -  :           2 :     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   [ +  -  +  -  :           6 :     BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     186   [ +  -  +  -  :           2 :     BOOST_CHECK(!child_state.IsValid());
                   +  - ]
     187   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
     188                 :           1 :     child_state = TxValidationState();
     189         [ +  - ]:           1 :     child_wtxid = Wtxid();
     190                 :             : 
     191   [ +  -  +  -  :           2 :     auto dust_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
                   +  - ]
     192   [ +  -  +  -  :           6 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     193   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     194   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     195                 :             : 
     196                 :             :     // Spending other outputs is also correct, as long as the dusty one is spent
     197         [ +  - ]:           1 :     const std::vector<COutPoint> all_outpoints{COutPoint(dust_txid, 0), COutPoint(dust_txid, 1), COutPoint(dust_txid, 2),
     198         [ +  - ]:           1 :         COutPoint(dust_txid_2, 0), COutPoint(dust_txid_2, 1), COutPoint(dust_txid_2, 2)};
     199         [ +  - ]:           1 :     auto dust_spend_all_outpoints = make_tx(all_outpoints, /*version=*/2);
     200   [ +  -  +  -  :           6 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_all_outpoints}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     201   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     202   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     203                 :             : 
     204                 :             :     // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with no dust
     205   [ +  -  +  -  :           2 :     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   [ +  -  +  -  :           6 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     208   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     209   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     210   [ +  -  +  -  :           2 :     auto child_no_dust = make_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
                   +  - ]
     211   [ +  -  +  -  :           7 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_no_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     212   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     213   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     214                 :             : 
     215                 :             :     // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with dust
     216   [ +  -  +  -  :           2 :     auto child_with_dust = make_ephemeral_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
                   +  - ]
     217   [ +  -  +  -  :           7 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_with_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     218   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     219   [ +  -  +  - ]:           1 :     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   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     225   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     226   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     227                 :             : 
     228                 :             :     // Add first grandparent to mempool and fetch entry
     229   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(grandparent_tx_1));
     230                 :             : 
     231                 :             :     // Ignores ancestors that aren't direct parents
     232   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     233   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     234   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     235                 :             : 
     236                 :             :     // Valid spend of dust with grandparent in mempool
     237   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     238   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     239   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     240                 :             : 
     241                 :             :     // Second grandparent in same package
     242   [ +  -  +  -  :           5 :     BOOST_CHECK(CheckEphemeralSpends({parent_with_dust, grandparent_tx_2}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     243   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     244   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     245                 :             : 
     246                 :             :     // Order in package doesn't matter
     247   [ +  -  +  -  :           5 :     BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     248   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     249   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     250                 :             : 
     251                 :             :     // Add second grandparent to mempool
     252   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(grandparent_tx_2));
     253                 :             : 
     254                 :             :     // Only spends single dust out of two direct parents
     255   [ +  -  +  -  :           4 :     BOOST_CHECK(!CheckEphemeralSpends({dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     256   [ +  -  +  -  :           2 :     BOOST_CHECK(!child_state.IsValid());
                   +  - ]
     257   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
     258                 :           1 :     child_state = TxValidationState();
     259         [ +  - ]:           1 :     child_wtxid = Wtxid();
     260                 :             : 
     261                 :             :     // Spends both parents' dust
     262   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     263   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     264   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
     265                 :             : 
     266                 :             :     // Now add dusty parent to mempool
     267   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(parent_with_dust));
     268                 :             : 
     269                 :             :     // Passes dust checks even with non-parent ancestors
     270   [ +  -  +  -  :           4 :     BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     271   [ +  -  +  -  :           2 :     BOOST_CHECK(child_state.IsValid());
                   +  - ]
     272   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
                   +  - ]
     273   [ +  -  +  -  :          82 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     274                 :             : 
     275   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     276                 :             : {
     277                 :             :     // Test TRUC policy helper functions
     278         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     279         [ +  - ]:           1 :     LOCK2(cs_main, pool.cs);
     280                 :           1 :     TestMemPoolEntryHelper entry;
     281         [ +  - ]:           1 :     std::set<Txid> empty_conflicts_set;
     282                 :           1 :     std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> empty_parents;
     283                 :             : 
     284   [ +  -  +  - ]:           1 :     auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
     285   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(mempool_tx_v3));
     286   [ +  -  +  - ]:           1 :     auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
     287   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(mempool_tx_v2));
     288                 :             : 
     289                 :             :     // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.
     290                 :           1 :     {
     291                 :             :         // mempool_tx_v3
     292                 :             :         //      ^
     293                 :             :         // tx_v2_from_v3
     294   [ +  -  +  -  :           2 :         auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);
                   +  - ]
     295   [ +  -  +  - ]:           1 :         auto parents_v2_from_v3 = pool.GetParents(entry.FromTx(tx_v2_from_v3));
     296         [ +  - ]:           1 :         const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     297   [ +  -  +  - ]:           2 :             tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
     298   [ +  -  +  -  :           3 :             mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
             +  -  +  - ]
     299   [ +  -  +  - ]:           1 :         auto result_v2_from_v3{SingleTRUCChecks(pool, tx_v2_from_v3, parents_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
     300   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
     301   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
     302                 :             : 
     303   [ +  +  +  -  :           3 :         Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
             -  -  -  - ]
     304   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_parents), expected_error_str);
             +  -  +  - ]
     305   [ +  -  +  -  :           3 :         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   [ +  -  +  -  :           2 :         auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
                   +  - ]
     311   [ +  -  +  - ]:           1 :         auto parents_2_from_both{pool.GetParents(entry.FromTx(tx_v2_from_v2_and_v3))};
     312         [ +  - ]:           1 :         const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     313   [ +  -  +  - ]:           2 :             tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
     314   [ +  -  +  -  :           3 :             mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
             +  -  +  - ]
     315   [ +  -  +  - ]:           1 :         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   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
     317   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
     318                 :             : 
     319   [ +  +  +  -  :           4 :         Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
             -  -  -  - ]
     320   [ +  -  +  -  :           1 :         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   [ +  -  +  - ]:           2 :     }
     322                 :             : 
     323                 :             :     // TRUC cannot spend from an unconfirmed non-TRUC transaction.
     324                 :           1 :     {
     325                 :             :         // mempool_tx_v2
     326                 :             :         //      ^
     327                 :             :         // tx_v3_from_v2
     328   [ +  -  +  -  :           2 :         auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     329   [ +  -  +  - ]:           1 :         auto parents_v3_from_v2{pool.GetParents(entry.FromTx(tx_v3_from_v2))};
     330         [ +  - ]:           1 :         const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     331   [ +  -  +  - ]:           2 :             tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
     332   [ +  -  +  -  :           3 :             mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
             +  -  +  - ]
     333   [ +  -  +  - ]:           1 :         auto result_v3_from_v2{SingleTRUCChecks(pool, tx_v3_from_v2, parents_v3_from_v2,  empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
     334   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
     335   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
     336                 :             : 
     337   [ +  +  +  -  :           3 :         Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
             -  -  -  - ]
     338   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_parents), expected_error_str);
             +  -  +  - ]
     339   [ +  -  +  -  :           3 :         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   [ +  -  +  -  :           2 :         auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     345   [ +  -  +  - ]:           1 :         auto parents_v3_from_both{pool.GetParents(entry.FromTx(tx_v3_from_v2_and_v3))};
     346         [ +  - ]:           1 :         const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     347   [ +  -  +  - ]:           2 :             tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
     348   [ +  -  +  -  :           3 :             mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
             +  -  +  - ]
     349   [ +  -  +  - ]:           1 :         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   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
     351   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
     352                 :             : 
     353                 :             :         // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.
     354         [ +  - ]:           1 :         const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
     355   [ +  -  +  -  :           2 :             tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};
                   +  - ]
     356   [ +  +  +  -  :           4 :         Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};
             -  -  -  - ]
     357   [ +  -  +  -  :           1 :         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   [ +  -  +  - ]:           2 :     }
     359                 :             :     // V3 from V3 is ok, and non-V3 from non-V3 is ok.
     360                 :           1 :     {
     361                 :             :         // mempool_tx_v3
     362                 :             :         //      ^
     363                 :             :         // tx_v3_from_v3
     364   [ +  -  +  -  :           2 :         auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     365   [ +  -  +  - ]:           1 :         auto parents_v3{pool.GetParents(entry.FromTx(tx_v3_from_v3))};
     366   [ +  -  +  -  :           2 :         BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_from_v3, parents_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))
          +  -  +  -  +  
                      - ]
     367                 :             :                     == std::nullopt);
     368                 :             : 
     369   [ +  +  +  -  :           3 :         Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};
             -  -  -  - ]
     370   [ +  -  +  -  :           2 :         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   [ +  -  +  -  :           2 :         auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
                   +  - ]
     376   [ +  -  +  - ]:           1 :         auto parents_v2{pool.GetParents(entry.FromTx(tx_v2_from_v2))};
     377   [ +  -  +  -  :           2 :         BOOST_CHECK(SingleTRUCChecks(pool, tx_v2_from_v2, parents_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))
          +  -  +  -  +  
                      - ]
     378                 :             :                     == std::nullopt);
     379                 :             : 
     380   [ +  +  +  -  :           3 :         Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};
             -  -  -  - ]
     381   [ +  -  +  -  :           2 :         BOOST_CHECK(PackageTRUCChecks(pool, tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_parents) == std::nullopt);
             +  -  +  - ]
     382   [ +  -  +  - ]:           2 :     }
     383                 :             : 
     384                 :             :     // Tx spending TRUC cannot have too many mempool ancestors
     385                 :             :     // Configuration where the tx has multiple direct parents.
     386                 :           1 :     {
     387                 :           1 :         Package package_multi_parents;
     388                 :           1 :         std::vector<COutPoint> mempool_outpoints;
     389         [ +  - ]:           1 :         mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
     390         [ +  - ]:           1 :         package_multi_parents.emplace_back(mempool_tx_v3);
     391         [ +  + ]:           3 :         for (size_t i{0}; i < 2; ++i) {
     392   [ +  -  +  - ]:           2 :             auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
     393   [ +  -  +  - ]:           2 :             TryAddToMempool(pool, entry.FromTx(mempool_tx));
     394         [ +  - ]:           2 :             mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
     395         [ +  - ]:           2 :             package_multi_parents.emplace_back(mempool_tx);
     396                 :           2 :         }
     397         [ +  - ]:           1 :         auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);
     398         [ +  - ]:           1 :         package_multi_parents.emplace_back(tx_v3_multi_parent);
     399   [ +  -  +  - ]:           1 :         auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_parent))};
     400   [ +  -  -  +  :           1 :         BOOST_CHECK_EQUAL(parents.size(), 3);
                   +  - ]
     401         [ +  - ]:           1 :         const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
     402   [ +  -  +  -  :           2 :             tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
                   +  - ]
     403   [ +  -  +  - ]:           1 :         auto result{SingleTRUCChecks(pool, tx_v3_multi_parent, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
     404   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->first, expected_error_str);
     405   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->second, nullptr);
     406                 :             : 
     407   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_parents),
             +  -  +  - ]
     408                 :             :                           expected_error_str);
     409         [ +  - ]:           2 :     }
     410                 :             : 
     411                 :             :     // Configuration where the tx is in a multi-generation chain.
     412                 :           1 :     {
     413                 :           1 :         Package package_multi_gen;
     414                 :           1 :         CTransactionRef middle_tx;
     415         [ +  - ]:           1 :         auto last_outpoint{random_outpoints(1)[0]};
     416         [ +  + ]:           3 :         for (size_t i{0}; i < 2; ++i) {
     417   [ +  -  +  -  :           4 :             auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
                   +  - ]
     418   [ +  -  +  - ]:           2 :             TryAddToMempool(pool, entry.FromTx(mempool_tx));
     419         [ +  - ]:           2 :             last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
     420         [ +  - ]:           2 :             package_multi_gen.emplace_back(mempool_tx);
     421         [ +  + ]:           2 :             if (i == 1) middle_tx = mempool_tx;
     422                 :           2 :         }
     423   [ +  -  +  -  :           2 :         auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);
                   +  - ]
     424         [ +  - ]:           1 :         package_multi_gen.emplace_back(tx_v3_multi_gen);
     425   [ +  -  +  - ]:           1 :         auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_gen))};
     426         [ +  - ]:           1 :         const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
     427   [ +  -  +  -  :           2 :             tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
                   +  - ]
     428   [ +  -  +  - ]:           1 :         auto result{SingleTRUCChecks(pool, tx_v3_multi_gen, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
     429   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->first, expected_error_str);
     430   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->second, nullptr);
     431                 :             : 
     432                 :             :         // Middle tx is what triggers a failure for the grandchild:
     433   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_parents), expected_error_str);
             +  -  +  - ]
     434   [ +  -  +  -  :           2 :         BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_parents) == std::nullopt);
             +  -  +  - ]
     435   [ +  -  +  - ]:           3 :     }
     436                 :             : 
     437                 :             :     // Tx spending TRUC cannot be too large in virtual size.
     438         [ +  - ]:           1 :     auto many_inputs{random_outpoints(100)};
     439         [ +  - ]:           1 :     many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);
     440                 :           1 :     {
     441         [ +  - ]:           1 :         auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);
     442         [ +  - ]:           1 :         const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};
     443   [ +  -  +  - ]:           1 :         auto parents{pool.GetParents(entry.FromTx(tx_v3_child_big))};
     444         [ +  - ]:           1 :         const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     445   [ +  -  +  -  :           2 :             tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};
                   +  - ]
     446   [ +  -  +  - ]:           1 :         auto result{SingleTRUCChecks(pool, tx_v3_child_big, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
     447   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->first, expected_error_str);
     448   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->second, nullptr);
     449                 :             : 
     450   [ +  +  +  -  :           3 :         Package package_child_big{mempool_tx_v3, tx_v3_child_big};
             -  -  -  - ]
     451   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_parents),
             +  -  +  - ]
     452                 :             :                           expected_error_str);
     453         [ +  - ]:           1 :     }
     454                 :             : 
     455                 :             :     // Tx spending TRUC cannot have too many sigops.
     456                 :             :     // This child has 10 P2WSH multisig inputs.
     457         [ +  - ]:           1 :     auto multisig_outpoints{random_outpoints(10)};
     458         [ +  - ]:           1 :     multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
     459         [ +  - ]:           1 :     auto keys{random_keys(2)};
     460                 :           1 :     CScript script_multisig;
     461         [ +  - ]:           1 :     script_multisig << OP_1;
     462         [ +  + ]:           3 :     for (const auto& key : keys) {
     463         [ +  - ]:           4 :         script_multisig << ToByteVector(key);
     464                 :             :     }
     465   [ +  -  +  - ]:           1 :     script_multisig << OP_2 << OP_CHECKMULTISIG;
     466                 :           1 :     {
     467         [ +  - ]:           1 :         CMutableTransaction mtx_many_sigops = CMutableTransaction{};
     468                 :           1 :         mtx_many_sigops.version = TRUC_VERSION;
     469         [ +  + ]:          12 :         for (const auto& outpoint : multisig_outpoints) {
     470         [ +  - ]:          11 :             mtx_many_sigops.vin.emplace_back(outpoint);
     471   [ +  -  +  - ]:          22 :             mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());
     472                 :             :         }
     473         [ +  - ]:           1 :         mtx_many_sigops.vout.resize(1);
     474         [ +  - ]:           1 :         mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;
     475                 :           1 :         mtx_many_sigops.vout.back().nValue = 10000;
     476         [ +  - ]:           1 :         auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};
     477                 :             : 
     478   [ +  -  +  - ]:           1 :         auto parents{pool.GetParents(entry.FromTx(tx_many_sigops))};
     479                 :             :         // legacy uses fAccurate = false, and the maximum number of multisig keys is used
     480   [ -  +  +  - ]:           1 :         const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};
     481   [ +  -  -  +  :           1 :         BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);
                   +  - ]
     482         [ +  - ]:           1 :         const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};
     483                 :             :         // Weight limit is not reached...
     484   [ +  -  +  -  :           2 :         BOOST_CHECK(SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set, bip141_vsize) == std::nullopt);
             +  -  +  - ]
     485                 :             :         // ...but sigop limit is.
     486                 :           1 :         const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     487   [ +  -  +  - ]:           2 :             tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
     488   [ +  -  +  - ]:           2 :             total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, TRUC_CHILD_MAX_VSIZE)};
     489                 :           1 :         auto result{SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set,
     490   [ +  -  +  - ]:           1 :                                         GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
     491   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->first, expected_error_str);
     492   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result->second, nullptr);
     493                 :             : 
     494   [ +  +  +  -  :           3 :         Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
             -  -  -  - ]
     495   [ +  -  +  -  :           1 :         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         [ +  - ]:           2 :     }
     498                 :             : 
     499                 :             :     // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.
     500   [ +  -  +  -  :           2 :     auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     501                 :           1 :     {
     502   [ +  -  +  -  :           2 :         BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR);
                   +  - ]
     503   [ +  -  +  - ]:           1 :         auto parents{pool.GetParents(entry.FromTx(tx_mempool_v3_child))};
     504   [ +  -  +  -  :           2 :         BOOST_CHECK(SingleTRUCChecks(pool, tx_mempool_v3_child, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
          +  -  +  -  +  
                      - ]
     505   [ +  -  +  - ]:           1 :         TryAddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
     506                 :             : 
     507   [ +  +  +  -  :           3 :         Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
             -  -  -  - ]
     508   [ +  -  +  -  :           2 :         BOOST_CHECK(PackageTRUCChecks(pool, tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_parents) == std::nullopt);
             +  -  +  - ]
     509                 :           1 :     }
     510                 :             : 
     511                 :             :     // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
     512                 :           1 :     {
     513   [ +  -  +  -  :           2 :         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   [ +  -  +  - ]:           1 :         auto parents_1sibling{pool.GetParents(entry.FromTx(tx_v3_child2))};
     516         [ +  - ]:           1 :         const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     517   [ +  -  +  -  :           2 :             mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
                   +  - ]
     518   [ +  -  +  - ]:           1 :         auto result_with_sibling_eviction{SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
     519   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
     520                 :             :         // The other mempool child is returned to allow for sibling eviction.
     521   [ +  -  +  - ]:           1 :         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   [ +  -  +  -  :           2 :         BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
          +  -  +  -  +  
                -  +  - ]
     525                 :             :                     == std::nullopt);
     526                 :             : 
     527   [ +  +  +  -  :           4 :         Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
             -  -  -  - ]
     528   [ +  -  +  -  :           1 :         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   [ +  -  +  - ]:           1 :         TryAddToMempool(pool, entry.FromTx(tx_v3_child2));
     533   [ +  -  +  -  :           2 :         auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
                   +  - ]
     534         [ +  - ]:           1 :         auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash()).value();
     535   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(pool.GetDescendantCount(entry_mempool_parent), 3);
                   +  - ]
     536   [ +  -  +  - ]:           1 :         auto parents_2siblings{pool.GetParents(entry.FromTx(tx_v3_child3))};
     537                 :             : 
     538   [ +  -  +  - ]:           1 :         auto result_2children{SingleTRUCChecks(pool, tx_v3_child3, parents_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
     539   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
     540                 :             :         // The other mempool child is not returned because sibling eviction is not allowed.
     541   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_2children->second, nullptr);
     542   [ +  -  +  - ]:           2 :     }
     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                 :           1 :     {
     546   [ +  -  +  - ]:           1 :         auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
     547   [ +  -  +  -  :           2 :         auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     548   [ +  -  +  -  :           2 :         auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
                   +  - ]
     549   [ +  -  +  -  :           2 :         auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
                   +  - ]
     550                 :             : 
     551   [ +  -  +  - ]:           1 :         TryAddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
     552   [ +  -  +  - ]:           1 :         TryAddToMempool(pool, entry.FromTx(tx_mempool_sibling));
     553   [ +  -  +  - ]:           1 :         TryAddToMempool(pool, entry.FromTx(tx_mempool_nibling));
     554                 :             : 
     555   [ +  -  +  - ]:           1 :         auto parents_3gen{pool.GetParents(entry.FromTx(tx_to_submit))};
     556         [ +  - ]:           1 :         const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     557   [ +  -  +  -  :           2 :             tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
                   +  - ]
     558   [ +  -  +  - ]:           1 :         auto result_3gen{SingleTRUCChecks(pool, tx_to_submit, parents_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
     559   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
     560                 :             :         // The other mempool child is not returned because sibling eviction is not allowed.
     561   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
     562   [ +  -  +  -  :           5 :     }
          +  -  +  -  +  
                      - ]
     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   [ +  -  +  -  :          42 : }
          -  +  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  +  -  +  
          -  -  +  +  -  
          +  -  -  +  +  
          -  +  -  -  +  
          +  -  +  -  -  
          +  +  -  +  -  
          -  +  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
                -  +  - ]
     567                 :             : 
     568                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1