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