Branch data Line data Source code
1 : : // Copyright (c) 2020 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 <chainparams.h> 6 : : #include <consensus/consensus.h> 7 : : #include <consensus/merkle.h> 8 : : #include <kernel/coinstats.h> 9 : : #include <node/miner.h> 10 : : #include <script/interpreter.h> 11 : : #include <streams.h> 12 : : #include <test/fuzz/FuzzedDataProvider.h> 13 : : #include <test/fuzz/fuzz.h> 14 : : #include <test/fuzz/util.h> 15 : : #include <test/util/mining.h> 16 : : #include <test/util/setup_common.h> 17 [ + - ]: 173 : #include <util/chaintype.h> 18 [ + - ]: 173 : #include <validation.h> 19 : : #include <version.h> 20 : : 21 [ - + ]: 958 : FUZZ_TARGET(utxo_total_supply) 22 : : { 23 : : /** The testing setup that creates a chainman only (no chainstate) */ 24 [ + - ]: 612 : ChainTestingSetup test_setup{ 25 [ + - ]: 173 : ChainType::REGTEST, 26 [ + - ]: 612 : { 27 : : "-testactivationheight=bip34@2", 28 : : }, 29 : : }; 30 : : // Create chainstate 31 [ + - ]: 612 : test_setup.LoadVerifyActivateChainstate(); 32 : 612 : auto& node{test_setup.m_node}; 33 [ + - + - ]: 612 : auto& chainman{*Assert(test_setup.m_node.chainman)}; 34 [ + - ]: 612 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 35 : : 36 : 80318 : const auto ActiveHeight = [&]() { 37 : 79706 : LOCK(chainman.GetMutex()); 38 [ + - ]: 79706 : return chainman.ActiveHeight(); 39 : 79706 : }; 40 : 50930 : const auto PrepareNextBlock = [&]() { 41 : : // Use OP_FALSE to avoid BIP30 check from hitting early 42 [ + - + - ]: 50318 : auto block = PrepareBlock(node, CScript{} << OP_FALSE); 43 : : // Replace OP_FALSE with OP_TRUE 44 : : { 45 [ - + ]: 50318 : CMutableTransaction tx{*block->vtx.back()}; 46 [ + - + - : 50318 : tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE; + - + - ] 47 [ + - ]: 50318 : block->vtx.back() = MakeTransactionRef(tx); 48 : 50318 : } 49 : 50318 : return block; 50 [ + - ]: 50318 : }; 51 : : 52 : : /** The block template this fuzzer is working on */ 53 [ + - ]: 612 : auto current_block = PrepareNextBlock(); 54 : : /** Append-only set of tx outpoints, entries are not removed when spent */ 55 : 612 : std::vector<std::pair<COutPoint, CTxOut>> txos; 56 : : /** The utxo stats at the chain tip */ 57 [ + - ]: 612 : kernel::CCoinsStats utxo_stats; 58 : : /** The total amount of coins in the utxo set */ 59 : 612 : CAmount circulation{0}; 60 : : 61 : : 62 : : // Store the tx out in the txo map 63 : 103226 : const auto StoreLastTxo = [&]() { 64 : : // get last tx 65 : 102614 : const CTransaction& tx = *current_block->vtx.back(); 66 : : // get last out 67 : 102614 : const uint32_t i = tx.vout.size() - 1; 68 : : // store it 69 : 102614 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i)); 70 [ + + + + ]: 102614 : if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) { 71 : : // also store coinbase 72 : 58229 : const uint32_t i = tx.vout.size() - 2; 73 : 58229 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i)); 74 : 58402 : } 75 : 102614 : }; 76 : 52908 : const auto AppendRandomTxo = [&](CMutableTransaction& tx) { 77 : 52296 : const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1)); 78 : 52296 : tx.vin.emplace_back(txo.first); 79 : 52296 : tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee 80 : 52296 : }; 81 : 50318 : const auto UpdateUtxoStats = [&]() { 82 : 49706 : LOCK(chainman.GetMutex()); 83 [ + - + - ]: 49706 : chainman.ActiveChainstate().ForceFlushStateToDisk(); 84 : 49706 : utxo_stats = std::move( 85 [ + - + - : 49706 : *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {}))); + - + - + - ] 86 : : // Check that miner can't print more money than they are allowed to 87 [ + - + - ]: 49706 : assert(circulation == utxo_stats.total_amount); 88 : 49706 : }; 89 : : 90 : : 91 : : // Update internal state to chain tip 92 [ + - ]: 612 : StoreLastTxo(); 93 [ + - ]: 612 : UpdateUtxoStats(); 94 [ + - + - ]: 612 : assert(ActiveHeight() == 0); 95 : : // Get at which height we duplicate the coinbase 96 : : // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high. 97 : : // Up to 2000 seems reasonable. 98 [ + - ]: 612 : int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 20 * COINBASE_MATURITY); 99 : : // Always pad with OP_0 at the end to avoid bad-cb-length error 100 [ + - + - : 612 : const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0; + - + - ] 101 : : // Mine the first block with this duplicate 102 [ + - ]: 612 : current_block = PrepareNextBlock(); 103 [ + - ]: 612 : StoreLastTxo(); 104 : : 105 : : { 106 : : // Create duplicate (CScript should match exact format as in CreateNewBlock) 107 [ + - ]: 612 : CMutableTransaction tx{*current_block->vtx.front()}; 108 [ + - + - ]: 612 : tx.vin.at(0).scriptSig = duplicate_coinbase_script; 109 : : 110 : : // Mine block and create next block template 111 [ + - ]: 612 : current_block->vtx.front() = MakeTransactionRef(tx); 112 : 612 : } 113 [ + - ]: 612 : current_block->hashMerkleRoot = BlockMerkleRoot(*current_block); 114 [ + - + - : 612 : assert(!MineBlock(node, current_block).IsNull()); + - ] 115 [ + - + - : 612 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); + - + - ] 116 : : 117 [ + - + - ]: 612 : assert(ActiveHeight() == 1); 118 [ + - ]: 612 : UpdateUtxoStats(); 119 [ + - ]: 612 : current_block = PrepareNextBlock(); 120 [ + - ]: 612 : StoreLastTxo(); 121 : : 122 : : // Limit to avoid timeout, but enough to cover duplicate_coinbase_height 123 : : // and CVE-2018-17144. 124 [ + - + + : 101390 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'000) + + ] 125 : : { 126 [ + - ]: 100778 : CallOneOf( 127 : : fuzzed_data_provider, 128 : 130715 : [&] { 129 : : // Append an input-output pair to the last tx in the current block 130 : 29937 : CMutableTransaction tx{*current_block->vtx.back()}; 131 [ + - ]: 29937 : AppendRandomTxo(tx); 132 [ + - ]: 29937 : current_block->vtx.back() = MakeTransactionRef(tx); 133 [ + - ]: 29937 : StoreLastTxo(); 134 : 29937 : }, 135 : 123137 : [&] { 136 : : // Append a tx to the list of txs in the current block 137 : 22359 : CMutableTransaction tx{}; 138 [ + - ]: 22359 : AppendRandomTxo(tx); 139 [ + - + - ]: 22359 : current_block->vtx.push_back(MakeTransactionRef(tx)); 140 [ + - ]: 22359 : StoreLastTxo(); 141 : 22359 : }, 142 : 149260 : [&] { 143 : : // Append the current block to the active chain 144 : 48482 : node::RegenerateCommitments(*current_block, chainman); 145 : 48482 : const bool was_valid = !MineBlock(node, current_block).IsNull(); 146 : : 147 : 48482 : const auto prev_utxo_stats = utxo_stats; 148 [ + + ]: 48482 : if (was_valid) { 149 [ + + ]: 38935 : if (duplicate_coinbase_height == ActiveHeight()) { 150 : : // we mined the duplicate coinbase 151 [ + - ]: 3 : assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script); 152 : 3 : } 153 : : 154 : 38935 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); 155 : 38935 : } 156 : : 157 : 48482 : UpdateUtxoStats(); 158 : : 159 [ + + ]: 48482 : if (!was_valid) { 160 : : // utxo stats must not change 161 [ + - ]: 9547 : assert(prev_utxo_stats.hashSerialized == utxo_stats.hashSerialized); 162 : 9547 : } 163 : : 164 : 48482 : current_block = PrepareNextBlock(); 165 : 48482 : StoreLastTxo(); 166 : 48482 : }); 167 : 100778 : } 168 : 612 : }