LCOV - code coverage report
Current view: top level - src/test - txdownload_tests.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 95.8 % 192 184
Test Date: 2025-01-19 05:08:01 Functions: 100.0 % 8 8
Branches: 52.7 % 552 291

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2011-2022 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 <addresstype.h>
       6                 :             : #include <consensus/validation.h>
       7                 :             : #include <net_processing.h>
       8                 :             : #include <node/txdownloadman_impl.h>
       9                 :             : #include <primitives/transaction.h>
      10                 :             : #include <script/script.h>
      11                 :             : #include <test/util/random.h>
      12                 :             : #include <test/util/setup_common.h>
      13                 :             : #include <validation.h>
      14                 :             : 
      15                 :             : #include <array>
      16                 :             : 
      17                 :             : #include <boost/test/unit_test.hpp>
      18                 :             : 
      19                 :             : BOOST_FIXTURE_TEST_SUITE(txdownload_tests, TestingSetup)
      20                 :             : 
      21                 :             : struct Behaviors {
      22                 :             :     bool m_txid_in_rejects;
      23                 :             :     bool m_wtxid_in_rejects;
      24                 :             :     bool m_txid_in_rejects_recon;
      25                 :             :     bool m_wtxid_in_rejects_recon;
      26                 :             :     bool m_keep_for_compact;
      27                 :             :     bool m_ignore_inv_txid;
      28                 :             :     bool m_ignore_inv_wtxid;
      29                 :             : 
      30                 :             :     // Constructor. We are passing and casting ints because they are more readable in a table (see expected_behaviors).
      31                 :          48 :     Behaviors(bool txid_rejects, bool wtxid_rejects, bool txid_recon, bool wtxid_recon, bool keep, bool txid_inv, bool wtxid_inv) :
      32                 :          48 :         m_txid_in_rejects(txid_rejects),
      33                 :          48 :         m_wtxid_in_rejects(wtxid_rejects),
      34                 :          48 :         m_txid_in_rejects_recon(txid_recon),
      35                 :          48 :         m_wtxid_in_rejects_recon(wtxid_recon),
      36                 :          48 :         m_keep_for_compact(keep),
      37                 :          48 :         m_ignore_inv_txid(txid_inv),
      38                 :          48 :         m_ignore_inv_wtxid(wtxid_inv)
      39                 :             :     {}
      40                 :             : 
      41                 :          48 :     void CheckEqual(const Behaviors& other, bool segwit)
      42                 :             :     {
      43         [ +  - ]:          48 :         BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects,       m_wtxid_in_rejects);
      44         [ +  - ]:          48 :         BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects_recon, m_wtxid_in_rejects_recon);
      45         [ +  - ]:          48 :         BOOST_CHECK_EQUAL(other.m_keep_for_compact,       m_keep_for_compact);
      46         [ +  - ]:          48 :         BOOST_CHECK_EQUAL(other.m_ignore_inv_wtxid,       m_ignore_inv_wtxid);
      47                 :             : 
      48                 :             :         // false negatives for nonsegwit transactions, since txid == wtxid.
      49         [ +  + ]:          48 :         if (segwit) {
      50         [ +  - ]:          24 :             BOOST_CHECK_EQUAL(other.m_txid_in_rejects,        m_txid_in_rejects);
      51         [ +  - ]:          24 :             BOOST_CHECK_EQUAL(other.m_txid_in_rejects_recon,  m_txid_in_rejects_recon);
      52         [ +  - ]:          24 :             BOOST_CHECK_EQUAL(other.m_ignore_inv_txid,        m_ignore_inv_txid);
      53                 :             :         }
      54                 :          48 :     }
      55                 :             : };
      56                 :             : 
      57                 :             : // Map from failure reason to expected behavior for a segwit tx that fails
      58                 :             : // Txid and Wtxid are assumed to be different here. For a nonsegwit transaction, use the wtxid results.
      59                 :             : static std::map<TxValidationResult, Behaviors> expected_behaviors{
      60                 :             :     {TxValidationResult::TX_CONSENSUS,               {/*txid_rejects*/0,/*wtxid_rejects*/1,/*txid_recon*/0,/*wtxid_recon*/0,/*keep*/1,/*txid_inv*/0,/*wtxid_inv*/1}},
      61                 :             :     {TxValidationResult::TX_INPUTS_NOT_STANDARD,     {                1,                 1,              0,               0,        1,            1,             1}},
      62                 :             :     {TxValidationResult::TX_NOT_STANDARD,            {                0,                 1,              0,               0,        1,            0,             1}},
      63                 :             :     {TxValidationResult::TX_MISSING_INPUTS,          {                0,                 0,              0,               0,        1,            0,             1}},
      64                 :             :     {TxValidationResult::TX_PREMATURE_SPEND,         {                0,                 1,              0,               0,        1,            0,             1}},
      65                 :             :     {TxValidationResult::TX_WITNESS_MUTATED,         {                0,                 1,              0,               0,        1,            0,             1}},
      66                 :             :     {TxValidationResult::TX_WITNESS_STRIPPED,        {                0,                 0,              0,               0,        0,            0,             0}},
      67                 :             :     {TxValidationResult::TX_CONFLICT,                {                0,                 1,              0,               0,        1,            0,             1}},
      68                 :             :     {TxValidationResult::TX_MEMPOOL_POLICY,          {                0,                 1,              0,               0,        1,            0,             1}},
      69                 :             :     {TxValidationResult::TX_NO_MEMPOOL,              {                0,                 1,              0,               0,        1,            0,             1}},
      70                 :             :     {TxValidationResult::TX_RECONSIDERABLE,          {                0,                 0,              0,               1,        1,            0,             1}},
      71                 :             :     {TxValidationResult::TX_UNKNOWN,                 {                0,                 1,              0,               0,        1,            0,             1}},
      72                 :             : };
      73                 :             : 
      74                 :          21 : static bool CheckOrphanBehavior(node::TxDownloadManagerImpl& txdownload_impl, const CTransactionRef& tx, const node::RejectedTxTodo& ret, std::string& err_msg,
      75                 :             :                                 bool expect_orphan, bool expect_keep, unsigned int expected_parents)
      76                 :             : {
      77                 :             :     // Missing inputs can never result in a PackageToValidate.
      78         [ -  + ]:          21 :     if (ret.m_package_to_validate.has_value()) {
      79                 :           0 :         err_msg = strprintf("returned a PackageToValidate on missing inputs");
      80                 :           0 :         return false;
      81                 :             :     }
      82                 :             : 
      83         [ -  + ]:          21 :     if (expect_orphan != txdownload_impl.m_orphanage.HaveTx(tx->GetWitnessHash())) {
      84         [ #  # ]:           0 :         err_msg = strprintf("unexpectedly %s tx in orphanage", expect_orphan ? "did not find" : "found");
      85                 :           0 :         return false;
      86                 :             :     }
      87         [ -  + ]:          21 :     if (expect_keep != ret.m_should_add_extra_compact_tx) {
      88         [ #  # ]:           0 :         err_msg = strprintf("unexpectedly returned %s add to vExtraTxnForCompact", expect_keep ? "should not" : "should");
      89                 :           0 :         return false;
      90                 :             :     }
      91         [ -  + ]:          21 :     if (expected_parents != ret.m_unique_parents.size()) {
      92                 :           0 :         err_msg = strprintf("expected %u unique_parents, got %u", expected_parents, ret.m_unique_parents.size());
      93                 :           0 :         return false;
      94                 :             :     }
      95                 :             :     return true;
      96                 :             : }
      97                 :             : 
      98                 :           8 : static CTransactionRef CreatePlaceholderTx(bool segwit)
      99                 :             : {
     100                 :             :     // Each tx returned from here spends the previous one.
     101   [ +  +  +  - ]:           8 :     static Txid prevout_hash{};
     102                 :             : 
     103                 :           8 :     CMutableTransaction mtx;
     104         [ +  - ]:           8 :     mtx.vin.emplace_back(prevout_hash, 0);
     105                 :             :     // This makes txid != wtxid
     106   [ +  +  +  - ]:          12 :     if (segwit) mtx.vin[0].scriptWitness.stack.push_back({1});
     107         [ +  - ]:           8 :     mtx.vout.emplace_back(CENT, CScript());
     108         [ +  - ]:           8 :     auto ptx = MakeTransactionRef(mtx);
     109                 :           8 :     prevout_hash = ptx->GetHash();
     110                 :           8 :     return ptx;
     111                 :           8 : }
     112                 :             : 
     113   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(tx_rejection_types, TestChain100Setup)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     114                 :             : {
     115                 :           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     116                 :           1 :     FastRandomContext det_rand{true};
     117                 :           1 :     node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, DEFAULT_MAX_ORPHAN_TRANSACTIONS, true};
     118                 :             : 
     119                 :             :     // A new TxDownloadManagerImpl is created for each tx so we can just reuse the same one.
     120         [ +  - ]:           1 :     TxValidationState state;
     121                 :           1 :     NodeId nodeid{0};
     122         [ +  - ]:           1 :     std::chrono::microseconds now{GetTime()};
     123                 :           1 :     node::TxDownloadConnectionInfo connection_info{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
     124                 :             : 
     125         [ +  + ]:           3 :     for (const auto segwit_parent : {true, false}) {
     126         [ +  + ]:           6 :         for (const auto segwit_child : {true, false}) {
     127         [ +  - ]:           4 :             const auto ptx_parent = CreatePlaceholderTx(segwit_parent);
     128         [ +  - ]:           4 :             const auto ptx_child = CreatePlaceholderTx(segwit_child);
     129                 :           4 :             const auto& parent_txid = ptx_parent->GetHash().ToUint256();
     130                 :           4 :             const auto& parent_wtxid = ptx_parent->GetWitnessHash().ToUint256();
     131                 :           4 :             const auto& child_txid = ptx_child->GetHash().ToUint256();
     132                 :           4 :             const auto& child_wtxid = ptx_child->GetWitnessHash().ToUint256();
     133                 :             : 
     134   [ +  -  +  + ]:          52 :             for (const auto& [result, expected_behavior] : expected_behaviors) {
     135         [ +  - ]:          48 :                 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     136         [ +  - ]:          48 :                 txdownload_impl.ConnectedPeer(nodeid, connection_info);
     137                 :             :                 // Parent failure
     138   [ +  -  +  -  :          96 :                 state.Invalid(result, "");
                   +  - ]
     139         [ +  - ]:          48 :                 const auto& [keep, unique_txids, package_to_validate] = txdownload_impl.MempoolRejectedTx(ptx_parent, state, nodeid, /*first_time_failure=*/true);
     140                 :             : 
     141                 :             :                 // No distinction between txid and wtxid caching for nonsegwit transactions, so only test these specific
     142                 :             :                 // behaviors for segwit transactions.
     143                 :          48 :                 Behaviors actual_behavior{
     144   [ +  -  +  - ]:          48 :                     /*txid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_txid),
     145   [ +  -  +  - ]:          48 :                     /*wtxid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_wtxid),
     146   [ +  -  +  - ]:          48 :                     /*txid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_txid),
     147   [ +  -  +  - ]:          48 :                     /*wtxid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_wtxid),
     148                 :             :                     /*keep=*/keep,
     149         [ +  - ]:          48 :                     /*txid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, GenTxid::Txid(parent_txid), now),
     150         [ +  - ]:          48 :                     /*wtxid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, GenTxid::Wtxid(parent_wtxid), now),
     151   [ +  -  +  - ]:          48 :                 };
     152   [ +  -  +  -  :          72 :                 BOOST_TEST_MESSAGE("Testing behavior for " << result << (segwit_parent ? " segwit " : " nonsegwit"));
             +  +  +  - ]
     153         [ +  - ]:          48 :                 actual_behavior.CheckEqual(expected_behavior, /*segwit=*/segwit_parent);
     154                 :             : 
     155                 :             :                 // Later, a child of this transaction fails for missing inputs
     156   [ +  -  +  -  :          96 :                 state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");
                   +  - ]
     157         [ +  - ]:          48 :                 txdownload_impl.MempoolRejectedTx(ptx_child, state, nodeid, /*first_time_failure=*/true);
     158                 :             : 
     159                 :             :                 // If parent (by txid) was rejected, child is too.
     160         [ +  + ]:          48 :                 const bool parent_txid_rejected{segwit_parent ? expected_behavior.m_txid_in_rejects : expected_behavior.m_wtxid_in_rejects};
     161   [ +  -  +  -  :          48 :                 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_txid));
             +  -  +  - ]
     162   [ +  -  +  -  :          48 :                 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid));
             +  -  +  - ]
     163                 :             : 
     164                 :             :                 // Unless rejected, the child should be in orphanage.
     165   [ +  -  +  -  :          48 :                 BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage.HaveTx(ptx_child->GetWitnessHash()));
                   +  - ]
     166                 :          48 :             }
     167         [ +  - ]:           8 :         }
     168                 :             :     }
     169                 :           1 : }
     170                 :             : 
     171   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(handle_missing_inputs, TestChain100Setup)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     172                 :             : {
     173                 :           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     174                 :           1 :     FastRandomContext det_rand{true};
     175                 :           1 :     node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, DEFAULT_MAX_ORPHAN_TRANSACTIONS, true};
     176                 :           1 :     NodeId nodeid{1};
     177                 :           1 :     node::TxDownloadConnectionInfo DEFAULT_CONN{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
     178                 :             : 
     179                 :             :     // We need mature coinbases
     180         [ +  - ]:           1 :     mineBlocks(20);
     181                 :             : 
     182                 :             :     // Transactions with missing inputs are treated differently depending on how much we know about
     183                 :             :     // their parents.
     184                 :           1 :     CKey wallet_key = GenerateRandomKey();
     185   [ +  -  +  -  :           1 :     CScript destination = GetScriptForDestination(PKHash(wallet_key.GetPubKey()));
                   +  - ]
     186                 :             :     // Amount for spending coinbase in a 1-in-1-out tx, at depth n, each time deducting 1000 from the amount as fees.
     187                 :           1 :     CAmount amount_depth_1{50 * COIN - 1000};
     188                 :           1 :     CAmount amount_depth_2{amount_depth_1 - 1000};
     189                 :             :     // Amount for spending coinbase in a 1-in-2-out tx, deducting 1000 in fees
     190                 :           1 :     CAmount amount_split_half{25 * COIN - 500};
     191                 :           1 :     int test_chain_height{100};
     192                 :             : 
     193         [ +  - ]:           1 :     TxValidationState state_orphan;
     194   [ +  -  +  -  :           2 :     state_orphan.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");
                   +  - ]
     195                 :             : 
     196                 :             :     // Transactions are not all submitted to mempool. Conserve the number of m_coinbase_txns we
     197                 :             :     // consume, and only increment this index number when we would conflict with an existing
     198                 :             :     // mempool transaction.
     199                 :           1 :     size_t coinbase_idx{0};
     200                 :             : 
     201         [ +  + ]:          17 :     for (int decisions = 0; decisions < (1 << 4); ++decisions) {
     202   [ +  -  +  -  :          48 :         auto mtx_single_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx], /*input_vout=*/0, test_chain_height, coinbaseKey, destination, amount_depth_1, /*submit=*/false);
                   +  - ]
     203         [ +  - ]:          16 :         auto single_parent = MakeTransactionRef(mtx_single_parent);
     204                 :             : 
     205   [ +  -  +  - ]:          48 :         auto mtx_orphan = CreateValidMempoolTransaction(single_parent, /*input_vout=*/0, test_chain_height, wallet_key, destination, amount_depth_2, /*submit=*/false);
     206         [ +  - ]:          16 :         auto orphan = MakeTransactionRef(mtx_orphan);
     207                 :             : 
     208         [ +  - ]:          16 :         node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     209         [ +  - ]:          16 :         txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
     210                 :             : 
     211                 :             :         // Each bit of decisions tells us whether the parent is in a particular cache.
     212                 :             :         // It is definitely possible for a transaction to be in multiple caches. For example, it
     213                 :             :         // may have both a low feerate and found to violate some mempool policy when validated
     214                 :             :         // in a 1p1c.
     215                 :          16 :         const bool parent_recent_rej(decisions & 1);
     216                 :          16 :         const bool parent_recent_rej_recon((decisions >> 1) & 1);
     217                 :          16 :         const bool parent_recent_conf((decisions >> 2) & 1);
     218                 :          16 :         const bool parent_in_mempool((decisions >> 3) & 1);
     219                 :             : 
     220   [ +  +  +  -  :          16 :         if (parent_recent_rej) txdownload_impl.RecentRejectsFilter().insert(single_parent->GetHash().ToUint256());
                   +  - ]
     221   [ +  +  +  -  :          16 :         if (parent_recent_rej_recon) txdownload_impl.RecentRejectsReconsiderableFilter().insert(single_parent->GetHash().ToUint256());
                   +  - ]
     222   [ +  +  +  -  :          16 :         if (parent_recent_conf) txdownload_impl.RecentConfirmedTransactionsFilter().insert(single_parent->GetHash().ToUint256());
                   +  - ]
     223         [ +  + ]:          16 :         if (parent_in_mempool) {
     224         [ +  - ]:          24 :             const auto mempool_result = WITH_LOCK(::cs_main, return m_node.chainman->ProcessTransaction(single_parent));
     225   [ +  -  +  -  :          16 :             BOOST_CHECK(mempool_result.m_result_type == MempoolAcceptResult::ResultType::VALID);
                   -  + ]
     226                 :           8 :             coinbase_idx += 1;
     227         [ -  + ]:           8 :             assert(coinbase_idx < m_coinbase_txns.size());
     228                 :           8 :         }
     229                 :             : 
     230                 :             :         // Whether or not the transaction is added as an orphan depends solely on whether or not
     231                 :             :         // it's in RecentRejectsFilter. Specifically, the parent is allowed to be in
     232                 :             :         // RecentRejectsReconsiderableFilter, but it cannot be in RecentRejectsFilter.
     233                 :          16 :         const bool expect_keep_orphan = !parent_recent_rej;
     234   [ +  +  +  + ]:          16 :         const unsigned int expected_parents = parent_recent_rej || parent_recent_conf || parent_in_mempool ? 0 : 1;
     235                 :             :         // If we don't expect to keep the orphan then expected_parents is 0.
     236                 :             :         // !expect_keep_orphan => (expected_parents == 0)
     237   [ +  -  +  -  :          32 :         BOOST_CHECK(expect_keep_orphan || expected_parents == 0);
                   +  - ]
     238         [ +  - ]:          16 :         const auto ret_1p1c = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
     239         [ +  - ]:          16 :         std::string err_msg;
     240         [ +  - ]:          16 :         const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c, err_msg,
     241                 :             :                                             /*expect_orphan=*/expect_keep_orphan, /*expect_keep=*/true, /*expected_parents=*/expected_parents);
     242   [ +  -  +  - ]:          32 :         BOOST_CHECK_MESSAGE(ok, err_msg);
     243   [ +  -  +  - ]:          80 :     }
     244                 :             : 
     245                 :             :     // Orphan with multiple parents
     246                 :           1 :     {
     247                 :           1 :         std::vector<CTransactionRef> parents;
     248                 :           1 :         std::vector<COutPoint> outpoints;
     249                 :           1 :         int32_t num_parents{24};
     250         [ +  + ]:          25 :         for (int32_t i = 0; i < num_parents; ++i) {
     251         [ -  + ]:          24 :             assert(coinbase_idx < m_coinbase_txns.size());
     252         [ +  - ]:          48 :             auto mtx_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx++], /*input_vout=*/0, test_chain_height,
     253   [ +  -  +  - ]:          48 :                                                             coinbaseKey, destination, amount_depth_1 + i, /*submit=*/false);
     254         [ +  - ]:          24 :             auto ptx_parent = MakeTransactionRef(mtx_parent);
     255         [ +  - ]:          24 :             parents.emplace_back(ptx_parent);
     256         [ +  - ]:          24 :             outpoints.emplace_back(ptx_parent->GetHash(), 0);
     257                 :          48 :         }
     258                 :             : 
     259                 :             :         // Send all coins to 1 output.
     260   [ +  -  +  -  :           5 :         auto mtx_orphan = CreateValidMempoolTransaction(parents, outpoints, test_chain_height, {wallet_key}, {{amount_depth_2 * num_parents, destination}}, /*submit=*/false);
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     261         [ +  - ]:           1 :         auto orphan = MakeTransactionRef(mtx_orphan);
     262                 :             : 
     263                 :             :         // 1 parent in RecentRejectsReconsiderableFilter, the rest are unknown
     264                 :           1 :         {
     265         [ +  - ]:           1 :             node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     266         [ +  - ]:           1 :             txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
     267                 :             : 
     268   [ +  -  +  - ]:           1 :             txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
     269         [ +  - ]:           1 :             const auto ret_1p1c_parent_reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
     270         [ +  - ]:           1 :             std::string err_msg;
     271         [ +  - ]:           1 :             const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_parent_reconsiderable, err_msg,
     272                 :             :                                                 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/num_parents);
     273   [ +  -  +  - ]:           2 :             BOOST_CHECK_MESSAGE(ok, err_msg);
     274                 :           1 :         }
     275                 :             : 
     276                 :             :         // 1 parent in RecentRejectsReconsiderableFilter, the rest are confirmed
     277                 :           1 :         {
     278         [ +  - ]:           1 :             node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     279         [ +  - ]:           1 :             txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
     280                 :             : 
     281   [ +  -  +  - ]:           1 :             txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
     282         [ +  + ]:          24 :             for (int32_t i = 1; i < num_parents; ++i) {
     283   [ +  -  +  - ]:          23 :                 txdownload_impl.RecentConfirmedTransactionsFilter().insert(parents[i]->GetHash().ToUint256());
     284                 :             :             }
     285                 :           1 :             const unsigned int expected_parents = 1;
     286                 :             : 
     287         [ +  - ]:           1 :             const auto ret_1recon_conf = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
     288         [ +  - ]:           1 :             std::string err_msg;
     289         [ +  - ]:           1 :             const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1recon_conf, err_msg,
     290                 :             :                                                 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/expected_parents);
     291   [ +  -  +  - ]:           2 :             BOOST_CHECK_MESSAGE(ok, err_msg);
     292                 :           1 :         }
     293                 :             : 
     294                 :             :         // 1 parent in RecentRejectsReconsiderableFilter, 1 other in {RecentRejectsReconsiderableFilter, RecentRejectsFilter}
     295         [ +  + ]:           3 :         for (int i = 0; i < 2; ++i) {
     296         [ +  - ]:           2 :             node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     297         [ +  - ]:           2 :             txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
     298                 :             : 
     299   [ +  -  +  - ]:           2 :             txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[1]->GetHash().ToUint256());
     300                 :             : 
     301                 :             :             // Doesn't really matter which parent
     302         [ +  + ]:           2 :             auto& alreadyhave_parent = parents[0];
     303         [ +  + ]:           2 :             if (i == 0) {
     304   [ +  -  +  - ]:           1 :                 txdownload_impl.RecentRejectsReconsiderableFilter().insert(alreadyhave_parent->GetHash().ToUint256());
     305         [ +  - ]:           1 :             } else if (i == 1) {
     306   [ +  -  +  - ]:           1 :                 txdownload_impl.RecentRejectsFilter().insert(alreadyhave_parent->GetHash().ToUint256());
     307                 :             :             }
     308                 :             : 
     309         [ +  - ]:           2 :             const auto ret_2_problems = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
     310         [ +  - ]:           2 :             std::string err_msg;
     311         [ +  - ]:           2 :             const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_2_problems, err_msg,
     312                 :             :                                                 /*expect_orphan=*/false, /*expect_keep=*/true, /*expected_parents=*/0);
     313   [ +  -  +  - ]:           4 :             BOOST_CHECK_MESSAGE(ok, err_msg);
     314                 :           2 :         }
     315                 :           2 :     }
     316                 :             : 
     317                 :             :     // Orphan with multiple inputs spending from a single parent
     318                 :           1 :     {
     319         [ -  + ]:           1 :         assert(coinbase_idx < m_coinbase_txns.size());
     320   [ +  -  +  -  :          10 :         auto parent_2outputs = MakeTransactionRef(CreateValidMempoolTransaction({m_coinbase_txns[coinbase_idx]}, {{m_coinbase_txns[coinbase_idx]->GetHash(), 0}}, test_chain_height, {coinbaseKey},
          +  -  +  -  +  
          -  +  +  +  -  
          +  +  +  +  -  
          -  -  -  -  -  
                   -  - ]
     321                 :           1 :                                                              {{amount_split_half, destination}, {amount_split_half, destination}}, /*submit=*/false));
     322                 :             : 
     323   [ +  -  +  -  :           8 :         auto orphan = MakeTransactionRef(CreateValidMempoolTransaction({parent_2outputs}, {{parent_2outputs->GetHash(), 0}, {parent_2outputs->GetHash(), 1}},
          +  -  +  -  +  
          +  +  -  +  +  
          +  +  -  -  -  
             -  -  -  -  
                      - ]
     324                 :           1 :                                                                        test_chain_height, {wallet_key}, {{amount_depth_2, destination}}, /*submit=*/false));
     325                 :             :         // Parent is in RecentRejectsReconsiderableFilter. Inputs will find it twice, but this
     326                 :             :         // should only counts as 1 parent in the filter.
     327                 :           1 :         {
     328         [ +  - ]:           1 :             node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
     329         [ +  - ]:           1 :             txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
     330                 :             : 
     331   [ +  -  +  - ]:           1 :             txdownload_impl.RecentRejectsReconsiderableFilter().insert(parent_2outputs->GetHash().ToUint256());
     332         [ +  - ]:           1 :             const auto ret_1p1c_2reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
     333         [ +  - ]:           1 :             std::string err_msg;
     334         [ +  - ]:           1 :             const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_2reconsiderable, err_msg,
     335                 :             :                                                 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/1);
     336   [ +  -  +  - ]:           2 :             BOOST_CHECK_MESSAGE(ok, err_msg);
     337         [ +  - ]:           1 :         }
     338         [ +  - ]:           2 :     }
     339   [ +  -  +  -  :          10 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                -  -  - ]
     340                 :             : 
     341                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1