LCOV - code coverage report
Current view: top level - src/test - coins_tests.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 574 587 97.8 %
Date: 2022-04-21 14:51:19 Functions: 29 30 96.7 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 188 198 94.9 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2014-2021 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.
#       4                 :            : 
#       5                 :            : #include <attributes.h>
#       6                 :            : #include <clientversion.h>
#       7                 :            : #include <coins.h>
#       8                 :            : #include <script/standard.h>
#       9                 :            : #include <streams.h>
#      10                 :            : #include <test/util/setup_common.h>
#      11                 :            : #include <txdb.h>
#      12                 :            : #include <uint256.h>
#      13                 :            : #include <undo.h>
#      14                 :            : #include <util/strencodings.h>
#      15                 :            : 
#      16                 :            : #include <map>
#      17                 :            : #include <vector>
#      18                 :            : 
#      19                 :            : #include <boost/test/unit_test.hpp>
#      20                 :            : 
#      21                 :            : int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out);
#      22                 :            : void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight);
#      23                 :            : 
#      24                 :            : namespace
#      25                 :            : {
#      26                 :            : //! equality test
#      27                 :    2279160 : bool operator==(const Coin &a, const Coin &b) {
#      28                 :            :     // Empty Coin objects are always equal.
#      29 [ +  + ][ +  - ]:    2279160 :     if (a.IsSpent() && b.IsSpent()) return true;
#      30         [ +  - ]:     627926 :     return a.fCoinBase == b.fCoinBase &&
#      31         [ +  - ]:     627926 :            a.nHeight == b.nHeight &&
#      32         [ +  - ]:     627926 :            a.out == b.out;
#      33                 :    2279160 : }
#      34                 :            : 
#      35                 :            : class CCoinsViewTest : public CCoinsView
#      36                 :            : {
#      37                 :            :     uint256 hashBestBlock_;
#      38                 :            :     std::map<COutPoint, Coin> map_;
#      39                 :            : 
#      40                 :            : public:
#      41                 :            :     [[nodiscard]] bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
#      42                 :   11881036 :     {
#      43                 :   11881036 :         std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
#      44         [ +  + ]:   11881036 :         if (it == map_.end()) {
#      45                 :   11051870 :             return false;
#      46                 :   11051870 :         }
#      47                 :     829166 :         coin = it->second;
#      48 [ +  + ][ +  + ]:     829166 :         if (coin.IsSpent() && InsecureRandBool() == 0) {
#      49                 :            :             // Randomly return false in case of an empty entry.
#      50                 :     305978 :             return false;
#      51                 :     305978 :         }
#      52                 :     523188 :         return true;
#      53                 :     829166 :     }
#      54                 :            : 
#      55                 :          0 :     uint256 GetBestBlock() const override { return hashBestBlock_; }
#      56                 :            : 
#      57                 :            :     bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override
#      58                 :        518 :     {
#      59         [ +  + ]:     612946 :         for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
#      60         [ +  + ]:     612428 :             if (it->second.flags & CCoinsCacheEntry::DIRTY) {
#      61                 :            :                 // Same optimization used in CCoinsViewDB is to only write dirty entries.
#      62                 :     156388 :                 map_[it->first] = it->second.coin;
#      63 [ +  + ][ +  + ]:     156388 :                 if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
#      64                 :            :                     // Randomly delete empty entries on write.
#      65                 :      23182 :                     map_.erase(it->first);
#      66                 :      23182 :                 }
#      67                 :     156388 :             }
#      68                 :     612428 :             mapCoins.erase(it++);
#      69                 :     612428 :         }
#      70         [ -  + ]:        518 :         if (!hashBlock.IsNull())
#      71                 :          0 :             hashBestBlock_ = hashBlock;
#      72                 :        518 :         return true;
#      73                 :        518 :     }
#      74                 :            : };
#      75                 :            : 
#      76                 :            : class CCoinsViewCacheTest : public CCoinsViewCache
#      77                 :            : {
#      78                 :            : public:
#      79                 :       1956 :     explicit CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {}
#      80                 :            : 
#      81                 :            :     void SelfTest() const
#      82                 :        734 :     {
#      83                 :            :         // Manually recompute the dynamic usage of the whole data, and compare it.
#      84                 :        734 :         size_t ret = memusage::DynamicUsage(cacheCoins);
#      85                 :        734 :         size_t count = 0;
#      86         [ +  + ]:     953404 :         for (const auto& entry : cacheCoins) {
#      87                 :     953404 :             ret += entry.second.coin.DynamicMemoryUsage();
#      88                 :     953404 :             ++count;
#      89                 :     953404 :         }
#      90                 :        734 :         BOOST_CHECK_EQUAL(GetCacheSize(), count);
#      91                 :        734 :         BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
#      92                 :        734 :     }
#      93                 :            : 
#      94                 :        752 :     CCoinsMap& map() const { return cacheCoins; }
#      95                 :        396 :     size_t& usage() const { return cachedCoinsUsage; }
#      96                 :            : };
#      97                 :            : 
#      98                 :            : } // namespace
#      99                 :            : 
#     100                 :            : BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
#     101                 :            : 
#     102                 :            : static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
#     103                 :            : 
#     104                 :            : // This is a large randomized insert/remove simulation test on a variable-size
#     105                 :            : // stack of caches on top of CCoinsViewTest.
#     106                 :            : //
#     107                 :            : // It will randomly create/update/delete Coin entries to a tip of caches, with
#     108                 :            : // txids picked from a limited list of random 256-bit hashes. Occasionally, a
#     109                 :            : // new tip is added to the stack of caches, or the tip is flushed and removed.
#     110                 :            : //
#     111                 :            : // During the process, booleans are kept to make sure that the randomized
#     112                 :            : // operation hits all branches.
#     113                 :            : //
#     114                 :            : // If fake_best_block is true, assign a random uint256 to mock the recording
#     115                 :            : // of best block on flush. This is necessary when using CCoinsViewDB as the base,
#     116                 :            : // otherwise we'll hit an assertion in BatchWrite.
#     117                 :            : //
#     118                 :            : void SimulationTest(CCoinsView* base, bool fake_best_block)
#     119                 :          4 : {
#     120                 :            :     // Various coverage trackers.
#     121                 :          4 :     bool removed_all_caches = false;
#     122                 :          4 :     bool reached_4_caches = false;
#     123                 :          4 :     bool added_an_entry = false;
#     124                 :          4 :     bool added_an_unspendable_entry = false;
#     125                 :          4 :     bool removed_an_entry = false;
#     126                 :          4 :     bool updated_an_entry = false;
#     127                 :          4 :     bool found_an_entry = false;
#     128                 :          4 :     bool missed_an_entry = false;
#     129                 :          4 :     bool uncached_an_entry = false;
#     130                 :            : 
#     131                 :            :     // A simple map to track what we expect the cache stack to represent.
#     132                 :          4 :     std::map<COutPoint, Coin> result;
#     133                 :            : 
#     134                 :            :     // The cache stack.
#     135                 :          4 :     std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
#     136                 :          4 :     stack.push_back(new CCoinsViewCacheTest(base)); // Start with one cache.
#     137                 :            : 
#     138                 :            :     // Use a limited set of random transaction ids, so we do test overwriting entries.
#     139                 :          4 :     std::vector<uint256> txids;
#     140                 :          4 :     txids.resize(NUM_SIMULATION_ITERATIONS / 8);
#     141         [ +  + ]:      20004 :     for (unsigned int i = 0; i < txids.size(); i++) {
#     142                 :      20000 :         txids[i] = InsecureRand256();
#     143                 :      20000 :     }
#     144                 :            : 
#     145         [ +  + ]:     160004 :     for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
#     146                 :            :         // Do a random modification.
#     147                 :     160000 :         {
#     148                 :     160000 :             uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration.
#     149                 :     160000 :             Coin& coin = result[COutPoint(txid, 0)];
#     150                 :            : 
#     151                 :            :             // Determine whether to test HaveCoin before or after Access* (or both). As these functions
#     152                 :            :             // can influence each other's behaviour by pulling things into the cache, all combinations
#     153                 :            :             // are tested.
#     154                 :     160000 :             bool test_havecoin_before = InsecureRandBits(2) == 0;
#     155                 :     160000 :             bool test_havecoin_after = InsecureRandBits(2) == 0;
#     156                 :            : 
#     157         [ +  + ]:     160000 :             bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false;
#     158         [ +  + ]:     160000 :             const Coin& entry = (InsecureRandRange(500) == 0) ? AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0));
#     159                 :     160000 :             BOOST_CHECK(coin == entry);
#     160                 :     160000 :             BOOST_CHECK(!test_havecoin_before || result_havecoin == !entry.IsSpent());
#     161                 :            : 
#     162         [ +  + ]:     160000 :             if (test_havecoin_after) {
#     163                 :      40218 :                 bool ret = stack.back()->HaveCoin(COutPoint(txid, 0));
#     164                 :      40218 :                 BOOST_CHECK(ret == !entry.IsSpent());
#     165                 :      40218 :             }
#     166                 :            : 
#     167 [ +  + ][ +  + ]:     160000 :             if (InsecureRandRange(5) == 0 || coin.IsSpent()) {
#     168                 :      95900 :                 Coin newcoin;
#     169                 :      95900 :                 newcoin.out.nValue = InsecureRand32();
#     170                 :      95900 :                 newcoin.nHeight = 1;
#     171 [ +  + ][ +  + ]:      95900 :                 if (InsecureRandRange(16) == 0 && coin.IsSpent()) {
#     172                 :       5066 :                     newcoin.out.scriptPubKey.assign(1 + InsecureRandBits(6), OP_RETURN);
#     173                 :       5066 :                     BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable());
#     174                 :       5066 :                     added_an_unspendable_entry = true;
#     175                 :      90834 :                 } else {
#     176                 :      90834 :                     newcoin.out.scriptPubKey.assign(InsecureRandBits(6), 0); // Random sizes so we can test memory usage accounting
#     177         [ +  + ]:      90834 :                     (coin.IsSpent() ? added_an_entry : updated_an_entry) = true;
#     178                 :      90834 :                     coin = newcoin;
#     179                 :      90834 :                 }
#     180 [ +  + ][ +  + ]:      95900 :                 stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), !coin.IsSpent() || InsecureRand32() & 1);
#     181                 :      95900 :             } else {
#     182                 :      64100 :                 removed_an_entry = true;
#     183                 :      64100 :                 coin.Clear();
#     184                 :      64100 :                 BOOST_CHECK(stack.back()->SpendCoin(COutPoint(txid, 0)));
#     185                 :      64100 :             }
#     186                 :     160000 :         }
#     187                 :            : 
#     188                 :            :         // One every 10 iterations, remove a random entry from the cache
#     189         [ +  + ]:     160000 :         if (InsecureRandRange(10) == 0) {
#     190                 :      16248 :             COutPoint out(txids[InsecureRand32() % txids.size()], 0);
#     191                 :      16248 :             int cacheid = InsecureRand32() % stack.size();
#     192                 :      16248 :             stack[cacheid]->Uncache(out);
#     193                 :      16248 :             uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
#     194                 :      16248 :         }
#     195                 :            : 
#     196                 :            :         // Once every 1000 iterations and at the end, verify the full cache.
#     197 [ +  + ][ +  + ]:     160000 :         if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
#     198         [ +  + ]:     672782 :             for (const auto& entry : result) {
#     199                 :     672782 :                 bool have = stack.back()->HaveCoin(entry.first);
#     200                 :     672782 :                 const Coin& coin = stack.back()->AccessCoin(entry.first);
#     201                 :     672782 :                 BOOST_CHECK(have == !coin.IsSpent());
#     202                 :     672782 :                 BOOST_CHECK(coin == entry.second);
#     203         [ +  + ]:     672782 :                 if (coin.IsSpent()) {
#     204                 :     287562 :                     missed_an_entry = true;
#     205                 :     385220 :                 } else {
#     206                 :     385220 :                     BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
#     207                 :     385220 :                     found_an_entry = true;
#     208                 :     385220 :                 }
#     209                 :     672782 :             }
#     210         [ +  + ]:        378 :             for (const CCoinsViewCacheTest *test : stack) {
#     211                 :        378 :                 test->SelfTest();
#     212                 :        378 :             }
#     213                 :        156 :         }
#     214                 :            : 
#     215         [ +  + ]:     160000 :         if (InsecureRandRange(100) == 0) {
#     216                 :            :             // Every 100 iterations, flush an intermediate cache
#     217 [ +  + ][ +  + ]:       1654 :             if (stack.size() > 1 && InsecureRandBool() == 0) {
#     218                 :        626 :                 unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
#     219         [ +  + ]:        626 :                 if (fake_best_block) stack[flushIndex]->SetBestBlock(InsecureRand256());
#     220                 :        626 :                 BOOST_CHECK(stack[flushIndex]->Flush());
#     221                 :        626 :             }
#     222                 :       1654 :         }
#     223         [ +  + ]:     160000 :         if (InsecureRandRange(100) == 0) {
#     224                 :            :             // Every 100 iterations, change the cache stack.
#     225 [ +  - ][ +  + ]:       1582 :             if (stack.size() > 0 && InsecureRandBool() == 0) {
#     226                 :            :                 //Remove the top cache
#     227         [ +  + ]:        782 :                 if (fake_best_block) stack.back()->SetBestBlock(InsecureRand256());
#     228                 :        782 :                 BOOST_CHECK(stack.back()->Flush());
#     229                 :        782 :                 delete stack.back();
#     230                 :        782 :                 stack.pop_back();
#     231                 :        782 :             }
#     232 [ +  + ][ +  + ]:       1582 :             if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
#                 [ +  + ]
#     233                 :            :                 //Add a new cache
#     234                 :        788 :                 CCoinsView* tip = base;
#     235         [ +  + ]:        788 :                 if (stack.size() > 0) {
#     236                 :        596 :                     tip = stack.back();
#     237                 :        596 :                 } else {
#     238                 :        192 :                     removed_all_caches = true;
#     239                 :        192 :                 }
#     240                 :        788 :                 stack.push_back(new CCoinsViewCacheTest(tip));
#     241         [ +  + ]:        788 :                 if (stack.size() == 4) {
#     242                 :        192 :                     reached_4_caches = true;
#     243                 :        192 :                 }
#     244                 :        788 :             }
#     245                 :       1582 :         }
#     246                 :     160000 :     }
#     247                 :            : 
#     248                 :            :     // Clean up the stack.
#     249         [ +  + ]:         14 :     while (stack.size() > 0) {
#     250                 :         10 :         delete stack.back();
#     251                 :         10 :         stack.pop_back();
#     252                 :         10 :     }
#     253                 :            : 
#     254                 :            :     // Verify coverage.
#     255                 :          4 :     BOOST_CHECK(removed_all_caches);
#     256                 :          4 :     BOOST_CHECK(reached_4_caches);
#     257                 :          4 :     BOOST_CHECK(added_an_entry);
#     258                 :          4 :     BOOST_CHECK(added_an_unspendable_entry);
#     259                 :          4 :     BOOST_CHECK(removed_an_entry);
#     260                 :          4 :     BOOST_CHECK(updated_an_entry);
#     261                 :          4 :     BOOST_CHECK(found_an_entry);
#     262                 :          4 :     BOOST_CHECK(missed_an_entry);
#     263                 :          4 :     BOOST_CHECK(uncached_an_entry);
#     264                 :          4 : }
#     265                 :            : 
#     266                 :            : // Run the above simulation for multiple base types.
#     267                 :            : BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
#     268                 :          2 : {
#     269                 :          2 :     CCoinsViewTest base;
#     270                 :          2 :     SimulationTest(&base, false);
#     271                 :            : 
#     272                 :          2 :     CCoinsViewDB db_base{"test", /*nCacheSize=*/1 << 23, /*fMemory=*/true, /*fWipe=*/false};
#     273                 :          2 :     SimulationTest(&db_base, true);
#     274                 :          2 : }
#     275                 :            : 
#     276                 :            : // Store of all necessary tx and undo data for next test
#     277                 :            : typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData;
#     278                 :            : UtxoData utxoData;
#     279                 :            : 
#     280                 :      80444 : UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
#     281                 :      80444 :     assert(utxoSet.size());
#     282                 :          0 :     auto utxoSetIt = utxoSet.lower_bound(COutPoint(InsecureRand256(), 0));
#     283         [ +  + ]:      80444 :     if (utxoSetIt == utxoSet.end()) {
#     284                 :       1166 :         utxoSetIt = utxoSet.begin();
#     285                 :       1166 :     }
#     286                 :      80444 :     auto utxoDataIt = utxoData.find(*utxoSetIt);
#     287                 :      80444 :     assert(utxoDataIt != utxoData.end());
#     288                 :          0 :     return utxoDataIt;
#     289                 :      80444 : }
#     290                 :            : 
#     291                 :            : 
#     292                 :            : // This test is similar to the previous test
#     293                 :            : // except the emphasis is on testing the functionality of UpdateCoins
#     294                 :            : // random txs are created and UpdateCoins is used to update the cache stack
#     295                 :            : // In particular it is tested that spending a duplicate coinbase tx
#     296                 :            : // has the expected effect (the other duplicate is overwritten at all cache levels)
#     297                 :            : BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
#     298                 :          2 : {
#     299                 :          2 :     SeedInsecureRand(SeedRand::ZEROS);
#     300                 :          2 :     g_mock_deterministic_tests = true;
#     301                 :            : 
#     302                 :          2 :     bool spent_a_duplicate_coinbase = false;
#     303                 :            :     // A simple map to track what we expect the cache stack to represent.
#     304                 :          2 :     std::map<COutPoint, Coin> result;
#     305                 :            : 
#     306                 :            :     // The cache stack.
#     307                 :          2 :     CCoinsViewTest base; // A CCoinsViewTest at the bottom.
#     308                 :          2 :     std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
#     309                 :          2 :     stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
#     310                 :            : 
#     311                 :            :     // Track the txids we've used in various sets
#     312                 :          2 :     std::set<COutPoint> coinbase_coins;
#     313                 :          2 :     std::set<COutPoint> disconnected_coins;
#     314                 :          2 :     std::set<COutPoint> duplicate_coins;
#     315                 :          2 :     std::set<COutPoint> utxoset;
#     316                 :            : 
#     317         [ +  + ]:      80002 :     for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
#     318                 :      80000 :         uint32_t randiter = InsecureRand32();
#     319                 :            : 
#     320                 :            :         // 19/20 txs add a new transaction
#     321         [ +  + ]:      80000 :         if (randiter % 20 < 19) {
#     322                 :      76046 :             CMutableTransaction tx;
#     323                 :      76046 :             tx.vin.resize(1);
#     324                 :      76046 :             tx.vout.resize(1);
#     325                 :      76046 :             tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
#     326                 :      76046 :             tx.vout[0].scriptPubKey.assign(InsecureRand32() & 0x3F, 0); // Random sizes so we can test memory usage accounting
#     327                 :      76046 :             const int height{int(InsecureRand32() >> 1)};
#     328                 :      76046 :             Coin old_coin;
#     329                 :            : 
#     330                 :            :             // 2/20 times create a new coinbase
#     331 [ +  + ][ +  + ]:      76046 :             if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
#     332                 :            :                 // 1/10 of those times create a duplicate coinbase
#     333 [ +  + ][ +  - ]:       8090 :                 if (InsecureRandRange(10) == 0 && coinbase_coins.size()) {
#     334                 :        782 :                     auto utxod = FindRandomFrom(coinbase_coins);
#     335                 :            :                     // Reuse the exact same coinbase
#     336                 :        782 :                     tx = CMutableTransaction{std::get<0>(utxod->second)};
#     337                 :            :                     // shouldn't be available for reconnection if it's been duplicated
#     338                 :        782 :                     disconnected_coins.erase(utxod->first);
#     339                 :            : 
#     340                 :        782 :                     duplicate_coins.insert(utxod->first);
#     341                 :        782 :                 }
#     342                 :       7308 :                 else {
#     343                 :       7308 :                     coinbase_coins.insert(COutPoint(tx.GetHash(), 0));
#     344                 :       7308 :                 }
#     345                 :       8090 :                 assert(CTransaction(tx).IsCoinBase());
#     346                 :       8090 :             }
#     347                 :            : 
#     348                 :            :             // 17/20 times reconnect previous or add a regular tx
#     349                 :      67956 :             else {
#     350                 :            : 
#     351                 :      67956 :                 COutPoint prevout;
#     352                 :            :                 // 1/20 times reconnect a previously disconnected tx
#     353 [ +  + ][ +  + ]:      67956 :                 if (randiter % 20 == 2 && disconnected_coins.size()) {
#     354                 :       3918 :                     auto utxod = FindRandomFrom(disconnected_coins);
#     355                 :       3918 :                     tx = CMutableTransaction{std::get<0>(utxod->second)};
#     356                 :       3918 :                     prevout = tx.vin[0].prevout;
#     357 [ +  + ][ +  + ]:       3918 :                     if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
#                 [ +  + ]
#     358                 :        676 :                         disconnected_coins.erase(utxod->first);
#     359                 :        676 :                         continue;
#     360                 :        676 :                     }
#     361                 :            : 
#     362                 :            :                     // If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
#     363         [ -  + ]:       3242 :                     if (utxoset.count(utxod->first)) {
#     364                 :          0 :                         assert(CTransaction(tx).IsCoinBase());
#     365                 :          0 :                         assert(duplicate_coins.count(utxod->first));
#     366                 :          0 :                     }
#     367                 :          0 :                     disconnected_coins.erase(utxod->first);
#     368                 :       3242 :                 }
#     369                 :            : 
#     370                 :            :                 // 16/20 times create a regular tx
#     371                 :      64038 :                 else {
#     372                 :      64038 :                     auto utxod = FindRandomFrom(utxoset);
#     373                 :      64038 :                     prevout = utxod->first;
#     374                 :            : 
#     375                 :            :                     // Construct the tx to spend the coins of prevouthash
#     376                 :      64038 :                     tx.vin[0].prevout = prevout;
#     377                 :      64038 :                     assert(!CTransaction(tx).IsCoinBase());
#     378                 :      64038 :                 }
#     379                 :            :                 // In this simple test coins only have two states, spent or unspent, save the unspent state to restore
#     380                 :      67280 :                 old_coin = result[prevout];
#     381                 :            :                 // Update the expected result of prevouthash to know these coins are spent
#     382                 :      67280 :                 result[prevout].Clear();
#     383                 :            : 
#     384                 :      67280 :                 utxoset.erase(prevout);
#     385                 :            : 
#     386                 :            :                 // The test is designed to ensure spending a duplicate coinbase will work properly
#     387                 :            :                 // if that ever happens and not resurrect the previously overwritten coinbase
#     388         [ +  + ]:      67280 :                 if (duplicate_coins.count(prevout)) {
#     389                 :        696 :                     spent_a_duplicate_coinbase = true;
#     390                 :        696 :                 }
#     391                 :            : 
#     392                 :      67280 :             }
#     393                 :            :             // Update the expected result to know about the new output coins
#     394                 :      75370 :             assert(tx.vout.size() == 1);
#     395                 :          0 :             const COutPoint outpoint(tx.GetHash(), 0);
#     396                 :      75370 :             result[outpoint] = Coin{tx.vout[0], height, CTransaction{tx}.IsCoinBase()};
#     397                 :            : 
#     398                 :            :             // Call UpdateCoins on the top cache
#     399                 :      75370 :             CTxUndo undo;
#     400                 :      75370 :             UpdateCoins(CTransaction{tx}, *(stack.back()), undo, height);
#     401                 :            : 
#     402                 :            :             // Update the utxo set for future spends
#     403                 :      75370 :             utxoset.insert(outpoint);
#     404                 :            : 
#     405                 :            :             // Track this tx and undo info to use later
#     406                 :      75370 :             utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
#     407         [ +  - ]:      75370 :         } else if (utxoset.size()) {
#     408                 :            :             //1/20 times undo a previous transaction
#     409                 :       3954 :             auto utxod = FindRandomFrom(utxoset);
#     410                 :            : 
#     411                 :       3954 :             CTransaction &tx = std::get<0>(utxod->second);
#     412                 :       3954 :             CTxUndo &undo = std::get<1>(utxod->second);
#     413                 :       3954 :             Coin &orig_coin = std::get<2>(utxod->second);
#     414                 :            : 
#     415                 :            :             // Update the expected result
#     416                 :            :             // Remove new outputs
#     417                 :       3954 :             result[utxod->first].Clear();
#     418                 :            :             // If not coinbase restore prevout
#     419         [ +  + ]:       3954 :             if (!tx.IsCoinBase()) {
#     420                 :       3500 :                 result[tx.vin[0].prevout] = orig_coin;
#     421                 :       3500 :             }
#     422                 :            : 
#     423                 :            :             // Disconnect the tx from the current UTXO
#     424                 :            :             // See code in DisconnectBlock
#     425                 :            :             // remove outputs
#     426                 :       3954 :             BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
#     427                 :            :             // restore inputs
#     428         [ +  + ]:       3954 :             if (!tx.IsCoinBase()) {
#     429                 :       3500 :                 const COutPoint &out = tx.vin[0].prevout;
#     430                 :       3500 :                 Coin coin = undo.vprevout[0];
#     431                 :       3500 :                 ApplyTxInUndo(std::move(coin), *(stack.back()), out);
#     432                 :       3500 :             }
#     433                 :            :             // Store as a candidate for reconnection
#     434                 :       3954 :             disconnected_coins.insert(utxod->first);
#     435                 :            : 
#     436                 :            :             // Update the utxoset
#     437                 :       3954 :             utxoset.erase(utxod->first);
#     438         [ +  + ]:       3954 :             if (!tx.IsCoinBase())
#     439                 :       3500 :                 utxoset.insert(tx.vin[0].prevout);
#     440                 :       3954 :         }
#     441                 :            : 
#     442                 :            :         // Once every 1000 iterations and at the end, verify the full cache.
#     443 [ +  + ][ +  + ]:      79324 :         if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
#     444         [ +  + ]:    1446378 :             for (const auto& entry : result) {
#     445                 :    1446378 :                 bool have = stack.back()->HaveCoin(entry.first);
#     446                 :    1446378 :                 const Coin& coin = stack.back()->AccessCoin(entry.first);
#     447                 :    1446378 :                 BOOST_CHECK(have == !coin.IsSpent());
#     448                 :    1446378 :                 BOOST_CHECK(coin == entry.second);
#     449                 :    1446378 :             }
#     450                 :         76 :         }
#     451                 :            : 
#     452                 :            :         // One every 10 iterations, remove a random entry from the cache
#     453 [ +  + ][ +  + ]:      79324 :         if (utxoset.size() > 1 && InsecureRandRange(30) == 0) {
#     454                 :       2688 :             stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first);
#     455                 :       2688 :         }
#     456 [ +  + ][ +  + ]:      79324 :         if (disconnected_coins.size() > 1 && InsecureRandRange(30) == 0) {
#     457                 :       2398 :             stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(disconnected_coins)->first);
#     458                 :       2398 :         }
#     459 [ +  + ][ +  + ]:      79324 :         if (duplicate_coins.size() > 1 && InsecureRandRange(30) == 0) {
#     460                 :       2666 :             stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(duplicate_coins)->first);
#     461                 :       2666 :         }
#     462                 :            : 
#     463         [ +  + ]:      79324 :         if (InsecureRandRange(100) == 0) {
#     464                 :            :             // Every 100 iterations, flush an intermediate cache
#     465 [ +  + ][ +  + ]:        850 :             if (stack.size() > 1 && InsecureRandBool() == 0) {
#     466                 :        374 :                 unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
#     467                 :        374 :                 BOOST_CHECK(stack[flushIndex]->Flush());
#     468                 :        374 :             }
#     469                 :        850 :         }
#     470         [ +  + ]:      79324 :         if (InsecureRandRange(100) == 0) {
#     471                 :            :             // Every 100 iterations, change the cache stack.
#     472 [ +  - ][ +  + ]:        798 :             if (stack.size() > 0 && InsecureRandBool() == 0) {
#     473                 :        364 :                 BOOST_CHECK(stack.back()->Flush());
#     474                 :        364 :                 delete stack.back();
#     475                 :        364 :                 stack.pop_back();
#     476                 :        364 :             }
#     477 [ +  + ][ +  + ]:        798 :             if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
#                 [ +  + ]
#     478                 :        370 :                 CCoinsView* tip = &base;
#     479         [ +  + ]:        370 :                 if (stack.size() > 0) {
#     480                 :        318 :                     tip = stack.back();
#     481                 :        318 :                 }
#     482                 :        370 :                 stack.push_back(new CCoinsViewCacheTest(tip));
#     483                 :        370 :             }
#     484                 :        798 :         }
#     485                 :      79324 :     }
#     486                 :            : 
#     487                 :            :     // Clean up the stack.
#     488         [ +  + ]:         10 :     while (stack.size() > 0) {
#     489                 :          8 :         delete stack.back();
#     490                 :          8 :         stack.pop_back();
#     491                 :          8 :     }
#     492                 :            : 
#     493                 :            :     // Verify coverage.
#     494                 :          2 :     BOOST_CHECK(spent_a_duplicate_coinbase);
#     495                 :            : 
#     496                 :          2 :     g_mock_deterministic_tests = false;
#     497                 :          2 : }
#     498                 :            : 
#     499                 :            : BOOST_AUTO_TEST_CASE(ccoins_serialization)
#     500                 :          2 : {
#     501                 :            :     // Good example
#     502                 :          2 :     CDataStream ss1(ParseHex("97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"), SER_DISK, CLIENT_VERSION);
#     503                 :          2 :     Coin cc1;
#     504                 :          2 :     ss1 >> cc1;
#     505                 :          2 :     BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
#     506                 :          2 :     BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
#     507                 :          2 :     BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
#     508                 :          2 :     BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
#     509                 :            : 
#     510                 :            :     // Good example
#     511                 :          2 :     CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
#     512                 :          2 :     Coin cc2;
#     513                 :          2 :     ss2 >> cc2;
#     514                 :          2 :     BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
#     515                 :          2 :     BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
#     516                 :          2 :     BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
#     517                 :          2 :     BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
#     518                 :            : 
#     519                 :            :     // Smallest possible example
#     520                 :          2 :     CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
#     521                 :          2 :     Coin cc3;
#     522                 :          2 :     ss3 >> cc3;
#     523                 :          2 :     BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
#     524                 :          2 :     BOOST_CHECK_EQUAL(cc3.nHeight, 0U);
#     525                 :          2 :     BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
#     526                 :          2 :     BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U);
#     527                 :            : 
#     528                 :            :     // scriptPubKey that ends beyond the end of the stream
#     529                 :          2 :     CDataStream ss4(ParseHex("000007"), SER_DISK, CLIENT_VERSION);
#     530                 :          2 :     try {
#     531                 :          2 :         Coin cc4;
#     532                 :          2 :         ss4 >> cc4;
#     533                 :          2 :         BOOST_CHECK_MESSAGE(false, "We should have thrown");
#     534                 :          2 :     } catch (const std::ios_base::failure&) {
#     535                 :          2 :     }
#     536                 :            : 
#     537                 :            :     // Very large scriptPubKey (3*10^9 bytes) past the end of the stream
#     538                 :          2 :     CDataStream tmp(SER_DISK, CLIENT_VERSION);
#     539                 :          2 :     uint64_t x = 3000000000ULL;
#     540                 :          2 :     tmp << VARINT(x);
#     541                 :          2 :     BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
#     542                 :          2 :     CDataStream ss5(ParseHex("00008a95c0bb00"), SER_DISK, CLIENT_VERSION);
#     543                 :          2 :     try {
#     544                 :          2 :         Coin cc5;
#     545                 :          2 :         ss5 >> cc5;
#     546                 :          2 :         BOOST_CHECK_MESSAGE(false, "We should have thrown");
#     547                 :          2 :     } catch (const std::ios_base::failure&) {
#     548                 :          2 :     }
#     549                 :          2 : }
#     550                 :            : 
#     551                 :            : const static COutPoint OUTPOINT;
#     552                 :            : const static CAmount SPENT = -1;
#     553                 :            : const static CAmount ABSENT = -2;
#     554                 :            : const static CAmount FAIL = -3;
#     555                 :            : const static CAmount VALUE1 = 100;
#     556                 :            : const static CAmount VALUE2 = 200;
#     557                 :            : const static CAmount VALUE3 = 300;
#     558                 :            : const static char DIRTY = CCoinsCacheEntry::DIRTY;
#     559                 :            : const static char FRESH = CCoinsCacheEntry::FRESH;
#     560                 :            : const static char NO_ENTRY = -1;
#     561                 :            : 
#     562                 :            : const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
#     563                 :            : const static auto CLEAN_FLAGS = {char(0), FRESH};
#     564                 :            : const static auto ABSENT_FLAGS = {NO_ENTRY};
#     565                 :            : 
#     566                 :            : static void SetCoinsValue(CAmount value, Coin& coin)
#     567                 :        640 : {
#     568                 :        640 :     assert(value != ABSENT);
#     569                 :          0 :     coin.Clear();
#     570                 :        640 :     assert(coin.IsSpent());
#     571         [ +  + ]:        640 :     if (value != SPENT) {
#     572                 :        320 :         coin.out.nValue = value;
#     573                 :        320 :         coin.nHeight = 1;
#     574                 :        320 :         assert(!coin.IsSpent());
#     575                 :        320 :     }
#     576                 :        640 : }
#     577                 :            : 
#     578                 :            : static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
#     579                 :        972 : {
#     580         [ +  + ]:        972 :     if (value == ABSENT) {
#     581                 :        332 :         assert(flags == NO_ENTRY);
#     582                 :          0 :         return 0;
#     583                 :        332 :     }
#     584                 :        640 :     assert(flags != NO_ENTRY);
#     585                 :          0 :     CCoinsCacheEntry entry;
#     586                 :        640 :     entry.flags = flags;
#     587                 :        640 :     SetCoinsValue(value, entry.coin);
#     588                 :        640 :     auto inserted = map.emplace(OUTPOINT, std::move(entry));
#     589                 :        640 :     assert(inserted.second);
#     590                 :          0 :     return inserted.first->second.coin.DynamicMemoryUsage();
#     591                 :        972 : }
#     592                 :            : 
#     593                 :            : void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
#     594                 :        356 : {
#     595                 :        356 :     auto it = map.find(OUTPOINT);
#     596         [ +  + ]:        356 :     if (it == map.end()) {
#     597                 :         58 :         value = ABSENT;
#     598                 :         58 :         flags = NO_ENTRY;
#     599                 :        298 :     } else {
#     600         [ +  + ]:        298 :         if (it->second.coin.IsSpent()) {
#     601                 :        112 :             value = SPENT;
#     602                 :        186 :         } else {
#     603                 :        186 :             value = it->second.coin.out.nValue;
#     604                 :        186 :         }
#     605                 :        298 :         flags = it->second.flags;
#     606                 :        298 :         assert(flags != NO_ENTRY);
#     607                 :        298 :     }
#     608                 :        356 : }
#     609                 :            : 
#     610                 :            : void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
#     611                 :        576 : {
#     612                 :        576 :     CCoinsMap map;
#     613                 :        576 :     InsertCoinsMapEntry(map, value, flags);
#     614                 :        576 :     BOOST_CHECK(view.BatchWrite(map, {}));
#     615                 :        576 : }
#     616                 :            : 
#     617                 :            : class SingleEntryCacheTest
#     618                 :            : {
#     619                 :            : public:
#     620                 :            :     SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
#     621                 :        396 :     {
#     622         [ +  + ]:        396 :         WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
#     623                 :        396 :         cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
#     624                 :        396 :     }
#     625                 :            : 
#     626                 :            :     CCoinsView root;
#     627                 :            :     CCoinsViewCacheTest base{&root};
#     628                 :            :     CCoinsViewCacheTest cache{&base};
#     629                 :            : };
#     630                 :            : 
#     631                 :            : static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
#     632                 :         54 : {
#     633                 :         54 :     SingleEntryCacheTest test(base_value, cache_value, cache_flags);
#     634                 :         54 :     test.cache.AccessCoin(OUTPOINT);
#     635                 :         54 :     test.cache.SelfTest();
#     636                 :            : 
#     637                 :         54 :     CAmount result_value;
#     638                 :         54 :     char result_flags;
#     639                 :         54 :     GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
#     640                 :         54 :     BOOST_CHECK_EQUAL(result_value, expected_value);
#     641                 :         54 :     BOOST_CHECK_EQUAL(result_flags, expected_flags);
#     642                 :         54 : }
#     643                 :            : 
#     644                 :            : BOOST_AUTO_TEST_CASE(ccoins_access)
#     645                 :          2 : {
#     646                 :            :     /* Check AccessCoin behavior, requesting a coin from a cache view layered on
#     647                 :            :      * top of a base view, and checking the resulting entry in the cache after
#     648                 :            :      * the access.
#     649                 :            :      *
#     650                 :            :      *               Base    Cache   Result  Cache        Result
#     651                 :            :      *               Value   Value   Value   Flags        Flags
#     652                 :            :      */
#     653                 :          2 :     CheckAccessCoin(ABSENT, ABSENT, ABSENT, NO_ENTRY   , NO_ENTRY   );
#     654                 :          2 :     CheckAccessCoin(ABSENT, SPENT , SPENT , 0          , 0          );
#     655                 :          2 :     CheckAccessCoin(ABSENT, SPENT , SPENT , FRESH      , FRESH      );
#     656                 :          2 :     CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY      , DIRTY      );
#     657                 :          2 :     CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
#     658                 :          2 :     CheckAccessCoin(ABSENT, VALUE2, VALUE2, 0          , 0          );
#     659                 :          2 :     CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH      , FRESH      );
#     660                 :          2 :     CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY      , DIRTY      );
#     661                 :          2 :     CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
#     662                 :          2 :     CheckAccessCoin(SPENT , ABSENT, ABSENT, NO_ENTRY   , NO_ENTRY   );
#     663                 :          2 :     CheckAccessCoin(SPENT , SPENT , SPENT , 0          , 0          );
#     664                 :          2 :     CheckAccessCoin(SPENT , SPENT , SPENT , FRESH      , FRESH      );
#     665                 :          2 :     CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY      , DIRTY      );
#     666                 :          2 :     CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
#     667                 :          2 :     CheckAccessCoin(SPENT , VALUE2, VALUE2, 0          , 0          );
#     668                 :          2 :     CheckAccessCoin(SPENT , VALUE2, VALUE2, FRESH      , FRESH      );
#     669                 :          2 :     CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY      , DIRTY      );
#     670                 :          2 :     CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
#     671                 :          2 :     CheckAccessCoin(VALUE1, ABSENT, VALUE1, NO_ENTRY   , 0          );
#     672                 :          2 :     CheckAccessCoin(VALUE1, SPENT , SPENT , 0          , 0          );
#     673                 :          2 :     CheckAccessCoin(VALUE1, SPENT , SPENT , FRESH      , FRESH      );
#     674                 :          2 :     CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY      , DIRTY      );
#     675                 :          2 :     CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
#     676                 :          2 :     CheckAccessCoin(VALUE1, VALUE2, VALUE2, 0          , 0          );
#     677                 :          2 :     CheckAccessCoin(VALUE1, VALUE2, VALUE2, FRESH      , FRESH      );
#     678                 :          2 :     CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY      , DIRTY      );
#     679                 :          2 :     CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
#     680                 :          2 : }
#     681                 :            : 
#     682                 :            : static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
#     683                 :         54 : {
#     684                 :         54 :     SingleEntryCacheTest test(base_value, cache_value, cache_flags);
#     685                 :         54 :     test.cache.SpendCoin(OUTPOINT);
#     686                 :         54 :     test.cache.SelfTest();
#     687                 :            : 
#     688                 :         54 :     CAmount result_value;
#     689                 :         54 :     char result_flags;
#     690                 :         54 :     GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
#     691                 :         54 :     BOOST_CHECK_EQUAL(result_value, expected_value);
#     692                 :         54 :     BOOST_CHECK_EQUAL(result_flags, expected_flags);
#     693                 :         54 : };
#     694                 :            : 
#     695                 :            : BOOST_AUTO_TEST_CASE(ccoins_spend)
#     696                 :          2 : {
#     697                 :            :     /* Check SpendCoin behavior, requesting a coin from a cache view layered on
#     698                 :            :      * top of a base view, spending, and then checking
#     699                 :            :      * the resulting entry in the cache after the modification.
#     700                 :            :      *
#     701                 :            :      *              Base    Cache   Result  Cache        Result
#     702                 :            :      *              Value   Value   Value   Flags        Flags
#     703                 :            :      */
#     704                 :          2 :     CheckSpendCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY   , NO_ENTRY   );
#     705                 :          2 :     CheckSpendCoins(ABSENT, SPENT , SPENT , 0          , DIRTY      );
#     706                 :          2 :     CheckSpendCoins(ABSENT, SPENT , ABSENT, FRESH      , NO_ENTRY   );
#     707                 :          2 :     CheckSpendCoins(ABSENT, SPENT , SPENT , DIRTY      , DIRTY      );
#     708                 :          2 :     CheckSpendCoins(ABSENT, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     709                 :          2 :     CheckSpendCoins(ABSENT, VALUE2, SPENT , 0          , DIRTY      );
#     710                 :          2 :     CheckSpendCoins(ABSENT, VALUE2, ABSENT, FRESH      , NO_ENTRY   );
#     711                 :          2 :     CheckSpendCoins(ABSENT, VALUE2, SPENT , DIRTY      , DIRTY      );
#     712                 :          2 :     CheckSpendCoins(ABSENT, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     713                 :          2 :     CheckSpendCoins(SPENT , ABSENT, ABSENT, NO_ENTRY   , NO_ENTRY   );
#     714                 :          2 :     CheckSpendCoins(SPENT , SPENT , SPENT , 0          , DIRTY      );
#     715                 :          2 :     CheckSpendCoins(SPENT , SPENT , ABSENT, FRESH      , NO_ENTRY   );
#     716                 :          2 :     CheckSpendCoins(SPENT , SPENT , SPENT , DIRTY      , DIRTY      );
#     717                 :          2 :     CheckSpendCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     718                 :          2 :     CheckSpendCoins(SPENT , VALUE2, SPENT , 0          , DIRTY      );
#     719                 :          2 :     CheckSpendCoins(SPENT , VALUE2, ABSENT, FRESH      , NO_ENTRY   );
#     720                 :          2 :     CheckSpendCoins(SPENT , VALUE2, SPENT , DIRTY      , DIRTY      );
#     721                 :          2 :     CheckSpendCoins(SPENT , VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     722                 :          2 :     CheckSpendCoins(VALUE1, ABSENT, SPENT , NO_ENTRY   , DIRTY      );
#     723                 :          2 :     CheckSpendCoins(VALUE1, SPENT , SPENT , 0          , DIRTY      );
#     724                 :          2 :     CheckSpendCoins(VALUE1, SPENT , ABSENT, FRESH      , NO_ENTRY   );
#     725                 :          2 :     CheckSpendCoins(VALUE1, SPENT , SPENT , DIRTY      , DIRTY      );
#     726                 :          2 :     CheckSpendCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     727                 :          2 :     CheckSpendCoins(VALUE1, VALUE2, SPENT , 0          , DIRTY      );
#     728                 :          2 :     CheckSpendCoins(VALUE1, VALUE2, ABSENT, FRESH      , NO_ENTRY   );
#     729                 :          2 :     CheckSpendCoins(VALUE1, VALUE2, SPENT , DIRTY      , DIRTY      );
#     730                 :          2 :     CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY   );
#     731                 :          2 : }
#     732                 :            : 
#     733                 :            : static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
#     734                 :        108 : {
#     735                 :        108 :     SingleEntryCacheTest test(base_value, cache_value, cache_flags);
#     736                 :            : 
#     737                 :        108 :     CAmount result_value;
#     738                 :        108 :     char result_flags;
#     739                 :        108 :     try {
#     740                 :        108 :         CTxOut output;
#     741                 :        108 :         output.nValue = modify_value;
#     742                 :        108 :         test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase);
#     743                 :        108 :         test.cache.SelfTest();
#     744                 :        108 :         GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
#     745                 :        108 :     } catch (std::logic_error&) {
#     746                 :         24 :         result_value = FAIL;
#     747                 :         24 :         result_flags = NO_ENTRY;
#     748                 :         24 :     }
#     749                 :            : 
#     750                 :        108 :     BOOST_CHECK_EQUAL(result_value, expected_value);
#     751                 :        108 :     BOOST_CHECK_EQUAL(result_flags, expected_flags);
#     752                 :        108 : }
#     753                 :            : 
#     754                 :            : // Simple wrapper for CheckAddCoinBase function above that loops through
#     755                 :            : // different possible base_values, making sure each one gives the same results.
#     756                 :            : // This wrapper lets the coins_add test below be shorter and less repetitive,
#     757                 :            : // while still verifying that the CoinsViewCache::AddCoin implementation
#     758                 :            : // ignores base values.
#     759                 :            : template <typename... Args>
#     760                 :            : static void CheckAddCoin(Args&&... args)
#     761                 :         36 : {
#     762 [ +  + ][ +  + ]:         36 :     for (const CAmount base_value : {ABSENT, SPENT, VALUE1})
#         [ +  + ][ +  + ]
#     763                 :        108 :         CheckAddCoinBase(base_value, std::forward<Args>(args)...);
#     764                 :         36 : }
#     765                 :            : 
#     766                 :            : BOOST_AUTO_TEST_CASE(ccoins_add)
#     767                 :          2 : {
#     768                 :            :     /* Check AddCoin behavior, requesting a new coin from a cache view,
#     769                 :            :      * writing a modification to the coin, and then checking the resulting
#     770                 :            :      * entry in the cache after the modification. Verify behavior with the
#     771                 :            :      * AddCoin possible_overwrite argument set to false, and to true.
#     772                 :            :      *
#     773                 :            :      *           Cache   Write   Result  Cache        Result       possible_overwrite
#     774                 :            :      *           Value   Value   Value   Flags        Flags
#     775                 :            :      */
#     776                 :          2 :     CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY   , DIRTY|FRESH, false);
#     777                 :          2 :     CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY   , DIRTY      , true );
#     778                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, 0          , DIRTY|FRESH, false);
#     779                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, 0          , DIRTY      , true );
#     780                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH      , DIRTY|FRESH, false);
#     781                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH      , DIRTY|FRESH, true );
#     782                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY      , DIRTY      , false);
#     783                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY      , DIRTY      , true );
#     784                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
#     785                 :          2 :     CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
#     786                 :          2 :     CheckAddCoin(VALUE2, VALUE3, FAIL  , 0          , NO_ENTRY   , false);
#     787                 :          2 :     CheckAddCoin(VALUE2, VALUE3, VALUE3, 0          , DIRTY      , true );
#     788                 :          2 :     CheckAddCoin(VALUE2, VALUE3, FAIL  , FRESH      , NO_ENTRY   , false);
#     789                 :          2 :     CheckAddCoin(VALUE2, VALUE3, VALUE3, FRESH      , DIRTY|FRESH, true );
#     790                 :          2 :     CheckAddCoin(VALUE2, VALUE3, FAIL  , DIRTY      , NO_ENTRY   , false);
#     791                 :          2 :     CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY      , DIRTY      , true );
#     792                 :          2 :     CheckAddCoin(VALUE2, VALUE3, FAIL  , DIRTY|FRESH, NO_ENTRY   , false);
#     793                 :          2 :     CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
#     794                 :          2 : }
#     795                 :            : 
#     796                 :            : void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
#     797                 :        180 : {
#     798                 :        180 :     SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
#     799                 :            : 
#     800                 :        180 :     CAmount result_value;
#     801                 :        180 :     char result_flags;
#     802                 :        180 :     try {
#     803                 :        180 :         WriteCoinsViewEntry(test.cache, child_value, child_flags);
#     804                 :        180 :         test.cache.SelfTest();
#     805                 :        180 :         GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
#     806                 :        180 :     } catch (std::logic_error&) {
#     807                 :         16 :         result_value = FAIL;
#     808                 :         16 :         result_flags = NO_ENTRY;
#     809                 :         16 :     }
#     810                 :            : 
#     811                 :        180 :     BOOST_CHECK_EQUAL(result_value, expected_value);
#     812                 :        180 :     BOOST_CHECK_EQUAL(result_flags, expected_flags);
#     813                 :        180 : }
#     814                 :            : 
#     815                 :            : BOOST_AUTO_TEST_CASE(ccoins_write)
#     816                 :          2 : {
#     817                 :            :     /* Check BatchWrite behavior, flushing one entry from a child cache to a
#     818                 :            :      * parent cache, and checking the resulting entry in the parent cache
#     819                 :            :      * after the write.
#     820                 :            :      *
#     821                 :            :      *              Parent  Child   Result  Parent       Child        Result
#     822                 :            :      *              Value   Value   Value   Flags        Flags        Flags
#     823                 :            :      */
#     824                 :          2 :     CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY   , NO_ENTRY   , NO_ENTRY   );
#     825                 :          2 :     CheckWriteCoins(ABSENT, SPENT , SPENT , NO_ENTRY   , DIRTY      , DIRTY      );
#     826                 :          2 :     CheckWriteCoins(ABSENT, SPENT , ABSENT, NO_ENTRY   , DIRTY|FRESH, NO_ENTRY   );
#     827                 :          2 :     CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY   , DIRTY      , DIRTY      );
#     828                 :          2 :     CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY   , DIRTY|FRESH, DIRTY|FRESH);
#     829                 :          2 :     CheckWriteCoins(SPENT , ABSENT, SPENT , 0          , NO_ENTRY   , 0          );
#     830                 :          2 :     CheckWriteCoins(SPENT , ABSENT, SPENT , FRESH      , NO_ENTRY   , FRESH      );
#     831                 :          2 :     CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY      , NO_ENTRY   , DIRTY      );
#     832                 :          2 :     CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY|FRESH, NO_ENTRY   , DIRTY|FRESH);
#     833                 :          2 :     CheckWriteCoins(SPENT , SPENT , SPENT , 0          , DIRTY      , DIRTY      );
#     834                 :          2 :     CheckWriteCoins(SPENT , SPENT , SPENT , 0          , DIRTY|FRESH, DIRTY      );
#     835                 :          2 :     CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH      , DIRTY      , NO_ENTRY   );
#     836                 :          2 :     CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH      , DIRTY|FRESH, NO_ENTRY   );
#     837                 :          2 :     CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY      , DIRTY      , DIRTY      );
#     838                 :          2 :     CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY      , DIRTY|FRESH, DIRTY      );
#     839                 :          2 :     CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY      , NO_ENTRY   );
#     840                 :          2 :     CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY   );
#     841                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, 0          , DIRTY      , DIRTY      );
#     842                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, 0          , DIRTY|FRESH, DIRTY      );
#     843                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH      , DIRTY      , DIRTY|FRESH);
#     844                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH      , DIRTY|FRESH, DIRTY|FRESH);
#     845                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY      , DIRTY      , DIRTY      );
#     846                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY      , DIRTY|FRESH, DIRTY      );
#     847                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY      , DIRTY|FRESH);
#     848                 :          2 :     CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
#     849                 :          2 :     CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0          , NO_ENTRY   , 0          );
#     850                 :          2 :     CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH      , NO_ENTRY   , FRESH      );
#     851                 :          2 :     CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY      , NO_ENTRY   , DIRTY      );
#     852                 :          2 :     CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY   , DIRTY|FRESH);
#     853                 :          2 :     CheckWriteCoins(VALUE1, SPENT , SPENT , 0          , DIRTY      , DIRTY      );
#     854                 :          2 :     CheckWriteCoins(VALUE1, SPENT , FAIL  , 0          , DIRTY|FRESH, NO_ENTRY   );
#     855                 :          2 :     CheckWriteCoins(VALUE1, SPENT , ABSENT, FRESH      , DIRTY      , NO_ENTRY   );
#     856                 :          2 :     CheckWriteCoins(VALUE1, SPENT , FAIL  , FRESH      , DIRTY|FRESH, NO_ENTRY   );
#     857                 :          2 :     CheckWriteCoins(VALUE1, SPENT , SPENT , DIRTY      , DIRTY      , DIRTY      );
#     858                 :          2 :     CheckWriteCoins(VALUE1, SPENT , FAIL  , DIRTY      , DIRTY|FRESH, NO_ENTRY   );
#     859                 :          2 :     CheckWriteCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, DIRTY      , NO_ENTRY   );
#     860                 :          2 :     CheckWriteCoins(VALUE1, SPENT , FAIL  , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY   );
#     861                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0          , DIRTY      , DIRTY      );
#     862                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, FAIL  , 0          , DIRTY|FRESH, NO_ENTRY   );
#     863                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH      , DIRTY      , DIRTY|FRESH);
#     864                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, FAIL  , FRESH      , DIRTY|FRESH, NO_ENTRY   );
#     865                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY      , DIRTY      , DIRTY      );
#     866                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, FAIL  , DIRTY      , DIRTY|FRESH, NO_ENTRY   );
#     867                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY      , DIRTY|FRESH);
#     868                 :          2 :     CheckWriteCoins(VALUE1, VALUE2, FAIL  , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY   );
#     869                 :            : 
#     870                 :            :     // The checks above omit cases where the child flags are not DIRTY, since
#     871                 :            :     // they would be too repetitive (the parent cache is never updated in these
#     872                 :            :     // cases). The loop below covers these cases and makes sure the parent cache
#     873                 :            :     // is always left unchanged.
#     874         [ +  + ]:          2 :     for (const CAmount parent_value : {ABSENT, SPENT, VALUE1})
#     875         [ +  + ]:          6 :         for (const CAmount child_value : {ABSENT, SPENT, VALUE2})
#     876 [ +  + ][ +  + ]:         18 :             for (const char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
#     877 [ +  + ][ +  + ]:         54 :                 for (const char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
#     878                 :         90 :                     CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
#     879                 :          2 : }
#     880                 :            : 
#     881                 :            : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a