LCOV - code coverage report
Current view: top level - src/test - orphanage_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 111 112 99.1 %
Date: 2026-06-25 07:23:43 Functions: 24 24 100.0 %

          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 <arith_uint256.h>
       6             : #include <pubkey.h>
       7             : #include <script/sign.h>
       8             : #include <script/signingprovider.h>
       9             : #include <script/standard.h>
      10             : #include <test/util/random.h>
      11             : #include <test/util/setup_common.h>
      12             : #include <txorphanage.h>
      13             : 
      14             : #include <array>
      15             : #include <cstdint>
      16             : 
      17             : #include <boost/test/unit_test.hpp>
      18             : 
      19         146 : BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
      20             : 
      21             : class TxOrphanageTest : public TxOrphanage
      22             : {
      23             : public:
      24           9 :     inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      25             :     {
      26           9 :         LOCK(m_mutex);
      27           9 :         return m_orphans.size();
      28           9 :     }
      29             : 
      30          60 :     CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      31             :     {
      32          60 :         LOCK(m_mutex);
      33          60 :         std::map<uint256, OrphanTx>::iterator it;
      34          60 :         it = m_orphans.lower_bound(InsecureRand256());
      35          60 :         if (it == m_orphans.end())
      36           0 :             it = m_orphans.begin();
      37          60 :         return it->second.tx;
      38          60 :     }
      39             : };
      40             : 
      41           2 : static void MakeNewKeyWithFastRandomContext(CKey& key)
      42             : {
      43           2 :     std::vector<unsigned char> keydata;
      44           2 :     keydata = g_insecure_rand_ctx.randbytes(32);
      45           2 :     key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
      46           2 :     assert(key.IsValid());
      47           2 : }
      48             : 
      49         149 : BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
      50             : {
      51             :     // This test had non-deterministic coverage due to
      52             :     // randomly selected seeds.
      53             :     // This seed is chosen so that all branches of the function
      54             :     // ecdsa_signature_parse_der_lax are executed during this test.
      55             :     // Specifically branches that run only when an ECDSA
      56             :     // signature's R and S values have leading zeros.
      57           1 :     g_insecure_rand_ctx = FastRandomContext{uint256{33}};
      58             : 
      59           1 :     TxOrphanageTest orphanage;
      60           1 :     CKey key;
      61           1 :     MakeNewKeyWithFastRandomContext(key);
      62           1 :     FillableSigningProvider keystore;
      63           1 :     BOOST_CHECK(keystore.AddKey(key));
      64             : 
      65             :     // 50 orphan transactions:
      66          51 :     for (int i = 0; i < 50; i++)
      67             :     {
      68          50 :         CMutableTransaction tx;
      69          50 :         tx.vin.resize(1);
      70          50 :         tx.vin[0].prevout.n = 0;
      71          50 :         tx.vin[0].prevout.hash = InsecureRand256();
      72          50 :         tx.vin[0].scriptSig << OP_1;
      73          50 :         tx.vout.resize(1);
      74          50 :         tx.vout[0].nValue = 1*CENT;
      75          50 :         tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
      76             : 
      77          50 :         orphanage.AddTx(MakeTransactionRef(tx), i);
      78          50 :     }
      79             : 
      80             :     // ... and 50 that depend on other orphans:
      81          51 :     for (int i = 0; i < 50; i++)
      82             :     {
      83          50 :         CTransactionRef txPrev = orphanage.RandomOrphan();
      84             : 
      85          50 :         CMutableTransaction tx;
      86          50 :         tx.vin.resize(1);
      87          50 :         tx.vin[0].prevout.n = 0;
      88          50 :         tx.vin[0].prevout.hash = txPrev->GetHash();
      89          50 :         tx.vout.resize(1);
      90          50 :         tx.vout[0].nValue = 1*CENT;
      91          50 :         tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
      92          50 :         BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
      93             : 
      94          50 :         orphanage.AddTx(MakeTransactionRef(tx), i);
      95          50 :     }
      96             : 
      97             :     // This really-big orphan should be ignored:
      98          11 :     for (int i = 0; i < 10; i++)
      99             :     {
     100          10 :         CTransactionRef txPrev = orphanage.RandomOrphan();
     101             : 
     102          10 :         CMutableTransaction tx;
     103          10 :         tx.vout.resize(1);
     104          10 :         tx.vout[0].nValue = 1*CENT;
     105          10 :         tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
     106          10 :         tx.vin.resize(2777);
     107       27780 :         for (unsigned int j = 0; j < tx.vin.size(); j++)
     108             :         {
     109       27770 :             tx.vin[j].prevout.n = j;
     110       27770 :             tx.vin[j].prevout.hash = txPrev->GetHash();
     111       27770 :         }
     112          10 :         BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
     113             :         // Reuse same signature for other inputs
     114             :         // (they don't have to be valid for this test)
     115       27770 :         for (unsigned int j = 1; j < tx.vin.size(); j++)
     116       27760 :             tx.vin[j].scriptSig = tx.vin[0].scriptSig;
     117             : 
     118          10 :         BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
     119          10 :     }
     120             : 
     121             :     // Test EraseOrphansFor:
     122           4 :     for (NodeId i = 0; i < 3; i++)
     123             :     {
     124           3 :         size_t sizeBefore = orphanage.CountOrphans();
     125           3 :         orphanage.EraseForPeer(i);
     126           3 :         BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
     127           3 :     }
     128             : 
     129             :     // Test LimitOrphanTxSize() function:
     130           1 :     orphanage.LimitOrphans(40);
     131           1 :     BOOST_CHECK(orphanage.CountOrphans() <= 40);
     132           1 :     orphanage.LimitOrphans(10);
     133           1 :     BOOST_CHECK(orphanage.CountOrphans() <= 10);
     134           1 :     orphanage.LimitOrphans(0);
     135           1 :     BOOST_CHECK(orphanage.CountOrphans() == 0);
     136           1 : }
     137             : 
     138         149 : BOOST_AUTO_TEST_CASE(SetCandidatesByBlock)
     139             : {
     140           1 :     constexpr CAmount tx_fee{1 * CENT};
     141             : 
     142           1 :     TxOrphanageTest orphanage;
     143           1 :     CKey key;
     144           1 :     MakeNewKeyWithFastRandomContext(key);
     145           1 :     CScript scriptPubKey{GetScriptForDestination(PKHash(key.GetPubKey()))};
     146             : 
     147             :     // Construct two transactions, the first transaction splits one input into multiple outputs
     148             :     // and the second transaction spends the last output of the first transaction (i.e. our orphan).
     149             :     // We want our orphan to try and spend from a transaction with more outputs than inputs.
     150           1 :     CMutableTransaction tx_input;
     151           1 :     tx_input.vin.resize(1);
     152           1 :     tx_input.vin[0].prevout.n = 0;
     153           1 :     tx_input.vin[0].prevout.hash = InsecureRand256();
     154           1 :     tx_input.vin[0].scriptSig << OP_1;
     155           1 :     tx_input.vout.resize(3);
     156           4 :     for (size_t idx{0}; idx < tx_input.vout.size(); idx++) {
     157           3 :         tx_input.vout[idx].nValue = 10 * CENT;
     158           3 :         tx_input.vout[idx].scriptPubKey = scriptPubKey;
     159           3 :     }
     160           1 :     tx_input.vout.back().nValue -= tx_fee;
     161             : 
     162           1 :     CMutableTransaction orphan;
     163           1 :     orphan.vin.resize(1);
     164           1 :     orphan.vin[0].prevout = COutPoint(tx_input.GetHash(), tx_input.vout.size() - 1);
     165           1 :     orphan.vin[0].scriptSig << OP_1;
     166           1 :     orphan.vout.resize(1);
     167           1 :     orphan.vout[0].nValue = tx_input.vout.back().nValue - tx_fee;
     168           1 :     orphan.vout[0].scriptPubKey = scriptPubKey;
     169             : 
     170           1 :     CBlock block;
     171           1 :     block.vtx.push_back(MakeTransactionRef(tx_input));
     172             : 
     173             :     // Reprocess orphans based on inclusion of input transaction in block
     174           1 :     BOOST_CHECK(orphanage.AddTx(MakeTransactionRef(orphan), /*peer=*/128));
     175           1 :     orphanage.SetCandidatesByBlock(block);
     176             : 
     177             :     // Old SetCandidatesByBlock() behavior cycled through vin instead of vout and would therefore miss the
     178             :     // orphan because there are more vouts than vins in the transaction the orphan is attempting to spend.
     179             :     // Let's check to make sure this isn't happening again.
     180           1 :     NodeId _originator{-1}; bool more{false};
     181           1 :     CTransactionRef ref = orphanage.GetTxToReconsider(/*peer=*/-1, _originator, more);
     182           1 :     BOOST_CHECK(orphanage.HaveTx(Assert(ref)->GetHash()));
     183           1 :     BOOST_CHECK_EQUAL(ref->GetHash(), orphan.GetHash());
     184           1 :     BOOST_CHECK(!more);
     185           1 : }
     186             : 
     187         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16