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()