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/amount.h>
7 : : #include <consensus/merkle.h>
8 : : #include <kernel/coinstats.h>
9 : : #include <node/miner.h>
10 : : #include <primitives/block.h>
11 : : #include <primitives/transaction.h>
12 : : #include <script/script.h>
13 : : #include <sync.h>
14 : : #include <test/fuzz/FuzzedDataProvider.h>
15 : : #include <test/fuzz/fuzz.h>
16 : : #include <test/fuzz/util.h>
17 : : #include <test/util/mining.h>
18 : : #include <test/util/random.h>
19 : : #include <test/util/setup_common.h>
20 : : #include <test/util/time.h>
21 : : #include <txdb.h>
22 : : #include <uint256.h>
23 : : #include <util/check.h>
24 : : #include <validation.h>
25 : :
26 : : #include <cstddef>
27 : : #include <cstdint>
28 : : #include <functional>
29 : : #include <memory>
30 : : #include <optional>
31 : : #include <utility>
32 : : #include <vector>
33 : :
34 [ + - ]: 1612 : FUZZ_TARGET(utxo_total_supply)
35 : : {
36 : 1146 : SeedRandomStateForTest(SeedRand::ZEROS);
37 : 1146 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
38 : 1146 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp
39 : : /** The testing setup that creates a chainman only (no chainstate) */
40 : 1146 : ChainTestingSetup test_setup{
41 : : ChainType::REGTEST,
42 : : {
43 : : .extra_args = {
44 : : "-testactivationheight=bip34@2",
45 : : },
46 : : },
47 [ + - + - ]: 2292 : };
48 : : // Create chainstate
49 [ + - ]: 1146 : test_setup.LoadVerifyActivateChainstate();
50 : 1146 : auto& node{test_setup.m_node};
51 [ - + + - ]: 1146 : auto& chainman{*Assert(test_setup.m_node.chainman)};
52 : :
53 : 139794 : const auto ActiveHeight = [&]() {
54 : 138648 : LOCK(chainman.GetMutex());
55 [ + - + - ]: 138648 : return chainman.ActiveHeight();
56 : 139794 : };
57 : 95051 : const auto PrepareNextBlock = [&]() {
58 : : // Use OP_FALSE to avoid BIP30 check from hitting early
59 : 93905 : auto block = PrepareBlock(node, {
60 : 93905 : .coinbase_output_script = CScript() << OP_FALSE,
61 : 93905 : });
62 : : // Replace OP_FALSE with OP_TRUE
63 : 93905 : {
64 [ + - ]: 93905 : CMutableTransaction tx{*block->vtx.back()};
65 : 93905 : tx.nLockTime = 0; // Use the same nLockTime for all as we want to duplicate one of them.
66 [ + - + - ]: 93905 : tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE;
67 [ + - - + ]: 187810 : block->vtx.back() = MakeTransactionRef(tx);
68 : 0 : }
69 : 93905 : return block;
70 [ + - + - ]: 188956 : };
71 : :
72 : : /** The block template this fuzzer is working on */
73 [ + - ]: 1146 : auto current_block = PrepareNextBlock();
74 : : /** Append-only set of tx outpoints, entries are not removed when spent */
75 : 1146 : std::vector<std::pair<COutPoint, CTxOut>> txos;
76 : : /** The utxo stats at the chain tip */
77 : 1146 : kernel::CCoinsStats utxo_stats;
78 : : /** The total amount of coins in the utxo set */
79 : 1146 : CAmount circulation{0};
80 : :
81 : :
82 : : // Store the tx out in the txo map
83 : 132522 : const auto StoreLastTxo = [&]() {
84 : : // get last tx
85 [ - + ]: 131376 : const CTransaction& tx = *current_block->vtx.back();
86 : : // get last out
87 [ - + ]: 131376 : const uint32_t i = tx.vout.size() - 1;
88 : : // store it
89 : 131376 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
90 [ - + + + : 237241 : if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) {
+ + + + ]
91 : : // also store coinbase
92 [ - + ]: 100945 : const uint32_t i = tx.vout.size() - 2;
93 : 100945 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
94 : : }
95 : 132522 : };
96 : 38617 : const auto AppendRandomTxo = [&](CMutableTransaction& tx) {
97 [ - + ]: 37471 : const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1));
98 : 37471 : tx.vin.emplace_back(txo.first);
99 : 37471 : tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
100 : 38617 : };
101 : 93905 : const auto UpdateUtxoStats = [&](bool wipe_cache) {
102 : 92759 : LOCK(chainman.GetMutex());
103 [ + - + - ]: 92759 : chainman.ActiveChainstate().ForceFlushStateToDisk(wipe_cache);
104 : 92759 : utxo_stats = std::move(
105 [ + - + - : 185518 : *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {})));
+ - ]
106 : : // Check that miner can't print more money than they are allowed to
107 [ + - + - ]: 185518 : assert(circulation == utxo_stats.total_amount);
108 : 93905 : };
109 : :
110 : :
111 : : // Update internal state to chain tip
112 [ + - ]: 1146 : StoreLastTxo();
113 [ + - ]: 1146 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
114 [ + - - + ]: 1146 : assert(ActiveHeight() == 0);
115 : : // Get at which height we duplicate the coinbase
116 : : // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
117 : : // Up to 300 seems reasonable.
118 : 1146 : int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
119 : : // Avoid bad-cb-length error at heights <= 16. Pad the BIP34-encoded height
120 : : // with OP_0 to satisfy the minimum 2-byte coinbase scriptSig length.
121 [ + - ]: 1146 : CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height;
122 [ + + ]: 1146 : if (duplicate_coinbase_height <= 16) {
123 [ + - ]: 116 : duplicate_coinbase_script << OP_0;
124 : : }
125 : : // Mine the first block with this duplicate
126 [ + - - + ]: 2292 : current_block = PrepareNextBlock();
127 [ + - ]: 1146 : StoreLastTxo();
128 : :
129 : 1146 : {
130 : : // Create duplicate (CScript should match exact format as in CreateNewBlock)
131 [ + - ]: 1146 : CMutableTransaction tx{*current_block->vtx.front()};
132 [ + - ]: 1146 : tx.vin.at(0).scriptSig = duplicate_coinbase_script;
133 : :
134 : : // Mine block and create next block template
135 [ + - - + ]: 2292 : current_block->vtx.front() = MakeTransactionRef(tx);
136 : 0 : }
137 [ + - ]: 1146 : current_block->hashMerkleRoot = BlockMerkleRoot(*current_block);
138 [ + - - + ]: 1146 : assert(!MineBlock(node, current_block).IsNull());
139 [ + - + - : 1146 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
+ - ]
140 : :
141 [ + - - + ]: 1146 : assert(ActiveHeight() == 1);
142 [ + - ]: 1146 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
143 [ + - - + ]: 2292 : current_block = PrepareNextBlock();
144 [ + - ]: 1146 : StoreLastTxo();
145 : :
146 : : // Limit to avoid timeout, but enough to cover duplicate_coinbase_height
147 : : // and CVE-2018-17144.
148 [ + + + + ]: 129084 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'00)
149 : : {
150 [ + - ]: 127938 : CallOneOf(
151 : : fuzzed_data_provider,
152 : 18086 : [&] {
153 : : // Append an input-output pair to the last tx in the current block
154 : 18086 : CMutableTransaction tx{*current_block->vtx.back()};
155 [ + - ]: 18086 : AppendRandomTxo(tx);
156 [ + - - + ]: 36172 : current_block->vtx.back() = MakeTransactionRef(tx);
157 [ + - ]: 18086 : StoreLastTxo();
158 : 18086 : },
159 : 19385 : [&] {
160 : : // Append a tx to the list of txs in the current block
161 : 19385 : CMutableTransaction tx{};
162 [ + - ]: 19385 : AppendRandomTxo(tx);
163 [ + - + - : 38770 : current_block->vtx.push_back(MakeTransactionRef(tx));
- + ]
164 [ + - ]: 19385 : StoreLastTxo();
165 : 19385 : },
166 : 90467 : [&] {
167 : : // Append the current block to the active chain
168 : 90467 : node::RegenerateCommitments(*current_block, chainman);
169 : 90467 : const bool was_valid = !MineBlock(node, current_block).IsNull();
170 : :
171 : 90467 : const uint256 prev_hash_serialized{utxo_stats.hashSerialized};
172 [ + + ]: 90467 : if (was_valid) {
173 [ + + ]: 67605 : if (duplicate_coinbase_height == ActiveHeight()) {
174 : : // we mined the duplicate coinbase
175 [ - + ]: 22 : assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script);
176 : : }
177 : :
178 : 67605 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
179 : : }
180 : :
181 : 90467 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
182 : :
183 [ + + ]: 90467 : if (!was_valid) {
184 : : // utxo stats must not change
185 [ - + ]: 22862 : assert(prev_hash_serialized == utxo_stats.hashSerialized);
186 : : }
187 : :
188 [ - + ]: 90467 : current_block = PrepareNextBlock();
189 : 90467 : StoreLastTxo();
190 : 90467 : });
191 : : }
192 [ + - + - ]: 3438 : }
|