Branch data Line data Source code
1 : : // Copyright (c) 2020-2022 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 <coins.h>
6 : : #include <consensus/amount.h>
7 : : #include <consensus/tx_check.h>
8 : : #include <consensus/tx_verify.h>
9 : : #include <consensus/validation.h>
10 : : #include <policy/policy.h>
11 : : #include <primitives/transaction.h>
12 : : #include <script/interpreter.h>
13 : : #include <test/fuzz/FuzzedDataProvider.h>
14 : : #include <test/fuzz/fuzz.h>
15 : : #include <test/fuzz/util.h>
16 : : #include <test/util/setup_common.h>
17 : : #include <txdb.h>
18 : : #include <util/hasher.h>
19 : :
20 : : #include <cassert>
21 : : #include <cstdint>
22 : : #include <limits>
23 : : #include <memory>
24 : : #include <optional>
25 : : #include <stdexcept>
26 : : #include <string>
27 : : #include <utility>
28 : : #include <vector>
29 : :
30 : : namespace {
31 : : const TestingSetup* g_setup;
32 : 2 : const Coin EMPTY_COIN{};
33 : :
34 : 2755 : bool operator==(const Coin& a, const Coin& b)
35 : : {
36 [ + + ][ - + ]: 2755 : if (a.IsSpent() && b.IsSpent()) return true;
37 [ + + ][ + + ]: 2128 : return a.fCoinBase == b.fCoinBase && a.nHeight == b.nHeight && a.out == b.out;
38 : 2755 : }
39 : : } // namespace
40 : :
41 : 1 : void initialize_coins_view()
42 : : {
43 [ + - ][ - + ]: 1 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
[ + - ]
44 : 1 : g_setup = testing_setup.get();
45 : 1 : }
46 : 1691 :
47 : 1691 : void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend_coins_view, bool is_db = false)
48 : : {
49 : 1691 : bool good_data{true};
50 : :
51 : 1691 : CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true};
52 [ + - ][ + - ]: 1691 : if (is_db) coins_view_cache.SetBestBlock(uint256::ONE);
53 [ + - ]: 1691 : COutPoint random_out_point;
54 [ + - ]: 1691 : Coin random_coin;
55 [ + - ]: 1691 : CMutableTransaction random_mutable_transaction;
56 [ + + ][ + - ]: 616911 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
[ + + ][ + + ]
57 : : {
58 [ + - ]: 615220 : CallOneOf(
59 : 615220 : fuzzed_data_provider,
60 : 655580 : [&] {
61 [ + + ]: 40360 : if (random_coin.IsSpent()) {
62 : 1738 : return;
63 : : }
64 : 38622 : Coin coin = random_coin;
65 : 38622 : bool expected_code_path = false;
66 [ + - ]: 38622 : const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
67 : : try {
68 [ + + ]: 38622 : coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite);
69 : 37554 : expected_code_path = true;
70 [ - + ]: 38622 : } catch (const std::logic_error& e) {
71 [ + - ][ + - ]: 1068 : if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
[ - + ]
72 [ - + ]: 1068 : assert(!possible_overwrite);
73 : 1068 : expected_code_path = true;
74 : : // AddCoin() decreases cachedCoinsUsage by the memory usage of the old coin at the beginning and
75 : : // increase it by the value of the new coin at the end. If it throws in the process, the value
76 : : // of cachedCoinsUsage would have been incorrectly decreased, leading to an underflow later on.
77 : : // To avoid this, use Flush() to reset the value of cachedCoinsUsage in sync with the cacheCoins
78 : : // mapping.
79 [ + - ]: 1068 : (void)coins_view_cache.Flush();
80 : 1068 : }
81 [ + - ][ # # ]: 1068 : }
82 [ + - ]: 38622 : assert(expected_code_path);
83 : 41428 : },
84 : 770511 : [&] {
85 : 155291 : (void)coins_view_cache.Flush();
86 : 155291 : },
87 : 957736 : [&] {
88 : 342516 : (void)coins_view_cache.Sync();
89 : 342516 : },
90 : 621719 : [&] {
91 : 6499 : uint256 best_block{coins_view_cache.GetBestBlock()};
92 [ + - ][ + - ]: 6499 : if (is_db && best_block.IsNull()) best_block = uint256::ONE;
93 : 6499 : coins_view_cache.SetBestBlock(best_block);
94 : 6499 : },
95 : 657618 : [&] {
96 : 42398 : Coin move_to;
97 [ + - ][ + + ]: 42398 : (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
[ + - ]
98 : 42398 : },
99 : 633065 : [&] {
100 : 17845 : coins_view_cache.Uncache(random_out_point);
101 : 17845 : },
102 : 620149 : [&] {
103 [ + + ]: 4929 : if (fuzzed_data_provider.ConsumeBool()) {
104 : 4407 : backend_coins_view = CCoinsView{};
105 : 4407 : }
106 : 4929 : coins_view_cache.SetBackend(backend_coins_view);
107 : 4929 : },
108 : 615515 : [&] {
109 : 295 : const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
110 [ + + ]: 295 : if (!opt_out_point) {
111 : 30 : good_data = false;
112 : 30 : return;
113 : : }
114 : 265 : random_out_point = *opt_out_point;
115 : 295 : },
116 : 616593 : [&] {
117 : 1373 : const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
118 [ + + ]: 1373 : if (!opt_coin) {
119 : 27 : good_data = false;
120 : 27 : return;
121 : : }
122 [ + - ]: 1346 : random_coin = *opt_coin;
123 [ - + ]: 1373 : },
124 : 616715 : [&] {
125 : 1495 : const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
126 [ + + ]: 1495 : if (!opt_mutable_transaction) {
127 : 67 : good_data = false;
128 : 67 : return;
129 : : }
130 [ + - ]: 1428 : random_mutable_transaction = *opt_mutable_transaction;
131 [ - + ]: 1495 : },
132 : 617476 : [&] {
133 : 2256 : CCoinsMapMemoryResource resource;
134 [ + + ][ + + ]: 2256 : CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};
135 [ + - ][ + + ]: 13769 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
[ + + ]
136 : : {
137 [ + - ]: 11587 : CCoinsCacheEntry coins_cache_entry;
138 [ + - ]: 11587 : coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
139 [ + - ][ + + ]: 11587 : if (fuzzed_data_provider.ConsumeBool()) {
140 [ + - ]: 11438 : coins_cache_entry.coin = random_coin;
141 : 11438 : } else {
142 : 149 : const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
143 [ + + ]: 149 : if (!opt_coin) {
144 : 37 : good_data = false;
145 : 37 : return;
146 : : }
147 [ + - ]: 112 : coins_cache_entry.coin = *opt_coin;
148 [ + + ]: 149 : }
149 [ + - ]: 11550 : coins_map.emplace(random_out_point, std::move(coins_cache_entry));
150 : 11587 : }
151 : 2182 : bool expected_code_path = false;
152 : : try {
153 [ + - ]: 2182 : uint256 best_block{coins_view_cache.GetBestBlock()};
154 [ + - ][ + + ]: 2182 : if (fuzzed_data_provider.ConsumeBool()) best_block = ConsumeUInt256(fuzzed_data_provider);
155 [ + - ][ + - ]: 2182 : if (is_db && best_block.IsNull()) best_block = uint256::ONE;
[ + + ]
156 [ + + ]: 2182 : coins_view_cache.BatchWrite(coins_map, best_block);
157 : 2063 : expected_code_path = true;
158 [ - + ]: 2182 : } catch (const std::logic_error& e) {
159 [ + - ][ + - ]: 119 : if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
[ - + ]
160 : 119 : expected_code_path = true;
161 : 119 : }
162 [ # # ][ + - ]: 119 : }
163 [ + - ]: 2182 : assert(expected_code_path);
164 [ - + ]: 2375 : });
165 : 615220 : }
166 : :
167 : : {
168 [ + - ]: 1691 : const Coin& coin_using_access_coin = coins_view_cache.AccessCoin(random_out_point);
169 [ + - ]: 1691 : const bool exists_using_access_coin = !(coin_using_access_coin == EMPTY_COIN);
170 [ + - ]: 1691 : const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point);
171 [ + - ]: 1691 : const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point);
172 [ + - ]: 1691 : Coin coin_using_get_coin;
173 [ + - ]: 1691 : const bool exists_using_get_coin = coins_view_cache.GetCoin(random_out_point, coin_using_get_coin);
174 [ + + ]: 1691 : if (exists_using_get_coin) {
175 [ + - ][ + - ]: 1064 : assert(coin_using_get_coin == coin_using_access_coin);
176 : 1064 : }
177 [ + + ][ + - ]: 1691 : assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) ||
[ + - ][ + - ]
[ + - ][ + - ]
[ - + ][ + - ]
178 : : (!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin));
179 : : // If HaveCoin on the backend is true, it must also be on the cache if the coin wasn't spent.
180 [ + - ]: 1691 : const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point);
181 [ + - ][ + + ]: 1691 : if (!coin_using_access_coin.IsSpent() && exists_using_have_coin_in_backend) {
[ + + ]
182 [ - + ]: 256 : assert(exists_using_have_coin);
183 : 256 : }
184 [ + - ]: 1691 : Coin coin_using_backend_get_coin;
185 [ + - ][ + + ]: 1691 : if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) {
186 [ + - ]: 270 : assert(exists_using_have_coin_in_backend);
187 : : // Note we can't assert that `coin_using_get_coin == coin_using_backend_get_coin` because the coin in
188 : : // the cache may have been modified but not yet flushed.
189 : 270 : } else {
190 [ + - ]: 1421 : assert(!exists_using_have_coin_in_backend);
191 : : }
192 : 1691 : }
193 : :
194 : : {
195 : 1691 : bool expected_code_path = false;
196 : : try {
197 [ - + ]: 1691 : (void)coins_view_cache.Cursor();
198 [ + - ]: 1691 : } catch (const std::logic_error&) {
199 : 1691 : expected_code_path = true;
200 [ + - ]: 1691 : }
201 [ + - ]: 1691 : assert(expected_code_path);
202 [ + - ]: 1691 : (void)coins_view_cache.DynamicMemoryUsage();
203 [ + - ]: 1691 : (void)coins_view_cache.EstimateSize();
204 [ + - ]: 1691 : (void)coins_view_cache.GetBestBlock();
205 [ + - ]: 1691 : (void)coins_view_cache.GetCacheSize();
206 [ + - ]: 1691 : (void)coins_view_cache.GetHeadBlocks();
207 [ + - ][ + - ]: 1691 : (void)coins_view_cache.HaveInputs(CTransaction{random_mutable_transaction});
208 : : }
209 : :
210 : : {
211 [ + - ]: 1691 : std::unique_ptr<CCoinsViewCursor> coins_view_cursor = backend_coins_view.Cursor();
212 [ + - ][ + - ]: 1691 : assert(is_db ? !!coins_view_cursor : !coins_view_cursor);
213 [ + - ]: 1691 : (void)backend_coins_view.EstimateSize();
214 [ + - ]: 1691 : (void)backend_coins_view.GetBestBlock();
215 [ + - ]: 1691 : (void)backend_coins_view.GetHeadBlocks();
216 : 1691 : }
217 : :
218 [ + - ][ + + ]: 1691 : if (fuzzed_data_provider.ConsumeBool()) {
219 [ + - ]: 1071 : CallOneOf(
220 : 1071 : fuzzed_data_provider,
221 : 4809 : [&] {
222 : 3738 : const CTransaction transaction{random_mutable_transaction};
223 : 3738 : bool is_spent = false;
224 [ + + ]: 3744 : for (const CTxOut& tx_out : transaction.vout) {
225 [ + + ][ + + ]: 3668 : if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
[ - + ]
226 : 6 : is_spent = true;
227 : 6 : }
228 : : }
229 [ + + ]: 76 : if (is_spent) {
230 : : // Avoid:
231 : : // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed.
232 : 3 : return;
233 : : }
234 : 73 : bool expected_code_path = false;
235 [ + - ]: 73 : const int height{int(fuzzed_data_provider.ConsumeIntegral<uint32_t>() >> 1)};
236 [ + - ]: 73 : const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
237 : : try {
238 [ + - ]: 73 : AddCoins(coins_view_cache, transaction, height, possible_overwrite);
239 : 73 : expected_code_path = true;
240 [ # # ]: 73 : } catch (const std::logic_error& e) {
241 [ # # ][ # # ]: 0 : if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
[ # # ]
242 [ # # ]: 0 : assert(!possible_overwrite);
243 : 0 : expected_code_path = true;
244 : 0 : }
245 [ # # ][ # # ]: 0 : }
246 [ + - ]: 73 : assert(expected_code_path);
247 [ - + ]: 3738 : },
248 : 1735 : [&] {
249 [ + - ]: 664 : (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
250 : 664 : },
251 : 1113 : [&] {
252 : 42 : TxValidationState state;
253 : : CAmount tx_fee_out;
254 [ + - ]: 42 : const CTransaction transaction{random_mutable_transaction};
255 [ + + ]: 42 : if (ContainsSpentInput(transaction, coins_view_cache)) {
256 : : // Avoid:
257 : : // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
258 : 1 : return;
259 : : }
260 : 41 : TxValidationState dummy;
261 [ + - ][ + + ]: 41 : if (!CheckTransaction(transaction, dummy)) {
262 : : // It is not allowed to call CheckTxInputs if CheckTransaction failed
263 : 24 : return;
264 : : }
265 [ + - ][ + - ]: 17 : if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
[ + + ]
266 [ + - ][ + - ]: 3 : assert(MoneyRange(tx_fee_out));
267 : 3 : }
268 [ - + ]: 42 : },
269 : 1177 : [&] {
270 : 106 : const CTransaction transaction{random_mutable_transaction};
271 [ + + ]: 106 : if (ContainsSpentInput(transaction, coins_view_cache)) {
272 : : // Avoid:
273 : : // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
274 : 11 : return;
275 : : }
276 [ + - ]: 95 : (void)GetP2SHSigOpCount(transaction, coins_view_cache);
277 [ - + ]: 106 : },
278 : 1165 : [&] {
279 : 94 : const CTransaction transaction{random_mutable_transaction};
280 [ + + ]: 94 : if (ContainsSpentInput(transaction, coins_view_cache)) {
281 : : // Avoid:
282 : : // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
283 : 2 : return;
284 : : }
285 [ + - ]: 92 : const auto flags{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
286 [ + + ][ + + ]: 92 : if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
[ + + ]
287 : : // Avoid:
288 : : // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
289 : 1 : return;
290 : : }
291 [ + - ]: 91 : (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
292 [ - + ]: 94 : },
293 : 1160 : [&] {
294 [ + - ]: 89 : (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
295 : 89 : });
296 : 1071 : }
297 : 3382 : }
298 : :
299 [ + - ]: 4 : FUZZ_TARGET(coins_view, .init = initialize_coins_view)
300 : : {
301 : 0 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
302 : 0 : CCoinsView backend_coins_view;
303 [ # # ]: 0 : TestCoinsView(fuzzed_data_provider, backend_coins_view);
304 : 0 : }
305 : :
306 [ + - ]: 1695 : FUZZ_TARGET(coins_db, .init = initialize_coins_view)
307 : : {
308 : 1691 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
309 : 3382 : auto db_params = DBParams{
310 : 1691 : .path = "", // Memory only.
311 : : .cache_bytes = 1 << 20, // 1MB.
312 : : .memory_only = true,
313 : : };
314 [ + - ]: 1691 : CCoinsViewDB coins_db{std::move(db_params), CoinsViewOptions{}};
315 [ - + ]: 1691 : TestCoinsView(fuzzed_data_provider, coins_db, /* is_db = */ true);
316 : 1691 : }
|