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()
|