Introduce CHashVerifier to hash read data
[bitcoinplatinum.git] / src / test / coins_tests.cpp
blob1b5d8e91b552ac9a0dd57f813159fab219800d0c
1 // Copyright (c) 2014-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include "coins.h"
6 #include "script/standard.h"
7 #include "uint256.h"
8 #include "undo.h"
9 #include "utilstrencodings.h"
10 #include "test/test_bitcoin.h"
11 #include "test/test_random.h"
12 #include "validation.h"
13 #include "consensus/validation.h"
15 #include <vector>
16 #include <map>
18 #include <boost/test/unit_test.hpp>
20 int ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out);
21 void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight);
23 namespace
25 class CCoinsViewTest : public CCoinsView
27 uint256 hashBestBlock_;
28 std::map<uint256, CCoins> map_;
30 public:
31 bool GetCoins(const uint256& txid, CCoins& coins) const
33 std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
34 if (it == map_.end()) {
35 return false;
37 coins = it->second;
38 if (coins.IsPruned() && insecure_rand() % 2 == 0) {
39 // Randomly return false in case of an empty entry.
40 return false;
42 return true;
45 bool HaveCoins(const uint256& txid) const
47 CCoins coins;
48 return GetCoins(txid, coins);
51 uint256 GetBestBlock() const { return hashBestBlock_; }
53 bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock)
55 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
56 if (it->second.flags & CCoinsCacheEntry::DIRTY) {
57 // Same optimization used in CCoinsViewDB is to only write dirty entries.
58 map_[it->first] = it->second.coins;
59 if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) {
60 // Randomly delete empty entries on write.
61 map_.erase(it->first);
64 mapCoins.erase(it++);
66 if (!hashBlock.IsNull())
67 hashBestBlock_ = hashBlock;
68 return true;
72 class CCoinsViewCacheTest : public CCoinsViewCache
74 public:
75 CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {}
77 void SelfTest() const
79 // Manually recompute the dynamic usage of the whole data, and compare it.
80 size_t ret = memusage::DynamicUsage(cacheCoins);
81 for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
82 ret += it->second.coins.DynamicMemoryUsage();
84 BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
87 CCoinsMap& map() { return cacheCoins; }
88 size_t& usage() { return cachedCoinsUsage; }
93 BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
95 static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
97 // This is a large randomized insert/remove simulation test on a variable-size
98 // stack of caches on top of CCoinsViewTest.
100 // It will randomly create/update/delete CCoins entries to a tip of caches, with
101 // txids picked from a limited list of random 256-bit hashes. Occasionally, a
102 // new tip is added to the stack of caches, or the tip is flushed and removed.
104 // During the process, booleans are kept to make sure that the randomized
105 // operation hits all branches.
106 BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
108 // Various coverage trackers.
109 bool removed_all_caches = false;
110 bool reached_4_caches = false;
111 bool added_an_entry = false;
112 bool removed_an_entry = false;
113 bool updated_an_entry = false;
114 bool found_an_entry = false;
115 bool missed_an_entry = false;
117 // A simple map to track what we expect the cache stack to represent.
118 std::map<uint256, CCoins> result;
120 // The cache stack.
121 CCoinsViewTest base; // A CCoinsViewTest at the bottom.
122 std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
123 stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
125 // Use a limited set of random transaction ids, so we do test overwriting entries.
126 std::vector<uint256> txids;
127 txids.resize(NUM_SIMULATION_ITERATIONS / 8);
128 for (unsigned int i = 0; i < txids.size(); i++) {
129 txids[i] = GetRandHash();
132 for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
133 // Do a random modification.
135 uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration.
136 CCoins& coins = result[txid];
137 CCoinsModifier entry = stack.back()->ModifyCoins(txid);
138 BOOST_CHECK(coins == *entry);
139 if (insecure_rand() % 5 == 0 || coins.IsPruned()) {
140 if (coins.IsPruned()) {
141 added_an_entry = true;
142 } else {
143 updated_an_entry = true;
145 coins.nVersion = insecure_rand();
146 coins.vout.resize(1);
147 coins.vout[0].nValue = insecure_rand();
148 *entry = coins;
149 } else {
150 coins.Clear();
151 entry->Clear();
152 removed_an_entry = true;
156 // Once every 1000 iterations and at the end, verify the full cache.
157 if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
158 for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
159 const CCoins* coins = stack.back()->AccessCoins(it->first);
160 if (coins) {
161 BOOST_CHECK(*coins == it->second);
162 found_an_entry = true;
163 } else {
164 BOOST_CHECK(it->second.IsPruned());
165 missed_an_entry = true;
168 BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) {
169 test->SelfTest();
173 if (insecure_rand() % 100 == 0) {
174 // Every 100 iterations, flush an intermediate cache
175 if (stack.size() > 1 && insecure_rand() % 2 == 0) {
176 unsigned int flushIndex = insecure_rand() % (stack.size() - 1);
177 stack[flushIndex]->Flush();
180 if (insecure_rand() % 100 == 0) {
181 // Every 100 iterations, change the cache stack.
182 if (stack.size() > 0 && insecure_rand() % 2 == 0) {
183 //Remove the top cache
184 stack.back()->Flush();
185 delete stack.back();
186 stack.pop_back();
188 if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
189 //Add a new cache
190 CCoinsView* tip = &base;
191 if (stack.size() > 0) {
192 tip = stack.back();
193 } else {
194 removed_all_caches = true;
196 stack.push_back(new CCoinsViewCacheTest(tip));
197 if (stack.size() == 4) {
198 reached_4_caches = true;
204 // Clean up the stack.
205 while (stack.size() > 0) {
206 delete stack.back();
207 stack.pop_back();
210 // Verify coverage.
211 BOOST_CHECK(removed_all_caches);
212 BOOST_CHECK(reached_4_caches);
213 BOOST_CHECK(added_an_entry);
214 BOOST_CHECK(removed_an_entry);
215 BOOST_CHECK(updated_an_entry);
216 BOOST_CHECK(found_an_entry);
217 BOOST_CHECK(missed_an_entry);
220 typedef std::tuple<CTransaction,CTxUndo,CCoins> TxData;
221 // Store of all necessary tx and undo data for next test
222 std::map<uint256, TxData> alltxs;
224 TxData &FindRandomFrom(const std::set<uint256> &txidset) {
225 assert(txidset.size());
226 std::set<uint256>::iterator txIt = txidset.lower_bound(GetRandHash());
227 if (txIt == txidset.end()) {
228 txIt = txidset.begin();
230 std::map<uint256, TxData>::iterator txdit = alltxs.find(*txIt);
231 assert(txdit != alltxs.end());
232 return txdit->second;
236 // This test is similar to the previous test
237 // except the emphasis is on testing the functionality of UpdateCoins
238 // random txs are created and UpdateCoins is used to update the cache stack
239 // In particular it is tested that spending a duplicate coinbase tx
240 // has the expected effect (the other duplicate is overwitten at all cache levels)
241 BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
243 bool spent_a_duplicate_coinbase = false;
244 // A simple map to track what we expect the cache stack to represent.
245 std::map<uint256, CCoins> result;
247 // The cache stack.
248 CCoinsViewTest base; // A CCoinsViewTest at the bottom.
249 std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
250 stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
252 // Track the txids we've used in various sets
253 std::set<uint256> coinbaseids;
254 std::set<uint256> disconnectedids;
255 std::set<uint256> duplicateids;
256 std::set<uint256> utxoset;
258 for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
259 uint32_t randiter = insecure_rand();
261 // 19/20 txs add a new transaction
262 if (randiter % 20 < 19) {
263 CMutableTransaction tx;
264 tx.vin.resize(1);
265 tx.vout.resize(1);
266 tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
267 unsigned int height = insecure_rand();
268 CCoins oldcoins;
270 // 2/20 times create a new coinbase
271 if (randiter % 20 < 2 || coinbaseids.size() < 10) {
272 // 1/10 of those times create a duplicate coinbase
273 if (insecure_rand() % 10 == 0 && coinbaseids.size()) {
274 TxData &txd = FindRandomFrom(coinbaseids);
275 // Reuse the exact same coinbase
276 tx = std::get<0>(txd);
277 // shouldn't be available for reconnection if its been duplicated
278 disconnectedids.erase(tx.GetHash());
280 duplicateids.insert(tx.GetHash());
282 else {
283 coinbaseids.insert(tx.GetHash());
285 assert(CTransaction(tx).IsCoinBase());
288 // 17/20 times reconnect previous or add a regular tx
289 else {
291 uint256 prevouthash;
292 // 1/20 times reconnect a previously disconnected tx
293 if (randiter % 20 == 2 && disconnectedids.size()) {
294 TxData &txd = FindRandomFrom(disconnectedids);
295 tx = std::get<0>(txd);
296 prevouthash = tx.vin[0].prevout.hash;
297 if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevouthash)) {
298 disconnectedids.erase(tx.GetHash());
299 continue;
302 // If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
303 if (utxoset.count(tx.GetHash())) {
304 assert(CTransaction(tx).IsCoinBase());
305 assert(duplicateids.count(tx.GetHash()));
307 disconnectedids.erase(tx.GetHash());
310 // 16/20 times create a regular tx
311 else {
312 TxData &txd = FindRandomFrom(utxoset);
313 prevouthash = std::get<0>(txd).GetHash();
315 // Construct the tx to spend the coins of prevouthash
316 tx.vin[0].prevout.hash = prevouthash;
317 tx.vin[0].prevout.n = 0;
318 assert(!CTransaction(tx).IsCoinBase());
320 // In this simple test coins only have two states, spent or unspent, save the unspent state to restore
321 oldcoins = result[prevouthash];
322 // Update the expected result of prevouthash to know these coins are spent
323 result[prevouthash].Clear();
325 utxoset.erase(prevouthash);
327 // The test is designed to ensure spending a duplicate coinbase will work properly
328 // if that ever happens and not resurrect the previously overwritten coinbase
329 if (duplicateids.count(prevouthash))
330 spent_a_duplicate_coinbase = true;
333 // Update the expected result to know about the new output coins
334 result[tx.GetHash()].FromTx(tx, height);
336 // Call UpdateCoins on the top cache
337 CTxUndo undo;
338 UpdateCoins(tx, *(stack.back()), undo, height);
340 // Update the utxo set for future spends
341 utxoset.insert(tx.GetHash());
343 // Track this tx and undo info to use later
344 alltxs.insert(std::make_pair(tx.GetHash(),std::make_tuple(tx,undo,oldcoins)));
347 //1/20 times undo a previous transaction
348 else if (utxoset.size()) {
349 TxData &txd = FindRandomFrom(utxoset);
351 CTransaction &tx = std::get<0>(txd);
352 CTxUndo &undo = std::get<1>(txd);
353 CCoins &origcoins = std::get<2>(txd);
355 uint256 undohash = tx.GetHash();
357 // Update the expected result
358 // Remove new outputs
359 result[undohash].Clear();
360 // If not coinbase restore prevout
361 if (!tx.IsCoinBase()) {
362 result[tx.vin[0].prevout.hash] = origcoins;
365 // Disconnect the tx from the current UTXO
366 // See code in DisconnectBlock
367 // remove outputs
369 CCoinsModifier outs = stack.back()->ModifyCoins(undohash);
370 outs->Clear();
372 // restore inputs
373 if (!tx.IsCoinBase()) {
374 const COutPoint &out = tx.vin[0].prevout;
375 const CTxInUndo &undoin = undo.vprevout[0];
376 ApplyTxInUndo(undoin, *(stack.back()), out);
378 // Store as a candidate for reconnection
379 disconnectedids.insert(undohash);
381 // Update the utxoset
382 utxoset.erase(undohash);
383 if (!tx.IsCoinBase())
384 utxoset.insert(tx.vin[0].prevout.hash);
387 // Once every 1000 iterations and at the end, verify the full cache.
388 if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
389 for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
390 const CCoins* coins = stack.back()->AccessCoins(it->first);
391 if (coins) {
392 BOOST_CHECK(*coins == it->second);
393 } else {
394 BOOST_CHECK(it->second.IsPruned());
399 if (insecure_rand() % 100 == 0) {
400 // Every 100 iterations, flush an intermediate cache
401 if (stack.size() > 1 && insecure_rand() % 2 == 0) {
402 unsigned int flushIndex = insecure_rand() % (stack.size() - 1);
403 stack[flushIndex]->Flush();
406 if (insecure_rand() % 100 == 0) {
407 // Every 100 iterations, change the cache stack.
408 if (stack.size() > 0 && insecure_rand() % 2 == 0) {
409 stack.back()->Flush();
410 delete stack.back();
411 stack.pop_back();
413 if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
414 CCoinsView* tip = &base;
415 if (stack.size() > 0) {
416 tip = stack.back();
418 stack.push_back(new CCoinsViewCacheTest(tip));
423 // Clean up the stack.
424 while (stack.size() > 0) {
425 delete stack.back();
426 stack.pop_back();
429 // Verify coverage.
430 BOOST_CHECK(spent_a_duplicate_coinbase);
433 BOOST_AUTO_TEST_CASE(ccoins_serialization)
435 // Good example
436 CDataStream ss1(ParseHex("0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e"), SER_DISK, CLIENT_VERSION);
437 CCoins cc1;
438 ss1 >> cc1;
439 BOOST_CHECK_EQUAL(cc1.nVersion, 1);
440 BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
441 BOOST_CHECK_EQUAL(cc1.nHeight, 203998);
442 BOOST_CHECK_EQUAL(cc1.vout.size(), 2);
443 BOOST_CHECK_EQUAL(cc1.IsAvailable(0), false);
444 BOOST_CHECK_EQUAL(cc1.IsAvailable(1), true);
445 BOOST_CHECK_EQUAL(cc1.vout[1].nValue, 60000000000ULL);
446 BOOST_CHECK_EQUAL(HexStr(cc1.vout[1].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
448 // Good example
449 CDataStream ss2(ParseHex("0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b"), SER_DISK, CLIENT_VERSION);
450 CCoins cc2;
451 ss2 >> cc2;
452 BOOST_CHECK_EQUAL(cc2.nVersion, 1);
453 BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
454 BOOST_CHECK_EQUAL(cc2.nHeight, 120891);
455 BOOST_CHECK_EQUAL(cc2.vout.size(), 17);
456 for (int i = 0; i < 17; i++) {
457 BOOST_CHECK_EQUAL(cc2.IsAvailable(i), i == 4 || i == 16);
459 BOOST_CHECK_EQUAL(cc2.vout[4].nValue, 234925952);
460 BOOST_CHECK_EQUAL(HexStr(cc2.vout[4].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("61b01caab50f1b8e9c50a5057eb43c2d9563a4ee"))))));
461 BOOST_CHECK_EQUAL(cc2.vout[16].nValue, 110397);
462 BOOST_CHECK_EQUAL(HexStr(cc2.vout[16].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
464 // Smallest possible example
465 CDataStream ssx(SER_DISK, CLIENT_VERSION);
466 BOOST_CHECK_EQUAL(HexStr(ssx.begin(), ssx.end()), "");
468 CDataStream ss3(ParseHex("0002000600"), SER_DISK, CLIENT_VERSION);
469 CCoins cc3;
470 ss3 >> cc3;
471 BOOST_CHECK_EQUAL(cc3.nVersion, 0);
472 BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
473 BOOST_CHECK_EQUAL(cc3.nHeight, 0);
474 BOOST_CHECK_EQUAL(cc3.vout.size(), 1);
475 BOOST_CHECK_EQUAL(cc3.IsAvailable(0), true);
476 BOOST_CHECK_EQUAL(cc3.vout[0].nValue, 0);
477 BOOST_CHECK_EQUAL(cc3.vout[0].scriptPubKey.size(), 0);
479 // scriptPubKey that ends beyond the end of the stream
480 CDataStream ss4(ParseHex("0002000800"), SER_DISK, CLIENT_VERSION);
481 try {
482 CCoins cc4;
483 ss4 >> cc4;
484 BOOST_CHECK_MESSAGE(false, "We should have thrown");
485 } catch (const std::ios_base::failure& e) {
488 // Very large scriptPubKey (3*10^9 bytes) past the end of the stream
489 CDataStream tmp(SER_DISK, CLIENT_VERSION);
490 uint64_t x = 3000000000ULL;
491 tmp << VARINT(x);
492 BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00");
493 CDataStream ss5(ParseHex("0002008a95c0bb0000"), SER_DISK, CLIENT_VERSION);
494 try {
495 CCoins cc5;
496 ss5 >> cc5;
497 BOOST_CHECK_MESSAGE(false, "We should have thrown");
498 } catch (const std::ios_base::failure& e) {
502 const static uint256 TXID;
503 const static CAmount PRUNED = -1;
504 const static CAmount ABSENT = -2;
505 const static CAmount FAIL = -3;
506 const static CAmount VALUE1 = 100;
507 const static CAmount VALUE2 = 200;
508 const static CAmount VALUE3 = 300;
509 const static char DIRTY = CCoinsCacheEntry::DIRTY;
510 const static char FRESH = CCoinsCacheEntry::FRESH;
511 const static char NO_ENTRY = -1;
513 const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
514 const static auto CLEAN_FLAGS = {char(0), FRESH};
515 const static auto ABSENT_FLAGS = {NO_ENTRY};
517 void SetCoinsValue(CAmount value, CCoins& coins)
519 assert(value != ABSENT);
520 coins.Clear();
521 assert(coins.IsPruned());
522 if (value != PRUNED) {
523 coins.vout.emplace_back();
524 coins.vout.back().nValue = value;
525 assert(!coins.IsPruned());
529 size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
531 if (value == ABSENT) {
532 assert(flags == NO_ENTRY);
533 return 0;
535 assert(flags != NO_ENTRY);
536 CCoinsCacheEntry entry;
537 entry.flags = flags;
538 SetCoinsValue(value, entry.coins);
539 auto inserted = map.emplace(TXID, std::move(entry));
540 assert(inserted.second);
541 return inserted.first->second.coins.DynamicMemoryUsage();
544 void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
546 auto it = map.find(TXID);
547 if (it == map.end()) {
548 value = ABSENT;
549 flags = NO_ENTRY;
550 } else {
551 if (it->second.coins.IsPruned()) {
552 assert(it->second.coins.vout.size() == 0);
553 value = PRUNED;
554 } else {
555 assert(it->second.coins.vout.size() == 1);
556 value = it->second.coins.vout[0].nValue;
558 flags = it->second.flags;
559 assert(flags != NO_ENTRY);
563 void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
565 CCoinsMap map;
566 InsertCoinsMapEntry(map, value, flags);
567 view.BatchWrite(map, {});
570 class SingleEntryCacheTest
572 public:
573 SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
575 WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
576 cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
579 CCoinsView root;
580 CCoinsViewCacheTest base{&root};
581 CCoinsViewCacheTest cache{&base};
584 void CheckAccessCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
586 SingleEntryCacheTest test(base_value, cache_value, cache_flags);
587 test.cache.AccessCoins(TXID);
588 test.cache.SelfTest();
590 CAmount result_value;
591 char result_flags;
592 GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
593 BOOST_CHECK_EQUAL(result_value, expected_value);
594 BOOST_CHECK_EQUAL(result_flags, expected_flags);
597 BOOST_AUTO_TEST_CASE(ccoins_access)
599 /* Check AccessCoin behavior, requesting a coin from a cache view layered on
600 * top of a base view, and checking the resulting entry in the cache after
601 * the access.
603 * Base Cache Result Cache Result
604 * Value Value Value Flags Flags
606 CheckAccessCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
607 CheckAccessCoins(ABSENT, PRUNED, PRUNED, 0 , 0 );
608 CheckAccessCoins(ABSENT, PRUNED, PRUNED, FRESH , FRESH );
609 CheckAccessCoins(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
610 CheckAccessCoins(ABSENT, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
611 CheckAccessCoins(ABSENT, VALUE2, VALUE2, 0 , 0 );
612 CheckAccessCoins(ABSENT, VALUE2, VALUE2, FRESH , FRESH );
613 CheckAccessCoins(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY );
614 CheckAccessCoins(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
615 CheckAccessCoins(PRUNED, ABSENT, PRUNED, NO_ENTRY , FRESH );
616 CheckAccessCoins(PRUNED, PRUNED, PRUNED, 0 , 0 );
617 CheckAccessCoins(PRUNED, PRUNED, PRUNED, FRESH , FRESH );
618 CheckAccessCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
619 CheckAccessCoins(PRUNED, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
620 CheckAccessCoins(PRUNED, VALUE2, VALUE2, 0 , 0 );
621 CheckAccessCoins(PRUNED, VALUE2, VALUE2, FRESH , FRESH );
622 CheckAccessCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY );
623 CheckAccessCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
624 CheckAccessCoins(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 );
625 CheckAccessCoins(VALUE1, PRUNED, PRUNED, 0 , 0 );
626 CheckAccessCoins(VALUE1, PRUNED, PRUNED, FRESH , FRESH );
627 CheckAccessCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
628 CheckAccessCoins(VALUE1, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
629 CheckAccessCoins(VALUE1, VALUE2, VALUE2, 0 , 0 );
630 CheckAccessCoins(VALUE1, VALUE2, VALUE2, FRESH , FRESH );
631 CheckAccessCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY );
632 CheckAccessCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
635 void CheckModifyCoins(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags)
637 SingleEntryCacheTest test(base_value, cache_value, cache_flags);
638 SetCoinsValue(modify_value, *test.cache.ModifyCoins(TXID));
639 test.cache.SelfTest();
641 CAmount result_value;
642 char result_flags;
643 GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
644 BOOST_CHECK_EQUAL(result_value, expected_value);
645 BOOST_CHECK_EQUAL(result_flags, expected_flags);
648 BOOST_AUTO_TEST_CASE(ccoins_modify)
650 /* Check ModifyCoin behavior, requesting a coin from a cache view layered on
651 * top of a base view, writing a modification to the coin, and then checking
652 * the resulting entry in the cache after the modification.
654 * Base Cache Write Result Cache Result
655 * Value Value Value Value Flags Flags
657 CheckModifyCoins(ABSENT, ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY );
658 CheckModifyCoins(ABSENT, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH);
659 CheckModifyCoins(ABSENT, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
660 CheckModifyCoins(ABSENT, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
661 CheckModifyCoins(ABSENT, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
662 CheckModifyCoins(ABSENT, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
663 CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
664 CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
665 CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
666 CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
667 CheckModifyCoins(ABSENT, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
668 CheckModifyCoins(ABSENT, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
669 CheckModifyCoins(ABSENT, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
670 CheckModifyCoins(ABSENT, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
671 CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
672 CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
673 CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
674 CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
675 CheckModifyCoins(PRUNED, ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY );
676 CheckModifyCoins(PRUNED, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH);
677 CheckModifyCoins(PRUNED, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
678 CheckModifyCoins(PRUNED, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
679 CheckModifyCoins(PRUNED, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
680 CheckModifyCoins(PRUNED, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
681 CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
682 CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
683 CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
684 CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
685 CheckModifyCoins(PRUNED, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
686 CheckModifyCoins(PRUNED, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
687 CheckModifyCoins(PRUNED, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
688 CheckModifyCoins(PRUNED, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
689 CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
690 CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
691 CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
692 CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
693 CheckModifyCoins(VALUE1, ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY );
694 CheckModifyCoins(VALUE1, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY );
695 CheckModifyCoins(VALUE1, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
696 CheckModifyCoins(VALUE1, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
697 CheckModifyCoins(VALUE1, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
698 CheckModifyCoins(VALUE1, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
699 CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
700 CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
701 CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
702 CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
703 CheckModifyCoins(VALUE1, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
704 CheckModifyCoins(VALUE1, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
705 CheckModifyCoins(VALUE1, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
706 CheckModifyCoins(VALUE1, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
707 CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
708 CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
709 CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
710 CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
713 void CheckModifyNewCoinsBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
715 SingleEntryCacheTest test(base_value, cache_value, cache_flags);
717 CAmount result_value;
718 char result_flags;
719 try {
720 SetCoinsValue(modify_value, *test.cache.ModifyNewCoins(TXID, coinbase));
721 GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
722 } catch (std::logic_error& e) {
723 result_value = FAIL;
724 result_flags = NO_ENTRY;
727 BOOST_CHECK_EQUAL(result_value, expected_value);
728 BOOST_CHECK_EQUAL(result_flags, expected_flags);
731 // Simple wrapper for CheckModifyNewCoinsBase function above that loops through
732 // different possible base_values, making sure each one gives the same results.
733 // This wrapper lets the modify_new test below be shorter and less repetitive,
734 // while still verifying that the CoinsViewCache::ModifyNewCoins implementation
735 // ignores base values.
736 template <typename... Args>
737 void CheckModifyNewCoins(Args&&... args)
739 for (CAmount base_value : {ABSENT, PRUNED, VALUE1})
740 CheckModifyNewCoinsBase(base_value, std::forward<Args>(args)...);
743 BOOST_AUTO_TEST_CASE(ccoins_modify_new)
745 /* Check ModifyNewCoin behavior, requesting a new coin from a cache view,
746 * writing a modification to the coin, and then checking the resulting
747 * entry in the cache after the modification. Verify behavior with the
748 * with the ModifyNewCoin coinbase argument set to false, and to true.
750 * Cache Write Result Cache Result Coinbase
751 * Value Value Value Flags Flags
753 CheckModifyNewCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY , false);
754 CheckModifyNewCoins(ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY , true );
755 CheckModifyNewCoins(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH, false);
756 CheckModifyNewCoins(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY , true );
757 CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, 0 , NO_ENTRY , false);
758 CheckModifyNewCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY , true );
759 CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY , false);
760 CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY , true );
761 CheckModifyNewCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , false);
762 CheckModifyNewCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , true );
763 CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , false);
764 CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , true );
765 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
766 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, 0 , DIRTY , true );
767 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
768 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
769 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY , false);
770 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY , true );
771 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
772 CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
773 CheckModifyNewCoins(VALUE2, PRUNED, FAIL , 0 , NO_ENTRY , false);
774 CheckModifyNewCoins(VALUE2, PRUNED, PRUNED, 0 , DIRTY , true );
775 CheckModifyNewCoins(VALUE2, PRUNED, FAIL , FRESH , NO_ENTRY , false);
776 CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY , true );
777 CheckModifyNewCoins(VALUE2, PRUNED, FAIL , DIRTY , NO_ENTRY , false);
778 CheckModifyNewCoins(VALUE2, PRUNED, PRUNED, DIRTY , DIRTY , true );
779 CheckModifyNewCoins(VALUE2, PRUNED, FAIL , DIRTY|FRESH, NO_ENTRY , false);
780 CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , true );
781 CheckModifyNewCoins(VALUE2, VALUE3, FAIL , 0 , NO_ENTRY , false);
782 CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, 0 , DIRTY , true );
783 CheckModifyNewCoins(VALUE2, VALUE3, FAIL , FRESH , NO_ENTRY , false);
784 CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
785 CheckModifyNewCoins(VALUE2, VALUE3, FAIL , DIRTY , NO_ENTRY , false);
786 CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY , DIRTY , true );
787 CheckModifyNewCoins(VALUE2, VALUE3, FAIL , DIRTY|FRESH, NO_ENTRY , false);
788 CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
791 void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
793 SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
795 CAmount result_value;
796 char result_flags;
797 try {
798 WriteCoinsViewEntry(test.cache, child_value, child_flags);
799 test.cache.SelfTest();
800 GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
801 } catch (std::logic_error& e) {
802 result_value = FAIL;
803 result_flags = NO_ENTRY;
806 BOOST_CHECK_EQUAL(result_value, expected_value);
807 BOOST_CHECK_EQUAL(result_flags, expected_flags);
810 BOOST_AUTO_TEST_CASE(ccoins_write)
812 /* Check BatchWrite behavior, flushing one entry from a child cache to a
813 * parent cache, and checking the resulting entry in the parent cache
814 * after the write.
816 * Parent Child Result Parent Child Result
817 * Value Value Value Flags Flags Flags
819 CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY );
820 CheckWriteCoins(ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY , DIRTY );
821 CheckWriteCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY );
822 CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY , DIRTY );
823 CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH);
824 CheckWriteCoins(PRUNED, ABSENT, PRUNED, 0 , NO_ENTRY , 0 );
825 CheckWriteCoins(PRUNED, ABSENT, PRUNED, FRESH , NO_ENTRY , FRESH );
826 CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY , NO_ENTRY , DIRTY );
827 CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
828 CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
829 CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
830 CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
831 CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
832 CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
833 CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
834 CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
835 CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
836 CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
837 CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
838 CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
839 CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
840 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
841 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
842 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
843 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
844 CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0 , NO_ENTRY , 0 );
845 CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH , NO_ENTRY , FRESH );
846 CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
847 CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
848 CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
849 CheckWriteCoins(VALUE1, PRUNED, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
850 CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
851 CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
852 CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
853 CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
854 CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
855 CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
856 CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
857 CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
858 CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
859 CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
860 CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
861 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
862 CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
863 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
865 // The checks above omit cases where the child flags are not DIRTY, since
866 // they would be too repetitive (the parent cache is never updated in these
867 // cases). The loop below covers these cases and makes sure the parent cache
868 // is always left unchanged.
869 for (CAmount parent_value : {ABSENT, PRUNED, VALUE1})
870 for (CAmount child_value : {ABSENT, PRUNED, VALUE2})
871 for (char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
872 for (char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
873 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
876 BOOST_AUTO_TEST_SUITE_END()