@@ -133,7 +133,13 @@ struct CacheLevel
133133 }
134134};
135135
136- /* * Class for the base of the hierarchy (roughly simulating a memory-backed CCoinsViewDB). */
136+ /* * Class for the base of the hierarchy (roughly simulating a memory-backed CCoinsViewDB).
137+ *
138+ * The initial state consists of the empty UTXO set, though coins whose output index
139+ * is 3 (mod 5) always have GetCoin() succeed (but returning an IsSpent() coin unless a UTXO
140+ * exists). Coins whose output index is 4 (mod 5) have GetCoin() always succeed after being spent.
141+ * This exercises code paths with spent, non-DIRTY cache entries.
142+ */
137143class CoinsViewBottom final : public CCoinsView
138144{
139145 std::map<COutPoint, Coin> m_data;
@@ -143,6 +149,10 @@ class CoinsViewBottom final : public CCoinsView
143149 {
144150 auto it = m_data.find (outpoint);
145151 if (it == m_data.end ()) {
152+ if ((outpoint.n % 5 ) == 3 ) {
153+ coin.Clear ();
154+ return true ;
155+ }
146156 return false ;
147157 } else {
148158 coin = it->second ;
@@ -164,7 +174,7 @@ class CoinsViewBottom final : public CCoinsView
164174 {
165175 for (auto it = data.begin (); it != data.end (); it = erase ? data.erase (it) : std::next (it)) {
166176 if (it->second .flags & CCoinsCacheEntry::DIRTY) {
167- if (it->second .coin .IsSpent ()) {
177+ if (it->second .coin .IsSpent () && (it-> first . n % 5 ) != 4 ) {
168178 m_data.erase (it->first );
169179 } else if (erase) {
170180 m_data[it->first ] = std::move (it->second .coin );
@@ -173,10 +183,10 @@ class CoinsViewBottom final : public CCoinsView
173183 }
174184 } else {
175185 /* For non-dirty entries being written, compare them with what we have. */
186+ auto it2 = m_data.find (it->first );
176187 if (it->second .coin .IsSpent ()) {
177- assert (m_data.count (it-> first ) == 0 );
188+ assert (it2 == m_data.end () || it2-> second . IsSpent () );
178189 } else {
179- auto it2 = m_data.find (it->first );
180190 assert (it2 != m_data.end ());
181191 assert (it->second .coin .out == it2->second .out );
182192 assert (it->second .coin .fCoinBase == it2->second .fCoinBase );
@@ -262,9 +272,9 @@ FUZZ_TARGET(coinscache_sim)
262272 auto real = caches.back ()->GetCoin (data.outpoints [outpointidx], realcoin);
263273 // Compare results.
264274 if (!sim.has_value ()) {
265- assert (!real);
275+ assert (!real || realcoin. IsSpent () );
266276 } else {
267- assert (!realcoin.IsSpent ());
277+ assert (real && !realcoin.IsSpent ());
268278 const auto & simcoin = data.coins [sim->first ];
269279 assert (realcoin.out == simcoin.out );
270280 assert (realcoin.fCoinBase == simcoin.fCoinBase );
@@ -457,9 +467,9 @@ FUZZ_TARGET(coinscache_sim)
457467 bool real = bottom.GetCoin (data.outpoints [outpointidx], realcoin);
458468 auto sim = lookup (outpointidx, 0 );
459469 if (!sim.has_value ()) {
460- assert (!real);
470+ assert (!real || realcoin. IsSpent () );
461471 } else {
462- assert (!realcoin.IsSpent ());
472+ assert (real && !realcoin.IsSpent ());
463473 assert (realcoin.out == data.coins [sim->first ].out );
464474 assert (realcoin.fCoinBase == data.coins [sim->first ].fCoinBase );
465475 assert (realcoin.nHeight == sim->second );
0 commit comments