@@ -133,7 +133,13 @@ struct CacheLevel
133
133
}
134
134
};
135
135
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
+ */
137
143
class CoinsViewBottom final : public CCoinsView
138
144
{
139
145
std::map<COutPoint, Coin> m_data;
@@ -143,6 +149,10 @@ class CoinsViewBottom final : public CCoinsView
143
149
{
144
150
auto it = m_data.find (outpoint);
145
151
if (it == m_data.end ()) {
152
+ if ((outpoint.n % 5 ) == 3 ) {
153
+ coin.Clear ();
154
+ return true ;
155
+ }
146
156
return false ;
147
157
} else {
148
158
coin = it->second ;
@@ -164,7 +174,7 @@ class CoinsViewBottom final : public CCoinsView
164
174
{
165
175
for (auto it = data.begin (); it != data.end (); it = erase ? data.erase (it) : std::next (it)) {
166
176
if (it->second .flags & CCoinsCacheEntry::DIRTY) {
167
- if (it->second .coin .IsSpent ()) {
177
+ if (it->second .coin .IsSpent () && (it-> first . n % 5 ) != 4 ) {
168
178
m_data.erase (it->first );
169
179
} else if (erase) {
170
180
m_data[it->first ] = std::move (it->second .coin );
@@ -173,10 +183,10 @@ class CoinsViewBottom final : public CCoinsView
173
183
}
174
184
} else {
175
185
/* For non-dirty entries being written, compare them with what we have. */
186
+ auto it2 = m_data.find (it->first );
176
187
if (it->second .coin .IsSpent ()) {
177
- assert (m_data.count (it-> first ) == 0 );
188
+ assert (it2 == m_data.end () || it2-> second . IsSpent () );
178
189
} else {
179
- auto it2 = m_data.find (it->first );
180
190
assert (it2 != m_data.end ());
181
191
assert (it->second .coin .out == it2->second .out );
182
192
assert (it->second .coin .fCoinBase == it2->second .fCoinBase );
@@ -262,9 +272,9 @@ FUZZ_TARGET(coinscache_sim)
262
272
auto real = caches.back ()->GetCoin (data.outpoints [outpointidx], realcoin);
263
273
// Compare results.
264
274
if (!sim.has_value ()) {
265
- assert (!real);
275
+ assert (!real || realcoin. IsSpent () );
266
276
} else {
267
- assert (!realcoin.IsSpent ());
277
+ assert (real && !realcoin.IsSpent ());
268
278
const auto & simcoin = data.coins [sim->first ];
269
279
assert (realcoin.out == simcoin.out );
270
280
assert (realcoin.fCoinBase == simcoin.fCoinBase );
@@ -457,9 +467,9 @@ FUZZ_TARGET(coinscache_sim)
457
467
bool real = bottom.GetCoin (data.outpoints [outpointidx], realcoin);
458
468
auto sim = lookup (outpointidx, 0 );
459
469
if (!sim.has_value ()) {
460
- assert (!real);
470
+ assert (!real || realcoin. IsSpent () );
461
471
} else {
462
- assert (!realcoin.IsSpent ());
472
+ assert (real && !realcoin.IsSpent ());
463
473
assert (realcoin.out == data.coins [sim->first ].out );
464
474
assert (realcoin.fCoinBase == data.coins [sim->first ].fCoinBase );
465
475
assert (realcoin.nHeight == sim->second );
0 commit comments