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 <amount.h>
6 #include <chainparams.h>
7 #include <chainparamsbase.h>
8 #include <coins.h>
9 #include <consensus/tx_verify.h>
10 #include <consensus/validation.h>
11 #include <key.h>
12 #include <node/coinstats.h>
13 #include <policy/policy.h>
14 #include <primitives/transaction.h>
15 #include <pubkey.h>
16 #include <test/fuzz/FuzzedDataProvider.h>
17 #include <test/fuzz/fuzz.h>
18 #include <test/fuzz/util.h>
19 #include <validation.h>
20
21 #include <cstdint>
22 #include <limits>
23 #include <optional>
24 #include <string>
25 #include <vector>
26
27 namespace {
28 const Coin EMPTY_COIN{};
29
operator ==(const Coin & a,const Coin & b)30 bool operator==(const Coin& a, const Coin& b)
31 {
32 if (a.IsSpent() && b.IsSpent()) return true;
33 return a.fCoinBase == b.fCoinBase && a.nHeight == b.nHeight && a.out == b.out;
34 }
35 } // namespace
36
initialize()37 void initialize()
38 {
39 static const ECCVerifyHandle ecc_verify_handle;
40 ECC_Start();
41 SelectParams(CBaseChainParams::REGTEST);
42 }
43
test_one_input(const std::vector<uint8_t> & buffer)44 void test_one_input(const std::vector<uint8_t>& buffer)
45 {
46 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
47 CCoinsView backend_coins_view;
48 CCoinsViewCache coins_view_cache{&backend_coins_view};
49 COutPoint random_out_point;
50 Coin random_coin;
51 CMutableTransaction random_mutable_transaction;
52 while (fuzzed_data_provider.ConsumeBool()) {
53 switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
54 case 0: {
55 if (random_coin.IsSpent()) {
56 break;
57 }
58 Coin coin = random_coin;
59 bool expected_code_path = false;
60 const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
61 try {
62 coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite);
63 expected_code_path = true;
64 } catch (const std::logic_error& e) {
65 if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
66 assert(!possible_overwrite);
67 expected_code_path = true;
68 }
69 }
70 assert(expected_code_path);
71 break;
72 }
73 case 1: {
74 (void)coins_view_cache.Flush();
75 break;
76 }
77 case 2: {
78 coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider));
79 break;
80 }
81 case 3: {
82 Coin move_to;
83 (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
84 break;
85 }
86 case 4: {
87 coins_view_cache.Uncache(random_out_point);
88 break;
89 }
90 case 5: {
91 if (fuzzed_data_provider.ConsumeBool()) {
92 backend_coins_view = CCoinsView{};
93 }
94 coins_view_cache.SetBackend(backend_coins_view);
95 break;
96 }
97 case 6: {
98 const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
99 if (!opt_out_point) {
100 break;
101 }
102 random_out_point = *opt_out_point;
103 break;
104 }
105 case 7: {
106 const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
107 if (!opt_coin) {
108 break;
109 }
110 random_coin = *opt_coin;
111 break;
112 }
113 case 8: {
114 const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
115 if (!opt_mutable_transaction) {
116 break;
117 }
118 random_mutable_transaction = *opt_mutable_transaction;
119 break;
120 }
121 case 9: {
122 CCoinsMap coins_map;
123 while (fuzzed_data_provider.ConsumeBool()) {
124 CCoinsCacheEntry coins_cache_entry;
125 coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
126 if (fuzzed_data_provider.ConsumeBool()) {
127 coins_cache_entry.coin = random_coin;
128 } else {
129 const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
130 if (!opt_coin) {
131 break;
132 }
133 coins_cache_entry.coin = *opt_coin;
134 }
135 coins_map.emplace(random_out_point, std::move(coins_cache_entry));
136 }
137 bool expected_code_path = false;
138 try {
139 coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
140 expected_code_path = true;
141 } catch (const std::logic_error& e) {
142 if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
143 expected_code_path = true;
144 }
145 }
146 assert(expected_code_path);
147 break;
148 }
149 }
150 }
151
152 {
153 const Coin& coin_using_access_coin = coins_view_cache.AccessCoin(random_out_point);
154 const bool exists_using_access_coin = !(coin_using_access_coin == EMPTY_COIN);
155 const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point);
156 const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point);
157 Coin coin_using_get_coin;
158 const bool exists_using_get_coin = coins_view_cache.GetCoin(random_out_point, coin_using_get_coin);
159 if (exists_using_get_coin) {
160 assert(coin_using_get_coin == coin_using_access_coin);
161 }
162 assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) ||
163 (!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin));
164 const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point);
165 if (exists_using_have_coin_in_backend) {
166 assert(exists_using_have_coin);
167 }
168 Coin coin_using_backend_get_coin;
169 if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) {
170 assert(exists_using_have_coin_in_backend);
171 assert(coin_using_get_coin == coin_using_backend_get_coin);
172 } else {
173 assert(!exists_using_have_coin_in_backend);
174 }
175 }
176
177 {
178 bool expected_code_path = false;
179 try {
180 (void)coins_view_cache.Cursor();
181 } catch (const std::logic_error&) {
182 expected_code_path = true;
183 }
184 assert(expected_code_path);
185 (void)coins_view_cache.DynamicMemoryUsage();
186 (void)coins_view_cache.EstimateSize();
187 (void)coins_view_cache.GetBestBlock();
188 (void)coins_view_cache.GetCacheSize();
189 (void)coins_view_cache.GetHeadBlocks();
190 (void)coins_view_cache.HaveInputs(CTransaction{random_mutable_transaction});
191 }
192
193 {
194 const CCoinsViewCursor* coins_view_cursor = backend_coins_view.Cursor();
195 assert(coins_view_cursor == nullptr);
196 (void)backend_coins_view.EstimateSize();
197 (void)backend_coins_view.GetBestBlock();
198 (void)backend_coins_view.GetHeadBlocks();
199 }
200
201 if (fuzzed_data_provider.ConsumeBool()) {
202 switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) {
203 case 0: {
204 const CTransaction transaction{random_mutable_transaction};
205 bool is_spent = false;
206 for (const CTxOut& tx_out : transaction.vout) {
207 if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
208 is_spent = true;
209 }
210 }
211 if (is_spent) {
212 // Avoid:
213 // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed.
214 break;
215 }
216 bool expected_code_path = false;
217 const int height = fuzzed_data_provider.ConsumeIntegral<int>();
218 const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
219 try {
220 AddCoins(coins_view_cache, transaction, height, possible_overwrite);
221 expected_code_path = true;
222 } catch (const std::logic_error& e) {
223 if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
224 assert(!possible_overwrite);
225 expected_code_path = true;
226 }
227 }
228 assert(expected_code_path);
229 break;
230 }
231 case 1: {
232 (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, false);
233 (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, true);
234 break;
235 }
236 case 2: {
237 TxValidationState state;
238 CAmount tx_fee_out;
239 const CTransaction transaction{random_mutable_transaction};
240 if (ContainsSpentInput(transaction, coins_view_cache)) {
241 // Avoid:
242 // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
243 break;
244 }
245 try {
246 (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
247 assert(MoneyRange(tx_fee_out));
248 } catch (const std::runtime_error&) {
249 }
250 break;
251 }
252 case 3: {
253 const CTransaction transaction{random_mutable_transaction};
254 if (ContainsSpentInput(transaction, coins_view_cache)) {
255 // Avoid:
256 // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
257 break;
258 }
259 (void)GetP2SHSigOpCount(transaction, coins_view_cache);
260 break;
261 }
262 case 4: {
263 const CTransaction transaction{random_mutable_transaction};
264 if (ContainsSpentInput(transaction, coins_view_cache)) {
265 // Avoid:
266 // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
267 break;
268 }
269 const int flags = fuzzed_data_provider.ConsumeIntegral<int>();
270 if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
271 // Avoid:
272 // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
273 break;
274 }
275 (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
276 break;
277 }
278 case 5: {
279 CCoinsStats stats;
280 bool expected_code_path = false;
281 try {
282 (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
283 } catch (const std::logic_error&) {
284 expected_code_path = true;
285 }
286 assert(expected_code_path);
287 break;
288 }
289 case 6: {
290 (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
291 break;
292 }
293 }
294 }
295 }
296