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 [ + - ]: 1836 : FUZZ_TARGET(utxo_total_supply)
35 : : {
36 : 1370 : SeedRandomStateForTest(SeedRand::ZEROS);
37 : 1370 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
38 : 1370 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp
39 : : /** The testing setup that creates a chainman only (no chainstate) */
40 : 1370 : ChainTestingSetup test_setup{
41 : : ChainType::REGTEST,
42 : : {
43 : : .extra_args = {
44 : : "-testactivationheight=bip34@2",
45 : : },
46 : : },
47 [ + - + - ]: 2740 : };
48 : : // Create chainstate
49 [ + - ]: 1370 : test_setup.LoadVerifyActivateChainstate();
50 : 1370 : auto& node{test_setup.m_node};
51 [ - + + - ]: 1370 : auto& chainman{*Assert(test_setup.m_node.chainman)};
52 : :
53 : 174360 : const auto ActiveHeight = [&]() {
54 : 172990 : LOCK(chainman.GetMutex());
55 [ + - + - ]: 172990 : return chainman.ActiveHeight();
56 : 174360 : };
57 : 117853 : const auto PrepareNextBlock = [&]() {
58 : : // Use OP_FALSE to avoid BIP30 check from hitting early
59 : 116483 : auto block = PrepareBlock(node, {
60 : 116483 : .coinbase_output_script = CScript() << OP_FALSE,
61 : 116483 : });
62 : : // Replace OP_FALSE with OP_TRUE
63 : 116483 : {
64 [ + - ]: 116483 : CMutableTransaction tx{*block->vtx.back()};
65 : 116483 : tx.nLockTime = 0; // Use the same nLockTime for all as we want to duplicate one of them.
66 [ + - + - ]: 116483 : tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE;
67 [ + - - + ]: 232966 : block->vtx.back() = MakeTransactionRef(tx);
68 : 0 : }
69 : 116483 : return block;
70 [ + - + - ]: 234336 : };
71 : :
72 : : /** The block template this fuzzer is working on */
73 [ + - ]: 1370 : auto current_block = PrepareNextBlock();
74 : : /** Append-only set of tx outpoints, entries are not removed when spent */
75 : 1370 : std::vector<std::pair<COutPoint, CTxOut>> txos;
76 : : /** The utxo stats at the chain tip */
77 : 1370 : kernel::CCoinsStats utxo_stats;
78 : : /** The total amount of coins in the utxo set */
79 : 1370 : CAmount circulation{0};
80 : :
81 : :
82 : : // Store the tx out in the txo map
83 : 163897 : const auto StoreLastTxo = [&]() {
84 : : // get last tx
85 [ - + ]: 162527 : const CTransaction& tx = *current_block->vtx.back();
86 : : // get last out
87 [ - + ]: 162527 : const uint32_t i = tx.vout.size() - 1;
88 : : // store it
89 : 162527 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
90 [ - + + + : 293689 : if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) {
+ + + + ]
91 : : // also store coinbase
92 [ - + ]: 125207 : const uint32_t i = tx.vout.size() - 2;
93 : 125207 : txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
94 : : }
95 : 163897 : };
96 : 47414 : const auto AppendRandomTxo = [&](CMutableTransaction& tx) {
97 [ - + ]: 46044 : const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1));
98 : 46044 : tx.vin.emplace_back(txo.first);
99 : 46044 : tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
100 : 47414 : };
101 : 116483 : const auto UpdateUtxoStats = [&](bool wipe_cache) {
102 : 115113 : LOCK(chainman.GetMutex());
103 [ + - + - ]: 115113 : chainman.ActiveChainstate().ForceFlushStateToDisk(wipe_cache);
104 : 115113 : utxo_stats = std::move(
105 [ + - + - : 230226 : *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 [ + - + - ]: 230226 : assert(circulation == utxo_stats.total_amount);
108 : 116483 : };
109 : :
110 : :
111 : : // Update internal state to chain tip
112 [ + - ]: 1370 : StoreLastTxo();
113 [ + - ]: 1370 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
114 [ + - - + ]: 1370 : 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 : 1370 : 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 [ + - ]: 1370 : CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height;
122 [ + + ]: 1370 : if (duplicate_coinbase_height <= 16) {
123 [ + - ]: 136 : duplicate_coinbase_script << OP_0;
124 : : }
125 : : // Mine the first block with this duplicate
126 [ + - - + ]: 2740 : current_block = PrepareNextBlock();
127 [ + - ]: 1370 : StoreLastTxo();
128 : :
129 : 1370 : {
130 : : // Create duplicate (CScript should match exact format as in CreateNewBlock)
131 [ + - ]: 1370 : CMutableTransaction tx{*current_block->vtx.front()};
132 [ + - ]: 1370 : tx.vin.at(0).scriptSig = duplicate_coinbase_script;
133 : :
134 : : // Mine block and create next block template
135 [ + - - + ]: 2740 : current_block->vtx.front() = MakeTransactionRef(tx);
136 : 0 : }
137 [ + - ]: 1370 : current_block->hashMerkleRoot = BlockMerkleRoot(*current_block);
138 [ + - - + ]: 1370 : assert(!MineBlock(node, current_block).IsNull());
139 [ + - + - : 1370 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
+ - ]
140 : :
141 [ + - - + ]: 1370 : assert(ActiveHeight() == 1);
142 [ + - ]: 1370 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
143 [ + - - + ]: 2740 : current_block = PrepareNextBlock();
144 [ + - ]: 1370 : StoreLastTxo();
145 : :
146 : : // Limit to avoid timeout, but enough to cover duplicate_coinbase_height
147 : : // and CVE-2018-17144.
148 [ + + + + ]: 159787 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'00)
149 : : {
150 [ + - ]: 158417 : CallOneOf(
151 : : fuzzed_data_provider,
152 : 22647 : [&] {
153 : : // Append an input-output pair to the last tx in the current block
154 : 22647 : CMutableTransaction tx{*current_block->vtx.back()};
155 [ + - ]: 22647 : AppendRandomTxo(tx);
156 [ + - - + ]: 45294 : current_block->vtx.back() = MakeTransactionRef(tx);
157 [ + - ]: 22647 : StoreLastTxo();
158 : 22647 : },
159 : 23397 : [&] {
160 : : // Append a tx to the list of txs in the current block
161 : 23397 : CMutableTransaction tx{};
162 [ + - ]: 23397 : AppendRandomTxo(tx);
163 [ + - + - : 46794 : current_block->vtx.push_back(MakeTransactionRef(tx));
- + ]
164 [ + - ]: 23397 : StoreLastTxo();
165 : 23397 : },
166 : 112373 : [&] {
167 : : // Append the current block to the active chain
168 : 112373 : node::RegenerateCommitments(*current_block, chainman);
169 : 112373 : const bool was_valid = !MineBlock(node, current_block).IsNull();
170 : :
171 : 112373 : const uint256 prev_hash_serialized{utxo_stats.hashSerialized};
172 [ + + ]: 112373 : if (was_valid) {
173 [ + + ]: 84440 : if (duplicate_coinbase_height == ActiveHeight()) {
174 : : // we mined the duplicate coinbase
175 [ - + ]: 29 : assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script);
176 : : }
177 : :
178 : 84440 : circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
179 : : }
180 : :
181 : 112373 : UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
182 : :
183 [ + + ]: 112373 : if (!was_valid) {
184 : : // utxo stats must not change
185 [ - + ]: 27933 : assert(prev_hash_serialized == utxo_stats.hashSerialized);
186 : : }
187 : :
188 [ - + ]: 112373 : current_block = PrepareNextBlock();
189 : 112373 : StoreLastTxo();
190 : 112373 : });
191 : : }
192 [ + - + - ]: 4110 : }
|