@@ -87,9 +87,12 @@ class CCoinsViewCacheTest : public CCoinsViewCache
87
87
{
88
88
// Manually recompute the dynamic usage of the whole data, and compare it.
89
89
size_t ret = memusage::DynamicUsage (cacheCoins);
90
+ size_t count = 0 ;
90
91
for (CCoinsMap::iterator it = cacheCoins.begin (); it != cacheCoins.end (); it++) {
91
92
ret += it->second .coins .DynamicMemoryUsage ();
93
+ ++count;
92
94
}
95
+ BOOST_CHECK_EQUAL (GetCacheSize (), count);
93
96
BOOST_CHECK_EQUAL (DynamicMemoryUsage (), ret);
94
97
}
95
98
@@ -118,10 +121,12 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
118
121
bool removed_all_caches = false ;
119
122
bool reached_4_caches = false ;
120
123
bool added_an_entry = false ;
124
+ bool added_an_unspendable_entry = false ;
121
125
bool removed_an_entry = false ;
122
126
bool updated_an_entry = false ;
123
127
bool found_an_entry = false ;
124
128
bool missed_an_entry = false ;
129
+ bool uncached_an_entry = false ;
125
130
126
131
// A simple map to track what we expect the cache stack to represent.
127
132
std::map<COutPoint, Coin> result;
@@ -143,36 +148,49 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
143
148
{
144
149
uint256 txid = txids[insecure_rand () % txids.size ()]; // txid we're going to modify in this iteration.
145
150
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 ));
147
152
BOOST_CHECK (coin == entry);
148
153
149
154
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 ;
152
162
} 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;
154
166
}
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 );
157
168
} else {
158
- coin.Clear ();
159
169
removed_an_entry = true ;
160
- }
161
- if (coin.IsPruned ()) {
170
+ coin.Clear ();
162
171
stack.back ()->SpendCoin (COutPoint (txid, 0 ));
163
- } else {
164
- stack.back ()->AddCoin (COutPoint (txid, 0 ), Coin (coin), true );
165
172
}
166
173
}
167
174
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
+
168
183
// Once every 1000 iterations and at the end, verify the full cache.
169
184
if (insecure_rand () % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1 ) {
170
185
for (auto it = result.begin (); it != result.end (); it++) {
186
+ bool have = stack.back ()->HaveCoins (it->first );
171
187
const Coin& coin = stack.back ()->AccessCoin (it->first );
188
+ BOOST_CHECK (have == !coin.IsPruned ());
172
189
BOOST_CHECK (coin == it->second );
173
190
if (coin.IsPruned ()) {
174
191
missed_an_entry = true ;
175
192
} else {
193
+ BOOST_CHECK (stack.back ()->HaveCoinsInCache (it->first ));
176
194
found_an_entry = true ;
177
195
}
178
196
}
@@ -222,10 +240,12 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
222
240
BOOST_CHECK (removed_all_caches);
223
241
BOOST_CHECK (reached_4_caches);
224
242
BOOST_CHECK (added_an_entry);
243
+ BOOST_CHECK (added_an_unspendable_entry);
225
244
BOOST_CHECK (removed_an_entry);
226
245
BOOST_CHECK (updated_an_entry);
227
246
BOOST_CHECK (found_an_entry);
228
247
BOOST_CHECK (missed_an_entry);
248
+ BOOST_CHECK (uncached_an_entry);
229
249
}
230
250
231
251
// Store of all necessary tx and undo data for next test
@@ -275,6 +295,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
275
295
tx.vin .resize (1 );
276
296
tx.vout .resize (1 );
277
297
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
278
299
unsigned int height = insecure_rand ();
279
300
Coin oldcoins;
280
301
@@ -393,11 +414,24 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
393
414
// Once every 1000 iterations and at the end, verify the full cache.
394
415
if (insecure_rand () % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1 ) {
395
416
for (auto it = result.begin (); it != result.end (); it++) {
417
+ bool have = stack.back ()->HaveCoins (it->first );
396
418
const Coin& coin = stack.back ()->AccessCoin (it->first );
419
+ BOOST_CHECK (have == !coin.IsPruned ());
397
420
BOOST_CHECK (coin == it->second );
398
421
}
399
422
}
400
423
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
+
401
435
if (insecure_rand () % 100 == 0 ) {
402
436
// Every 100 iterations, flush an intermediate cache
403
437
if (stack.size () > 1 && insecure_rand () % 2 == 0 ) {
0 commit comments