@@ -87,9 +87,12 @@ class CCoinsViewCacheTest : public CCoinsViewCache
8787 {
8888 // Manually recompute the dynamic usage of the whole data, and compare it.
8989 size_t ret = memusage::DynamicUsage (cacheCoins);
90+ size_t count = 0 ;
9091 for (CCoinsMap::iterator it = cacheCoins.begin (); it != cacheCoins.end (); it++) {
9192 ret += it->second .coins .DynamicMemoryUsage ();
93+ ++count;
9294 }
95+ BOOST_CHECK_EQUAL (GetCacheSize (), count);
9396 BOOST_CHECK_EQUAL (DynamicMemoryUsage (), ret);
9497 }
9598
@@ -118,10 +121,12 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
118121 bool removed_all_caches = false ;
119122 bool reached_4_caches = false ;
120123 bool added_an_entry = false ;
124+ bool added_an_unspendable_entry = false ;
121125 bool removed_an_entry = false ;
122126 bool updated_an_entry = false ;
123127 bool found_an_entry = false ;
124128 bool missed_an_entry = false ;
129+ bool uncached_an_entry = false ;
125130
126131 // A simple map to track what we expect the cache stack to represent.
127132 std::map<COutPoint, Coin> result;
@@ -143,36 +148,49 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
143148 {
144149 uint256 txid = txids[insecure_rand () % txids.size ()]; // txid we're going to modify in this iteration.
145150 Coin& coin = result[COutPoint (txid, 0 )];
146- const Coin& entry = stack.back ()->AccessCoin (COutPoint (txid, 0 ));
151+ const Coin& entry = ( insecure_rand () % 500 == 0 ) ? AccessByTxid (*stack. back (), txid) : stack.back ()->AccessCoin (COutPoint (txid, 0 ));
147152 BOOST_CHECK (coin == entry);
148153
149154 if (insecure_rand () % 5 == 0 || coin.IsPruned ()) {
150- if (coin.IsPruned ()) {
151- added_an_entry = true ;
155+ Coin newcoin;
156+ newcoin.out .nValue = insecure_rand ();
157+ newcoin.nHeight = 1 ;
158+ if (insecure_rand () % 16 == 0 && coin.IsPruned ()) {
159+ newcoin.out .scriptPubKey .assign (1 + (insecure_rand () & 0x3F ), OP_RETURN);
160+ BOOST_CHECK (newcoin.out .scriptPubKey .IsUnspendable ());
161+ added_an_unspendable_entry = true ;
152162 } else {
153- updated_an_entry = true ;
163+ newcoin.out .scriptPubKey .assign (insecure_rand () & 0x3F , 0 ); // Random sizes so we can test memory usage accounting
164+ (coin.IsPruned () ? added_an_entry : updated_an_entry) = true ;
165+ coin = newcoin;
154166 }
155- coin.out .nValue = insecure_rand ();
156- coin.nHeight = 1 ;
167+ stack.back ()->AddCoin (COutPoint (txid, 0 ), std::move (newcoin), !coin.IsPruned () || insecure_rand () & 1 );
157168 } else {
158- coin.Clear ();
159169 removed_an_entry = true ;
160- }
161- if (coin.IsPruned ()) {
170+ coin.Clear ();
162171 stack.back ()->SpendCoin (COutPoint (txid, 0 ));
163- } else {
164- stack.back ()->AddCoin (COutPoint (txid, 0 ), Coin (coin), true );
165172 }
166173 }
167174
175+ // One every 10 iterations, remove a random entry from the cache
176+ if (insecure_rand () % 10 ) {
177+ COutPoint out (txids[insecure_rand () % txids.size ()], 0 );
178+ int cacheid = insecure_rand () % stack.size ();
179+ stack[cacheid]->Uncache (out);
180+ uncached_an_entry |= !stack[cacheid]->HaveCoinsInCache (out);
181+ }
182+
168183 // Once every 1000 iterations and at the end, verify the full cache.
169184 if (insecure_rand () % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1 ) {
170185 for (auto it = result.begin (); it != result.end (); it++) {
186+ bool have = stack.back ()->HaveCoins (it->first );
171187 const Coin& coin = stack.back ()->AccessCoin (it->first );
188+ BOOST_CHECK (have == !coin.IsPruned ());
172189 BOOST_CHECK (coin == it->second );
173190 if (coin.IsPruned ()) {
174191 missed_an_entry = true ;
175192 } else {
193+ BOOST_CHECK (stack.back ()->HaveCoinsInCache (it->first ));
176194 found_an_entry = true ;
177195 }
178196 }
@@ -222,10 +240,12 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
222240 BOOST_CHECK (removed_all_caches);
223241 BOOST_CHECK (reached_4_caches);
224242 BOOST_CHECK (added_an_entry);
243+ BOOST_CHECK (added_an_unspendable_entry);
225244 BOOST_CHECK (removed_an_entry);
226245 BOOST_CHECK (updated_an_entry);
227246 BOOST_CHECK (found_an_entry);
228247 BOOST_CHECK (missed_an_entry);
248+ BOOST_CHECK (uncached_an_entry);
229249}
230250
231251// Store of all necessary tx and undo data for next test
@@ -275,6 +295,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
275295 tx.vin .resize (1 );
276296 tx.vout .resize (1 );
277297 tx.vout [0 ].nValue = i; // Keep txs unique unless intended to duplicate
298+ tx.vout [0 ].scriptPubKey .assign (insecure_rand () & 0x3F , 0 ); // Random sizes so we can test memory usage accounting
278299 unsigned int height = insecure_rand ();
279300 Coin oldcoins;
280301
@@ -393,11 +414,24 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
393414 // Once every 1000 iterations and at the end, verify the full cache.
394415 if (insecure_rand () % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1 ) {
395416 for (auto it = result.begin (); it != result.end (); it++) {
417+ bool have = stack.back ()->HaveCoins (it->first );
396418 const Coin& coin = stack.back ()->AccessCoin (it->first );
419+ BOOST_CHECK (have == !coin.IsPruned ());
397420 BOOST_CHECK (coin == it->second );
398421 }
399422 }
400423
424+ // One every 10 iterations, remove a random entry from the cache
425+ if (utxoset.size () > 1 && insecure_rand () % 30 ) {
426+ stack[insecure_rand () % stack.size ()]->Uncache (FindRandomFrom (utxoset)->first );
427+ }
428+ if (disconnectedids.size () > 1 && insecure_rand () % 30 ) {
429+ stack[insecure_rand () % stack.size ()]->Uncache (FindRandomFrom (disconnectedids)->first );
430+ }
431+ if (duplicateids.size () > 1 && insecure_rand () % 30 ) {
432+ stack[insecure_rand () % stack.size ()]->Uncache (FindRandomFrom (duplicateids)->first );
433+ }
434+
401435 if (insecure_rand () % 100 == 0 ) {
402436 // Every 100 iterations, flush an intermediate cache
403437 if (stack.size () > 1 && insecure_rand () % 2 == 0 ) {
0 commit comments