Branch data Line data Source code
1 : : // Copyright (c) 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 <consensus/amount.h> 6 : : #include <consensus/validation.h> 7 : : #include <net_processing.h> 8 : : #include <node/eviction.h> 9 : : #include <policy/policy.h> 10 : : #include <primitives/transaction.h> 11 : : #include <script/script.h> 12 : : #include <sync.h> 13 : : #include <test/fuzz/FuzzedDataProvider.h> 14 : : #include <test/fuzz/fuzz.h> 15 : : #include <test/fuzz/util.h> 16 : : #include <test/util/setup_common.h> 17 : : #include <txorphanage.h> 18 : : #include <uint256.h> 19 : : #include <util/check.h> 20 : : #include <util/time.h> 21 : : 22 : : #include <cstdint> 23 : : #include <memory> 24 : : #include <set> 25 : : #include <utility> 26 : : #include <vector> 27 : : 28 : 0 : void initialize_orphanage() 29 : : { 30 [ # # ][ # # ]: 0 : static const auto testing_setup = MakeNoLogFileContext(); [ # # ] 31 : 0 : } 32 : : 33 [ + - ]: 4 : FUZZ_TARGET(txorphan, .init = initialize_orphanage) 34 : : { 35 : 0 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 36 : 0 : FastRandomContext limit_orphans_rng{/*fDeterministic=*/true}; 37 [ # # ]: 0 : SetMockTime(ConsumeTime(fuzzed_data_provider)); 38 : : 39 : 0 : TxOrphanage orphanage; 40 : 0 : std::vector<COutPoint> outpoints; 41 : : // initial outpoints used to construct transactions later 42 [ # # ]: 0 : for (uint8_t i = 0; i < 4; i++) { 43 [ # # ][ # # ]: 0 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0); [ # # ] 44 : 0 : } 45 : : // if true, allow duplicate input when constructing tx 46 [ # # ]: 0 : const bool duplicate_input = fuzzed_data_provider.ConsumeBool(); 47 : : 48 [ # # ][ # # ]: 0 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS) [ # # ][ # # ] 49 : : { 50 : : // construct transaction 51 [ # # ]: 0 : const CTransactionRef tx = [&] { 52 : 0 : CMutableTransaction tx_mut; 53 [ # # ]: 0 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size()); 54 [ # # ]: 0 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size()); 55 : : // pick unique outpoints from outpoints as input 56 [ # # ]: 0 : for (uint32_t i = 0; i < num_in; i++) { 57 [ # # ]: 0 : auto& prevout = PickValue(fuzzed_data_provider, outpoints); 58 [ # # ]: 0 : tx_mut.vin.emplace_back(prevout); 59 : : // pop the picked outpoint if duplicate input is not allowed 60 [ # # ]: 0 : if (!duplicate_input) { 61 : 0 : std::swap(prevout, outpoints.back()); 62 : 0 : outpoints.pop_back(); 63 : 0 : } 64 : 0 : } 65 : : // output amount will not affect txorphanage 66 [ # # ]: 0 : for (uint32_t i = 0; i < num_out; i++) { 67 [ # # ][ # # ]: 0 : tx_mut.vout.emplace_back(CAmount{0}, CScript{}); 68 : 0 : } 69 : : // restore previously popped outpoints 70 [ # # ]: 0 : for (auto& in : tx_mut.vin) { 71 [ # # ]: 0 : outpoints.push_back(in.prevout); 72 : : } 73 [ # # ]: 0 : auto new_tx = MakeTransactionRef(tx_mut); 74 : : // add newly constructed transaction to outpoints 75 [ # # ]: 0 : for (uint32_t i = 0; i < num_out; i++) { 76 [ # # ][ # # ]: 0 : outpoints.emplace_back(new_tx->GetHash(), i); 77 : 0 : } 78 : 0 : return new_tx; 79 [ # # ]: 0 : }(); 80 : : 81 : : // trigger orphanage functions 82 [ # # ][ # # ]: 0 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS) [ # # ] 83 : : { 84 [ # # ]: 0 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>(); 85 : : 86 [ # # ]: 0 : CallOneOf( 87 : : fuzzed_data_provider, 88 : 0 : [&] { 89 : 0 : orphanage.AddChildrenToWorkSet(*tx); 90 : 0 : }, 91 : 0 : [&] { 92 : : { 93 : 0 : CTransactionRef ref = orphanage.GetTxToReconsider(peer_id); 94 [ # # ]: 0 : if (ref) { 95 [ # # ][ # # ]: 0 : bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetWitnessHash())); [ # # ][ # # ] [ # # ][ # # ] [ # # ][ # # ] 96 [ # # ]: 0 : Assert(have_tx); 97 : 0 : } 98 : 0 : } 99 : 0 : }, 100 : 0 : [&] { 101 [ # # ]: 0 : bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash())); 102 : : // AddTx should return false if tx is too big or already have it 103 : : // tx weight is unknown, we only check when tx is already in orphanage 104 : : { 105 : 0 : bool add_tx = orphanage.AddTx(tx, peer_id); 106 : : // have_tx == true -> add_tx == false 107 [ # # ]: 0 : Assert(!have_tx || !add_tx); 108 : : } 109 [ # # ]: 0 : have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash())); 110 : : { 111 : 0 : bool add_tx = orphanage.AddTx(tx, peer_id); 112 : : // if have_tx is still false, it must be too big 113 : 0 : Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)); 114 [ # # ]: 0 : Assert(!have_tx || !add_tx); 115 : : } 116 : 0 : }, 117 : 0 : [&] { 118 [ # # ]: 0 : bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash())); 119 : : // EraseTx should return 0 if m_orphans doesn't have the tx 120 : : { 121 : 0 : Assert(have_tx == orphanage.EraseTx(tx->GetHash())); 122 : : } 123 [ # # ]: 0 : have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash())); 124 : : // have_tx should be false and EraseTx should fail 125 : : { 126 [ # # ]: 0 : Assert(!have_tx && !orphanage.EraseTx(tx->GetHash())); 127 : : } 128 : 0 : }, 129 : 0 : [&] { 130 : 0 : orphanage.EraseForPeer(peer_id); 131 : 0 : }, 132 : 0 : [&] { 133 : : // test mocktime and expiry 134 : 0 : SetMockTime(ConsumeTime(fuzzed_data_provider)); 135 : 0 : auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); 136 : 0 : orphanage.LimitOrphans(limit, limit_orphans_rng); 137 : 0 : Assert(orphanage.Size() <= limit); 138 : 0 : }); 139 : 0 : } 140 : 0 : } 141 : 0 : }