1- // Copyright (c) 2020-2022 The Bitcoin Core developers
1+ // Copyright (c) 2020-present The Bitcoin Core developers
22// Distributed under the MIT software license, see the accompanying
33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
1414#include < test/fuzz/fuzz.h>
1515#include < test/fuzz/util.h>
1616#include < test/util/setup_common.h>
17+ #include < txdb.h>
1718#include < util/hasher.h>
1819
1920#include < cassert>
@@ -41,13 +42,12 @@ void initialize_coins_view()
4142 static const auto testing_setup = MakeNoLogFileContext<>();
4243}
4344
44- FUZZ_TARGET (coins_view, .init = initialize_coins_view )
45+ void TestCoinsView (FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend_coins_view, bool is_db )
4546{
46- FuzzedDataProvider fuzzed_data_provider{buffer.data (), buffer.size ()};
4747 bool good_data{true };
4848
49- CCoinsView backend_coins_view;
5049 CCoinsViewCache coins_view_cache{&backend_coins_view, /* deterministic=*/ true };
50+ if (is_db) coins_view_cache.SetBestBlock (uint256::ONE);
5151 COutPoint random_out_point;
5252 Coin random_coin;
5353 CMutableTransaction random_mutable_transaction;
@@ -69,6 +69,12 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
6969 if (e.what () == std::string{" Attempted to overwrite an unspent coin (when possible_overwrite is false)" }) {
7070 assert (!possible_overwrite);
7171 expected_code_path = true ;
72+ // AddCoin() decreases cachedCoinsUsage by the memory usage of the old coin at the beginning and
73+ // increases it by the value of the new coin at the end. If it throws in the process, the value
74+ // of cachedCoinsUsage would have been incorrectly decreased, leading to an underflow later on.
75+ // To avoid this, use Flush() to reset the value of cachedCoinsUsage in sync with the cacheCoins
76+ // mapping.
77+ (void )coins_view_cache.Flush ();
7278 }
7379 }
7480 assert (expected_code_path);
@@ -80,7 +86,10 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
8086 (void )coins_view_cache.Sync ();
8187 },
8288 [&] {
83- coins_view_cache.SetBestBlock (ConsumeUInt256 (fuzzed_data_provider));
89+ uint256 best_block{ConsumeUInt256 (fuzzed_data_provider)};
90+ // Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite().
91+ if (is_db && best_block.IsNull ()) best_block = uint256::ONE;
92+ coins_view_cache.SetBestBlock (best_block);
8493 },
8594 [&] {
8695 Coin move_to;
@@ -148,7 +157,11 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
148157 bool expected_code_path = false ;
149158 try {
150159 auto cursor{CoinsViewCacheCursor (usage, sentinel, coins_map, /* will_erase=*/ true )};
151- coins_view_cache.BatchWrite (cursor, fuzzed_data_provider.ConsumeBool () ? ConsumeUInt256 (fuzzed_data_provider) : coins_view_cache.GetBestBlock ());
160+ uint256 best_block{coins_view_cache.GetBestBlock ()};
161+ if (fuzzed_data_provider.ConsumeBool ()) best_block = ConsumeUInt256 (fuzzed_data_provider);
162+ // Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite().
163+ if (is_db && best_block.IsNull ()) best_block = uint256::ONE;
164+ coins_view_cache.BatchWrite (cursor, best_block);
152165 expected_code_path = true ;
153166 } catch (const std::logic_error& e) {
154167 if (e.what () == std::string{" FRESH flag misapplied to coin that exists in parent cache" }) {
@@ -202,7 +215,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
202215
203216 {
204217 std::unique_ptr<CCoinsViewCursor> coins_view_cursor = backend_coins_view.Cursor ();
205- assert (!coins_view_cursor);
218+ assert (is_db == ! !coins_view_cursor);
206219 (void )backend_coins_view.EstimateSize ();
207220 (void )backend_coins_view.GetBestBlock ();
208221 (void )backend_coins_view.GetHeadBlocks ();
@@ -288,3 +301,22 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
288301 });
289302 }
290303}
304+
305+ FUZZ_TARGET (coins_view, .init = initialize_coins_view)
306+ {
307+ FuzzedDataProvider fuzzed_data_provider{buffer.data (), buffer.size ()};
308+ CCoinsView backend_coins_view;
309+ TestCoinsView (fuzzed_data_provider, backend_coins_view, /* is_db=*/ false );
310+ }
311+
312+ FUZZ_TARGET (coins_view_db, .init = initialize_coins_view)
313+ {
314+ FuzzedDataProvider fuzzed_data_provider{buffer.data (), buffer.size ()};
315+ auto db_params = DBParams{
316+ .path = " " ,
317+ .cache_bytes = 1_MiB,
318+ .memory_only = true ,
319+ };
320+ CCoinsViewDB coins_db{std::move (db_params), CoinsViewOptions{}};
321+ TestCoinsView (fuzzed_data_provider, coins_db, /* is_db=*/ true );
322+ }
0 commit comments