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