@@ -202,6 +202,111 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
202202 BOOST_CHECK (missed_an_entry);
203203}
204204
205+ // This test is similar to the previous test
206+ // except the emphasis is on testing the functionality of UpdateCoins
207+ // random txs are created and UpdateCoins is used to update the cache stack
208+ BOOST_AUTO_TEST_CASE (updatecoins_simulation_test)
209+ {
210+ // A simple map to track what we expect the cache stack to represent.
211+ std::map<uint256, CCoins> result;
212+
213+ // The cache stack.
214+ CCoinsViewTest base; // A CCoinsViewTest at the bottom.
215+ std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
216+ stack.push_back (new CCoinsViewCacheTest (&base)); // Start with one cache.
217+
218+ // Track the txids we've used and whether they have been spent or not
219+ std::map<uint256, CAmount> coinbaseids;
220+ std::set<uint256> alltxids;
221+
222+ for (unsigned int i = 0 ; i < NUM_SIMULATION_ITERATIONS; i++) {
223+ {
224+ CMutableTransaction tx;
225+ tx.vin .resize (1 );
226+ tx.vout .resize (1 );
227+ tx.vout [0 ].nValue = i; // Keep txs unique unless intended to duplicate
228+ unsigned int height = InsecureRand32 ();
229+
230+ // 1/10 times create a coinbase
231+ if (InsecureRandRange (10 ) == 0 || coinbaseids.size () < 10 ) {
232+ // PIVX: don't test for duplicate coinbases as those are not possible due to
233+ // BIP34 enforced since the beginning.
234+ coinbaseids[tx.GetHash ()] = tx.vout [0 ].nValue ;
235+ assert (CTransaction (tx).IsCoinBase ());
236+ }
237+ // 9/10 times create a regular tx
238+ else {
239+ uint256 prevouthash;
240+ // equally likely to spend coinbase or non coinbase
241+ std::set<uint256>::iterator txIt = alltxids.lower_bound (GetRandHash ());
242+ if (txIt == alltxids.end ()) {
243+ txIt = alltxids.begin ();
244+ }
245+ prevouthash = *txIt;
246+
247+ // Construct the tx to spend the coins of prevouthash
248+ tx.vin [0 ].prevout .hash = prevouthash;
249+ tx.vin [0 ].prevout .n = 0 ;
250+
251+ // Update the expected result of prevouthash to know these coins are spent
252+ CCoins& oldcoins = result[prevouthash];
253+ oldcoins.Clear ();
254+
255+ // It is of particular importance here that once we spend a coinbase tx hash
256+ // it is no longer available to be duplicated (or spent again)
257+ // BIP 34 in conjunction with enforcing BIP 30 (at least until BIP 34 was active)
258+ // results in the fact that no coinbases were duplicated after they were already spent
259+ alltxids.erase (prevouthash);
260+ coinbaseids.erase (prevouthash);
261+
262+ assert (!CTransaction (tx).IsCoinBase ());
263+ }
264+ // Track this tx to possibly spend later
265+ alltxids.insert (tx.GetHash ());
266+
267+ // Update the expected result to know about the new output coins
268+ CCoins &coins = result[tx.GetHash ()];
269+ coins.FromTx (tx, height);
270+
271+ UpdateCoins (tx, *(stack.back ()), height);
272+ }
273+
274+ // Once every 1000 iterations and at the end, verify the full cache.
275+ if (InsecureRandRange (1000 ) == 1 || i == NUM_SIMULATION_ITERATIONS - 1 ) {
276+ for (std::map<uint256, CCoins>::iterator it = result.begin (); it != result.end (); it++) {
277+ const CCoins* coins = stack.back ()->AccessCoins (it->first );
278+ if (coins) {
279+ BOOST_CHECK (*coins == it->second );
280+ } else {
281+ BOOST_CHECK (it->second .IsPruned ());
282+ }
283+ }
284+ }
285+
286+ if (InsecureRandRange (100 ) == 0 ) {
287+ // Every 100 iterations, change the cache stack.
288+ if (stack.size () > 0 && InsecureRandBool () == 0 ) {
289+ stack.back ()->Flush ();
290+ delete stack.back ();
291+ stack.pop_back ();
292+ }
293+ if (stack.size () == 0 || (stack.size () < 4 && InsecureRandBool ())) {
294+ CCoinsView* tip = &base;
295+ if (stack.size () > 0 ) {
296+ tip = stack.back ();
297+ }
298+ stack.push_back (new CCoinsViewCacheTest (tip));
299+ }
300+ }
301+ }
302+
303+ // Clean up the stack.
304+ while (stack.size () > 0 ) {
305+ delete stack.back ();
306+ stack.pop_back ();
307+ }
308+ }
309+
205310BOOST_AUTO_TEST_CASE (ccoins_serialization)
206311{
207312 // Good example
0 commit comments