coins_tests.cpp
1 // Copyright (c) 2014-2022 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 <addresstype.h> 6 #include <clientversion.h> 7 #include <coins.h> 8 #include <streams.h> 9 #include <test/util/poolresourcetester.h> 10 #include <test/util/random.h> 11 #include <test/util/setup_common.h> 12 #include <txdb.h> 13 #include <uint256.h> 14 #include <undo.h> 15 #include <util/strencodings.h> 16 17 #include <map> 18 #include <vector> 19 20 #include <boost/test/unit_test.hpp> 21 22 int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out); 23 void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight); 24 25 namespace 26 { 27 //! equality test 28 bool operator==(const Coin &a, const Coin &b) { 29 // Empty Coin objects are always equal. 30 if (a.IsSpent() && b.IsSpent()) return true; 31 return a.fCoinBase == b.fCoinBase && 32 a.nHeight == b.nHeight && 33 a.out == b.out; 34 } 35 36 class CCoinsViewTest : public CCoinsView 37 { 38 uint256 hashBestBlock_; 39 std::map<COutPoint, Coin> map_; 40 41 public: 42 [[nodiscard]] bool GetCoin(const COutPoint& outpoint, Coin& coin) const override 43 { 44 std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint); 45 if (it == map_.end()) { 46 return false; 47 } 48 coin = it->second; 49 if (coin.IsSpent() && InsecureRandBool() == 0) { 50 // Randomly return false in case of an empty entry. 51 return false; 52 } 53 return true; 54 } 55 56 uint256 GetBestBlock() const override { return hashBestBlock_; } 57 58 bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool erase = true) override 59 { 60 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = erase ? mapCoins.erase(it) : std::next(it)) { 61 if (it->second.flags & CCoinsCacheEntry::DIRTY) { 62 // Same optimization used in CCoinsViewDB is to only write dirty entries. 63 map_[it->first] = it->second.coin; 64 if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) { 65 // Randomly delete empty entries on write. 66 map_.erase(it->first); 67 } 68 } 69 } 70 if (!hashBlock.IsNull()) 71 hashBestBlock_ = hashBlock; 72 return true; 73 } 74 }; 75 76 class CCoinsViewCacheTest : public CCoinsViewCache 77 { 78 public: 79 explicit CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {} 80 81 void SelfTest() const 82 { 83 // Manually recompute the dynamic usage of the whole data, and compare it. 84 size_t ret = memusage::DynamicUsage(cacheCoins); 85 size_t count = 0; 86 for (const auto& entry : cacheCoins) { 87 ret += entry.second.coin.DynamicMemoryUsage(); 88 ++count; 89 } 90 BOOST_CHECK_EQUAL(GetCacheSize(), count); 91 BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret); 92 } 93 94 CCoinsMap& map() const { return cacheCoins; } 95 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 { 120 // Various coverage trackers. 121 bool removed_all_caches = false; 122 bool reached_4_caches = false; 123 bool added_an_entry = false; 124 bool added_an_unspendable_entry = false; 125 bool removed_an_entry = false; 126 bool updated_an_entry = false; 127 bool found_an_entry = false; 128 bool missed_an_entry = false; 129 bool uncached_an_entry = false; 130 bool flushed_without_erase = false; 131 132 // A simple map to track what we expect the cache stack to represent. 133 std::map<COutPoint, Coin> result; 134 135 // The cache stack. 136 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top. 137 stack.push_back(std::make_unique<CCoinsViewCacheTest>(base)); // Start with one cache. 138 139 // Use a limited set of random transaction ids, so we do test overwriting entries. 140 std::vector<Txid> txids; 141 txids.resize(NUM_SIMULATION_ITERATIONS / 8); 142 for (unsigned int i = 0; i < txids.size(); i++) { 143 txids[i] = Txid::FromUint256(InsecureRand256()); 144 } 145 146 for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { 147 // Do a random modification. 148 { 149 auto txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration. 150 Coin& coin = result[COutPoint(txid, 0)]; 151 152 // Determine whether to test HaveCoin before or after Access* (or both). As these functions 153 // can influence each other's behaviour by pulling things into the cache, all combinations 154 // are tested. 155 bool test_havecoin_before = InsecureRandBits(2) == 0; 156 bool test_havecoin_after = InsecureRandBits(2) == 0; 157 158 bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false; 159 160 // Infrequently, test usage of AccessByTxid instead of AccessCoin - the 161 // former just delegates to the latter and returns the first unspent in a txn. 162 const Coin& entry = (InsecureRandRange(500) == 0) ? 163 AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0)); 164 BOOST_CHECK(coin == entry); 165 166 if (test_havecoin_before) { 167 BOOST_CHECK(result_havecoin == !entry.IsSpent()); 168 } 169 170 if (test_havecoin_after) { 171 bool ret = stack.back()->HaveCoin(COutPoint(txid, 0)); 172 BOOST_CHECK(ret == !entry.IsSpent()); 173 } 174 175 if (InsecureRandRange(5) == 0 || coin.IsSpent()) { 176 Coin newcoin; 177 newcoin.out.nValue = InsecureRandMoneyAmount(); 178 newcoin.nHeight = 1; 179 180 // Infrequently test adding unspendable coins. 181 if (InsecureRandRange(16) == 0 && coin.IsSpent()) { 182 newcoin.out.scriptPubKey.assign(1 + InsecureRandBits(6), OP_RETURN); 183 BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable()); 184 added_an_unspendable_entry = true; 185 } else { 186 // Random sizes so we can test memory usage accounting 187 newcoin.out.scriptPubKey.assign(InsecureRandBits(6), 0); 188 (coin.IsSpent() ? added_an_entry : updated_an_entry) = true; 189 coin = newcoin; 190 } 191 bool is_overwrite = !coin.IsSpent() || InsecureRand32() & 1; 192 stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), is_overwrite); 193 } else { 194 // Spend the coin. 195 removed_an_entry = true; 196 coin.Clear(); 197 BOOST_CHECK(stack.back()->SpendCoin(COutPoint(txid, 0))); 198 } 199 } 200 201 // Once every 10 iterations, remove a random entry from the cache 202 if (InsecureRandRange(10) == 0) { 203 COutPoint out(txids[InsecureRand32() % txids.size()], 0); 204 int cacheid = InsecureRand32() % stack.size(); 205 stack[cacheid]->Uncache(out); 206 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out); 207 } 208 209 // Once every 1000 iterations and at the end, verify the full cache. 210 if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { 211 for (const auto& entry : result) { 212 bool have = stack.back()->HaveCoin(entry.first); 213 const Coin& coin = stack.back()->AccessCoin(entry.first); 214 BOOST_CHECK(have == !coin.IsSpent()); 215 BOOST_CHECK(coin == entry.second); 216 if (coin.IsSpent()) { 217 missed_an_entry = true; 218 } else { 219 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first)); 220 found_an_entry = true; 221 } 222 } 223 for (const auto& test : stack) { 224 test->SelfTest(); 225 } 226 } 227 228 if (InsecureRandRange(100) == 0) { 229 // Every 100 iterations, flush an intermediate cache 230 if (stack.size() > 1 && InsecureRandBool() == 0) { 231 unsigned int flushIndex = InsecureRandRange(stack.size() - 1); 232 if (fake_best_block) stack[flushIndex]->SetBestBlock(InsecureRand256()); 233 bool should_erase = InsecureRandRange(4) < 3; 234 BOOST_CHECK(should_erase ? stack[flushIndex]->Flush() : stack[flushIndex]->Sync()); 235 flushed_without_erase |= !should_erase; 236 } 237 } 238 if (InsecureRandRange(100) == 0) { 239 // Every 100 iterations, change the cache stack. 240 if (stack.size() > 0 && InsecureRandBool() == 0) { 241 //Remove the top cache 242 if (fake_best_block) stack.back()->SetBestBlock(InsecureRand256()); 243 bool should_erase = InsecureRandRange(4) < 3; 244 BOOST_CHECK(should_erase ? stack.back()->Flush() : stack.back()->Sync()); 245 flushed_without_erase |= !should_erase; 246 stack.pop_back(); 247 } 248 if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { 249 //Add a new cache 250 CCoinsView* tip = base; 251 if (stack.size() > 0) { 252 tip = stack.back().get(); 253 } else { 254 removed_all_caches = true; 255 } 256 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip)); 257 if (stack.size() == 4) { 258 reached_4_caches = true; 259 } 260 } 261 } 262 } 263 264 // Verify coverage. 265 BOOST_CHECK(removed_all_caches); 266 BOOST_CHECK(reached_4_caches); 267 BOOST_CHECK(added_an_entry); 268 BOOST_CHECK(added_an_unspendable_entry); 269 BOOST_CHECK(removed_an_entry); 270 BOOST_CHECK(updated_an_entry); 271 BOOST_CHECK(found_an_entry); 272 BOOST_CHECK(missed_an_entry); 273 BOOST_CHECK(uncached_an_entry); 274 BOOST_CHECK(flushed_without_erase); 275 } 276 277 // Run the above simulation for multiple base types. 278 BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) 279 { 280 CCoinsViewTest base; 281 SimulationTest(&base, false); 282 283 CCoinsViewDB db_base{{.path = "test", .cache_bytes = 1 << 23, .memory_only = true}, {}}; 284 SimulationTest(&db_base, true); 285 } 286 287 // Store of all necessary tx and undo data for next test 288 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData; 289 UtxoData utxoData; 290 291 UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) { 292 assert(utxoSet.size()); 293 auto utxoSetIt = utxoSet.lower_bound(COutPoint(Txid::FromUint256(InsecureRand256()), 0)); 294 if (utxoSetIt == utxoSet.end()) { 295 utxoSetIt = utxoSet.begin(); 296 } 297 auto utxoDataIt = utxoData.find(*utxoSetIt); 298 assert(utxoDataIt != utxoData.end()); 299 return utxoDataIt; 300 } 301 302 303 // This test is similar to the previous test 304 // except the emphasis is on testing the functionality of UpdateCoins 305 // random txs are created and UpdateCoins is used to update the cache stack 306 // In particular it is tested that spending a duplicate coinbase tx 307 // has the expected effect (the other duplicate is overwritten at all cache levels) 308 BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) 309 { 310 SeedInsecureRand(SeedRand::ZEROS); 311 g_mock_deterministic_tests = true; 312 313 bool spent_a_duplicate_coinbase = false; 314 // A simple map to track what we expect the cache stack to represent. 315 std::map<COutPoint, Coin> result; 316 317 // The cache stack. 318 CCoinsViewTest base; // A CCoinsViewTest at the bottom. 319 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top. 320 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base)); // Start with one cache. 321 322 // Track the txids we've used in various sets 323 std::set<COutPoint> coinbase_coins; 324 std::set<COutPoint> disconnected_coins; 325 std::set<COutPoint> duplicate_coins; 326 std::set<COutPoint> utxoset; 327 328 for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { 329 uint32_t randiter = InsecureRand32(); 330 331 // 19/20 txs add a new transaction 332 if (randiter % 20 < 19) { 333 CMutableTransaction tx; 334 tx.vin.resize(1); 335 tx.vout.resize(1); 336 tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate 337 tx.vout[0].scriptPubKey.assign(InsecureRand32() & 0x3F, 0); // Random sizes so we can test memory usage accounting 338 const int height{int(InsecureRand32() >> 1)}; 339 Coin old_coin; 340 341 // 2/20 times create a new coinbase 342 if (randiter % 20 < 2 || coinbase_coins.size() < 10) { 343 // 1/10 of those times create a duplicate coinbase 344 if (InsecureRandRange(10) == 0 && coinbase_coins.size()) { 345 auto utxod = FindRandomFrom(coinbase_coins); 346 // Reuse the exact same coinbase 347 tx = CMutableTransaction{std::get<0>(utxod->second)}; 348 // shouldn't be available for reconnection if it's been duplicated 349 disconnected_coins.erase(utxod->first); 350 351 duplicate_coins.insert(utxod->first); 352 } 353 else { 354 coinbase_coins.insert(COutPoint(tx.GetHash(), 0)); 355 } 356 assert(CTransaction(tx).IsCoinBase()); 357 } 358 359 // 17/20 times reconnect previous or add a regular tx 360 else { 361 362 COutPoint prevout; 363 // 1/20 times reconnect a previously disconnected tx 364 if (randiter % 20 == 2 && disconnected_coins.size()) { 365 auto utxod = FindRandomFrom(disconnected_coins); 366 tx = CMutableTransaction{std::get<0>(utxod->second)}; 367 prevout = tx.vin[0].prevout; 368 if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) { 369 disconnected_coins.erase(utxod->first); 370 continue; 371 } 372 373 // If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate 374 if (utxoset.count(utxod->first)) { 375 assert(CTransaction(tx).IsCoinBase()); 376 assert(duplicate_coins.count(utxod->first)); 377 } 378 disconnected_coins.erase(utxod->first); 379 } 380 381 // 16/20 times create a regular tx 382 else { 383 auto utxod = FindRandomFrom(utxoset); 384 prevout = utxod->first; 385 386 // Construct the tx to spend the coins of prevouthash 387 tx.vin[0].prevout = prevout; 388 assert(!CTransaction(tx).IsCoinBase()); 389 } 390 // In this simple test coins only have two states, spent or unspent, save the unspent state to restore 391 old_coin = result[prevout]; 392 // Update the expected result of prevouthash to know these coins are spent 393 result[prevout].Clear(); 394 395 utxoset.erase(prevout); 396 397 // The test is designed to ensure spending a duplicate coinbase will work properly 398 // if that ever happens and not resurrect the previously overwritten coinbase 399 if (duplicate_coins.count(prevout)) { 400 spent_a_duplicate_coinbase = true; 401 } 402 403 } 404 // Update the expected result to know about the new output coins 405 assert(tx.vout.size() == 1); 406 const COutPoint outpoint(tx.GetHash(), 0); 407 result[outpoint] = Coin{tx.vout[0], height, CTransaction{tx}.IsCoinBase()}; 408 409 // Call UpdateCoins on the top cache 410 CTxUndo undo; 411 UpdateCoins(CTransaction{tx}, *(stack.back()), undo, height); 412 413 // Update the utxo set for future spends 414 utxoset.insert(outpoint); 415 416 // Track this tx and undo info to use later 417 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin)); 418 } else if (utxoset.size()) { 419 //1/20 times undo a previous transaction 420 auto utxod = FindRandomFrom(utxoset); 421 422 CTransaction &tx = std::get<0>(utxod->second); 423 CTxUndo &undo = std::get<1>(utxod->second); 424 Coin &orig_coin = std::get<2>(utxod->second); 425 426 // Update the expected result 427 // Remove new outputs 428 result[utxod->first].Clear(); 429 // If not coinbase restore prevout 430 if (!tx.IsCoinBase()) { 431 result[tx.vin[0].prevout] = orig_coin; 432 } 433 434 // Disconnect the tx from the current UTXO 435 // See code in DisconnectBlock 436 // remove outputs 437 BOOST_CHECK(stack.back()->SpendCoin(utxod->first)); 438 // restore inputs 439 if (!tx.IsCoinBase()) { 440 const COutPoint &out = tx.vin[0].prevout; 441 Coin coin = undo.vprevout[0]; 442 ApplyTxInUndo(std::move(coin), *(stack.back()), out); 443 } 444 // Store as a candidate for reconnection 445 disconnected_coins.insert(utxod->first); 446 447 // Update the utxoset 448 utxoset.erase(utxod->first); 449 if (!tx.IsCoinBase()) 450 utxoset.insert(tx.vin[0].prevout); 451 } 452 453 // Once every 1000 iterations and at the end, verify the full cache. 454 if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { 455 for (const auto& entry : result) { 456 bool have = stack.back()->HaveCoin(entry.first); 457 const Coin& coin = stack.back()->AccessCoin(entry.first); 458 BOOST_CHECK(have == !coin.IsSpent()); 459 BOOST_CHECK(coin == entry.second); 460 } 461 } 462 463 // One every 10 iterations, remove a random entry from the cache 464 if (utxoset.size() > 1 && InsecureRandRange(30) == 0) { 465 stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first); 466 } 467 if (disconnected_coins.size() > 1 && InsecureRandRange(30) == 0) { 468 stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(disconnected_coins)->first); 469 } 470 if (duplicate_coins.size() > 1 && InsecureRandRange(30) == 0) { 471 stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(duplicate_coins)->first); 472 } 473 474 if (InsecureRandRange(100) == 0) { 475 // Every 100 iterations, flush an intermediate cache 476 if (stack.size() > 1 && InsecureRandBool() == 0) { 477 unsigned int flushIndex = InsecureRandRange(stack.size() - 1); 478 BOOST_CHECK(stack[flushIndex]->Flush()); 479 } 480 } 481 if (InsecureRandRange(100) == 0) { 482 // Every 100 iterations, change the cache stack. 483 if (stack.size() > 0 && InsecureRandBool() == 0) { 484 BOOST_CHECK(stack.back()->Flush()); 485 stack.pop_back(); 486 } 487 if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { 488 CCoinsView* tip = &base; 489 if (stack.size() > 0) { 490 tip = stack.back().get(); 491 } 492 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip)); 493 } 494 } 495 } 496 497 // Verify coverage. 498 BOOST_CHECK(spent_a_duplicate_coinbase); 499 500 g_mock_deterministic_tests = false; 501 } 502 503 BOOST_AUTO_TEST_CASE(ccoins_serialization) 504 { 505 // Good example 506 DataStream ss1{ParseHex("97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35")}; 507 Coin cc1; 508 ss1 >> cc1; 509 BOOST_CHECK_EQUAL(cc1.fCoinBase, false); 510 BOOST_CHECK_EQUAL(cc1.nHeight, 203998U); 511 BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000}); 512 BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); 513 514 // Good example 515 DataStream ss2{ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4")}; 516 Coin cc2; 517 ss2 >> cc2; 518 BOOST_CHECK_EQUAL(cc2.fCoinBase, true); 519 BOOST_CHECK_EQUAL(cc2.nHeight, 120891U); 520 BOOST_CHECK_EQUAL(cc2.out.nValue, 110397); 521 BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); 522 523 // Smallest possible example 524 DataStream ss3{ParseHex("000006")}; 525 Coin cc3; 526 ss3 >> cc3; 527 BOOST_CHECK_EQUAL(cc3.fCoinBase, false); 528 BOOST_CHECK_EQUAL(cc3.nHeight, 0U); 529 BOOST_CHECK_EQUAL(cc3.out.nValue, 0); 530 BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U); 531 532 // scriptPubKey that ends beyond the end of the stream 533 DataStream ss4{ParseHex("000007")}; 534 try { 535 Coin cc4; 536 ss4 >> cc4; 537 BOOST_CHECK_MESSAGE(false, "We should have thrown"); 538 } catch (const std::ios_base::failure&) { 539 } 540 541 // Very large scriptPubKey (3*10^9 bytes) past the end of the stream 542 DataStream tmp{}; 543 uint64_t x = 3000000000ULL; 544 tmp << VARINT(x); 545 BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00"); 546 DataStream ss5{ParseHex("00008a95c0bb00")}; 547 try { 548 Coin cc5; 549 ss5 >> cc5; 550 BOOST_CHECK_MESSAGE(false, "We should have thrown"); 551 } catch (const std::ios_base::failure&) { 552 } 553 } 554 555 const static COutPoint OUTPOINT; 556 const static CAmount SPENT = -1; 557 const static CAmount ABSENT = -2; 558 const static CAmount FAIL = -3; 559 const static CAmount VALUE1 = 100; 560 const static CAmount VALUE2 = 200; 561 const static CAmount VALUE3 = 300; 562 const static char DIRTY = CCoinsCacheEntry::DIRTY; 563 const static char FRESH = CCoinsCacheEntry::FRESH; 564 const static char NO_ENTRY = -1; 565 566 const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)}; 567 const static auto CLEAN_FLAGS = {char(0), FRESH}; 568 const static auto ABSENT_FLAGS = {NO_ENTRY}; 569 570 static void SetCoinsValue(CAmount value, Coin& coin) 571 { 572 assert(value != ABSENT); 573 coin.Clear(); 574 assert(coin.IsSpent()); 575 if (value != SPENT) { 576 coin.out.nValue = value; 577 coin.nHeight = 1; 578 assert(!coin.IsSpent()); 579 } 580 } 581 582 static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags) 583 { 584 if (value == ABSENT) { 585 assert(flags == NO_ENTRY); 586 return 0; 587 } 588 assert(flags != NO_ENTRY); 589 CCoinsCacheEntry entry; 590 entry.flags = flags; 591 SetCoinsValue(value, entry.coin); 592 auto inserted = map.emplace(OUTPOINT, std::move(entry)); 593 assert(inserted.second); 594 return inserted.first->second.coin.DynamicMemoryUsage(); 595 } 596 597 void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags, const COutPoint& outp = OUTPOINT) 598 { 599 auto it = map.find(outp); 600 if (it == map.end()) { 601 value = ABSENT; 602 flags = NO_ENTRY; 603 } else { 604 if (it->second.coin.IsSpent()) { 605 value = SPENT; 606 } else { 607 value = it->second.coin.out.nValue; 608 } 609 flags = it->second.flags; 610 assert(flags != NO_ENTRY); 611 } 612 } 613 614 void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags) 615 { 616 CCoinsMapMemoryResource resource; 617 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource}; 618 InsertCoinsMapEntry(map, value, flags); 619 BOOST_CHECK(view.BatchWrite(map, {})); 620 } 621 622 class SingleEntryCacheTest 623 { 624 public: 625 SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags) 626 { 627 WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY); 628 cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags); 629 } 630 631 CCoinsView root; 632 CCoinsViewCacheTest base{&root}; 633 CCoinsViewCacheTest cache{&base}; 634 }; 635 636 static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags) 637 { 638 SingleEntryCacheTest test(base_value, cache_value, cache_flags); 639 test.cache.AccessCoin(OUTPOINT); 640 test.cache.SelfTest(); 641 642 CAmount result_value; 643 char result_flags; 644 GetCoinsMapEntry(test.cache.map(), result_value, result_flags); 645 BOOST_CHECK_EQUAL(result_value, expected_value); 646 BOOST_CHECK_EQUAL(result_flags, expected_flags); 647 } 648 649 BOOST_AUTO_TEST_CASE(ccoins_access) 650 { 651 /* Check AccessCoin behavior, requesting a coin from a cache view layered on 652 * top of a base view, and checking the resulting entry in the cache after 653 * the access. 654 * 655 * Base Cache Result Cache Result 656 * Value Value Value Flags Flags 657 */ 658 CheckAccessCoin(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY ); 659 CheckAccessCoin(ABSENT, SPENT , SPENT , 0 , 0 ); 660 CheckAccessCoin(ABSENT, SPENT , SPENT , FRESH , FRESH ); 661 CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY , DIRTY ); 662 CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH); 663 CheckAccessCoin(ABSENT, VALUE2, VALUE2, 0 , 0 ); 664 CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH , FRESH ); 665 CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY ); 666 CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH); 667 CheckAccessCoin(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY ); 668 CheckAccessCoin(SPENT , SPENT , SPENT , 0 , 0 ); 669 CheckAccessCoin(SPENT , SPENT , SPENT , FRESH , FRESH ); 670 CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY , DIRTY ); 671 CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH); 672 CheckAccessCoin(SPENT , VALUE2, VALUE2, 0 , 0 ); 673 CheckAccessCoin(SPENT , VALUE2, VALUE2, FRESH , FRESH ); 674 CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY , DIRTY ); 675 CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH); 676 CheckAccessCoin(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 ); 677 CheckAccessCoin(VALUE1, SPENT , SPENT , 0 , 0 ); 678 CheckAccessCoin(VALUE1, SPENT , SPENT , FRESH , FRESH ); 679 CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY , DIRTY ); 680 CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH); 681 CheckAccessCoin(VALUE1, VALUE2, VALUE2, 0 , 0 ); 682 CheckAccessCoin(VALUE1, VALUE2, VALUE2, FRESH , FRESH ); 683 CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY ); 684 CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH); 685 } 686 687 static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags) 688 { 689 SingleEntryCacheTest test(base_value, cache_value, cache_flags); 690 test.cache.SpendCoin(OUTPOINT); 691 test.cache.SelfTest(); 692 693 CAmount result_value; 694 char result_flags; 695 GetCoinsMapEntry(test.cache.map(), result_value, result_flags); 696 BOOST_CHECK_EQUAL(result_value, expected_value); 697 BOOST_CHECK_EQUAL(result_flags, expected_flags); 698 }; 699 700 BOOST_AUTO_TEST_CASE(ccoins_spend) 701 { 702 /* Check SpendCoin behavior, requesting a coin from a cache view layered on 703 * top of a base view, spending, and then checking 704 * the resulting entry in the cache after the modification. 705 * 706 * Base Cache Result Cache Result 707 * Value Value Value Flags Flags 708 */ 709 CheckSpendCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY ); 710 CheckSpendCoins(ABSENT, SPENT , SPENT , 0 , DIRTY ); 711 CheckSpendCoins(ABSENT, SPENT , ABSENT, FRESH , NO_ENTRY ); 712 CheckSpendCoins(ABSENT, SPENT , SPENT , DIRTY , DIRTY ); 713 CheckSpendCoins(ABSENT, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY ); 714 CheckSpendCoins(ABSENT, VALUE2, SPENT , 0 , DIRTY ); 715 CheckSpendCoins(ABSENT, VALUE2, ABSENT, FRESH , NO_ENTRY ); 716 CheckSpendCoins(ABSENT, VALUE2, SPENT , DIRTY , DIRTY ); 717 CheckSpendCoins(ABSENT, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY ); 718 CheckSpendCoins(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY ); 719 CheckSpendCoins(SPENT , SPENT , SPENT , 0 , DIRTY ); 720 CheckSpendCoins(SPENT , SPENT , ABSENT, FRESH , NO_ENTRY ); 721 CheckSpendCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY ); 722 CheckSpendCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY ); 723 CheckSpendCoins(SPENT , VALUE2, SPENT , 0 , DIRTY ); 724 CheckSpendCoins(SPENT , VALUE2, ABSENT, FRESH , NO_ENTRY ); 725 CheckSpendCoins(SPENT , VALUE2, SPENT , DIRTY , DIRTY ); 726 CheckSpendCoins(SPENT , VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY ); 727 CheckSpendCoins(VALUE1, ABSENT, SPENT , NO_ENTRY , DIRTY ); 728 CheckSpendCoins(VALUE1, SPENT , SPENT , 0 , DIRTY ); 729 CheckSpendCoins(VALUE1, SPENT , ABSENT, FRESH , NO_ENTRY ); 730 CheckSpendCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY ); 731 CheckSpendCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY ); 732 CheckSpendCoins(VALUE1, VALUE2, SPENT , 0 , DIRTY ); 733 CheckSpendCoins(VALUE1, VALUE2, ABSENT, FRESH , NO_ENTRY ); 734 CheckSpendCoins(VALUE1, VALUE2, SPENT , DIRTY , DIRTY ); 735 CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY ); 736 } 737 738 static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase) 739 { 740 SingleEntryCacheTest test(base_value, cache_value, cache_flags); 741 742 CAmount result_value; 743 char result_flags; 744 try { 745 CTxOut output; 746 output.nValue = modify_value; 747 test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase); 748 test.cache.SelfTest(); 749 GetCoinsMapEntry(test.cache.map(), result_value, result_flags); 750 } catch (std::logic_error&) { 751 result_value = FAIL; 752 result_flags = NO_ENTRY; 753 } 754 755 BOOST_CHECK_EQUAL(result_value, expected_value); 756 BOOST_CHECK_EQUAL(result_flags, expected_flags); 757 } 758 759 // Simple wrapper for CheckAddCoinBase function above that loops through 760 // different possible base_values, making sure each one gives the same results. 761 // This wrapper lets the coins_add test below be shorter and less repetitive, 762 // while still verifying that the CoinsViewCache::AddCoin implementation 763 // ignores base values. 764 template <typename... Args> 765 static void CheckAddCoin(Args&&... args) 766 { 767 for (const CAmount base_value : {ABSENT, SPENT, VALUE1}) 768 CheckAddCoinBase(base_value, std::forward<Args>(args)...); 769 } 770 771 BOOST_AUTO_TEST_CASE(ccoins_add) 772 { 773 /* Check AddCoin behavior, requesting a new coin from a cache view, 774 * writing a modification to the coin, and then checking the resulting 775 * entry in the cache after the modification. Verify behavior with the 776 * AddCoin possible_overwrite argument set to false, and to true. 777 * 778 * Cache Write Result Cache Result possible_overwrite 779 * Value Value Value Flags Flags 780 */ 781 CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH, false); 782 CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY , true ); 783 CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY|FRESH, false); 784 CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY , true ); 785 CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, false); 786 CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, true ); 787 CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , false); 788 CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , true ); 789 CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false); 790 CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true ); 791 CheckAddCoin(VALUE2, VALUE3, FAIL , 0 , NO_ENTRY , false); 792 CheckAddCoin(VALUE2, VALUE3, VALUE3, 0 , DIRTY , true ); 793 CheckAddCoin(VALUE2, VALUE3, FAIL , FRESH , NO_ENTRY , false); 794 CheckAddCoin(VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true ); 795 CheckAddCoin(VALUE2, VALUE3, FAIL , DIRTY , NO_ENTRY , false); 796 CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY , DIRTY , true ); 797 CheckAddCoin(VALUE2, VALUE3, FAIL , DIRTY|FRESH, NO_ENTRY , false); 798 CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true ); 799 } 800 801 void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags) 802 { 803 SingleEntryCacheTest test(ABSENT, parent_value, parent_flags); 804 805 CAmount result_value; 806 char result_flags; 807 try { 808 WriteCoinsViewEntry(test.cache, child_value, child_flags); 809 test.cache.SelfTest(); 810 GetCoinsMapEntry(test.cache.map(), result_value, result_flags); 811 } catch (std::logic_error&) { 812 result_value = FAIL; 813 result_flags = NO_ENTRY; 814 } 815 816 BOOST_CHECK_EQUAL(result_value, expected_value); 817 BOOST_CHECK_EQUAL(result_flags, expected_flags); 818 } 819 820 BOOST_AUTO_TEST_CASE(ccoins_write) 821 { 822 /* Check BatchWrite behavior, flushing one entry from a child cache to a 823 * parent cache, and checking the resulting entry in the parent cache 824 * after the write. 825 * 826 * Parent Child Result Parent Child Result 827 * Value Value Value Flags Flags Flags 828 */ 829 CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY ); 830 CheckWriteCoins(ABSENT, SPENT , SPENT , NO_ENTRY , DIRTY , DIRTY ); 831 CheckWriteCoins(ABSENT, SPENT , ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY ); 832 CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY , DIRTY ); 833 CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH); 834 CheckWriteCoins(SPENT , ABSENT, SPENT , 0 , NO_ENTRY , 0 ); 835 CheckWriteCoins(SPENT , ABSENT, SPENT , FRESH , NO_ENTRY , FRESH ); 836 CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY , NO_ENTRY , DIRTY ); 837 CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH); 838 CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY , DIRTY ); 839 CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY|FRESH, DIRTY ); 840 CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY ); 841 CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY ); 842 CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY , DIRTY ); 843 CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY|FRESH, DIRTY ); 844 CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY ); 845 CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY ); 846 CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY , DIRTY ); 847 CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY ); 848 CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH); 849 CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH); 850 CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY , DIRTY ); 851 CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY ); 852 CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH); 853 CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH); 854 CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0 , NO_ENTRY , 0 ); 855 CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH , NO_ENTRY , FRESH ); 856 CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY ); 857 CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH); 858 CheckWriteCoins(VALUE1, SPENT , SPENT , 0 , DIRTY , DIRTY ); 859 CheckWriteCoins(VALUE1, SPENT , FAIL , 0 , DIRTY|FRESH, NO_ENTRY ); 860 CheckWriteCoins(VALUE1, SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY ); 861 CheckWriteCoins(VALUE1, SPENT , FAIL , FRESH , DIRTY|FRESH, NO_ENTRY ); 862 CheckWriteCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY , DIRTY ); 863 CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY ); 864 CheckWriteCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY ); 865 CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY ); 866 CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY ); 867 CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY ); 868 CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH); 869 CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY ); 870 CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY ); 871 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY ); 872 CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH); 873 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY ); 874 875 // The checks above omit cases where the child flags are not DIRTY, since 876 // they would be too repetitive (the parent cache is never updated in these 877 // cases). The loop below covers these cases and makes sure the parent cache 878 // is always left unchanged. 879 for (const CAmount parent_value : {ABSENT, SPENT, VALUE1}) 880 for (const CAmount child_value : {ABSENT, SPENT, VALUE2}) 881 for (const char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS) 882 for (const char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS) 883 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags); 884 } 885 886 887 Coin MakeCoin() 888 { 889 Coin coin; 890 coin.out.nValue = InsecureRand32(); 891 coin.nHeight = InsecureRandRange(4096); 892 coin.fCoinBase = 0; 893 return coin; 894 } 895 896 897 //! For CCoinsViewCache instances backed by either another cache instance or 898 //! leveldb, test cache behavior and flag state (DIRTY/FRESH) by 899 //! 900 //! 1. Adding a random coin to the child-most cache, 901 //! 2. Flushing all caches (without erasing), 902 //! 3. Ensure the entry still exists in the cache and has been written to parent, 903 //! 4. (if `do_erasing_flush`) Flushing the caches again (with erasing), 904 //! 5. (if `do_erasing_flush`) Ensure the entry has been written to the parent and is no longer in the cache, 905 //! 6. Spend the coin, ensure it no longer exists in the parent. 906 //! 907 void TestFlushBehavior( 908 CCoinsViewCacheTest* view, 909 CCoinsViewDB& base, 910 std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches, 911 bool do_erasing_flush) 912 { 913 CAmount value; 914 char flags; 915 size_t cache_usage; 916 size_t cache_size; 917 918 auto flush_all = [&all_caches](bool erase) { 919 // Flush in reverse order to ensure that flushes happen from children up. 920 for (auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) { 921 auto& cache = *i; 922 // hashBlock must be filled before flushing to disk; value is 923 // unimportant here. This is normally done during connect/disconnect block. 924 cache->SetBestBlock(InsecureRand256()); 925 erase ? cache->Flush() : cache->Sync(); 926 } 927 }; 928 929 Txid txid = Txid::FromUint256(InsecureRand256()); 930 COutPoint outp = COutPoint(txid, 0); 931 Coin coin = MakeCoin(); 932 // Ensure the coins views haven't seen this coin before. 933 BOOST_CHECK(!base.HaveCoin(outp)); 934 BOOST_CHECK(!view->HaveCoin(outp)); 935 936 // --- 1. Adding a random coin to the child cache 937 // 938 view->AddCoin(outp, Coin(coin), false); 939 940 cache_usage = view->DynamicMemoryUsage(); 941 cache_size = view->map().size(); 942 943 // `base` shouldn't have coin (no flush yet) but `view` should have cached it. 944 BOOST_CHECK(!base.HaveCoin(outp)); 945 BOOST_CHECK(view->HaveCoin(outp)); 946 947 GetCoinsMapEntry(view->map(), value, flags, outp); 948 BOOST_CHECK_EQUAL(value, coin.out.nValue); 949 BOOST_CHECK_EQUAL(flags, DIRTY|FRESH); 950 951 // --- 2. Flushing all caches (without erasing) 952 // 953 flush_all(/*erase=*/ false); 954 955 // CoinsMap usage should be unchanged since we didn't erase anything. 956 BOOST_CHECK_EQUAL(cache_usage, view->DynamicMemoryUsage()); 957 BOOST_CHECK_EQUAL(cache_size, view->map().size()); 958 959 // --- 3. Ensuring the entry still exists in the cache and has been written to parent 960 // 961 GetCoinsMapEntry(view->map(), value, flags, outp); 962 BOOST_CHECK_EQUAL(value, coin.out.nValue); 963 BOOST_CHECK_EQUAL(flags, 0); // Flags should have been wiped. 964 965 // Both views should now have the coin. 966 BOOST_CHECK(base.HaveCoin(outp)); 967 BOOST_CHECK(view->HaveCoin(outp)); 968 969 if (do_erasing_flush) { 970 // --- 4. Flushing the caches again (with erasing) 971 // 972 flush_all(/*erase=*/ true); 973 974 // Memory does not necessarily go down due to the map using a memory pool 975 BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage); 976 // Size of the cache must go down though 977 BOOST_TEST(view->map().size() < cache_size); 978 979 // --- 5. Ensuring the entry is no longer in the cache 980 // 981 GetCoinsMapEntry(view->map(), value, flags, outp); 982 BOOST_CHECK_EQUAL(value, ABSENT); 983 BOOST_CHECK_EQUAL(flags, NO_ENTRY); 984 985 view->AccessCoin(outp); 986 GetCoinsMapEntry(view->map(), value, flags, outp); 987 BOOST_CHECK_EQUAL(value, coin.out.nValue); 988 BOOST_CHECK_EQUAL(flags, 0); 989 } 990 991 // Can't overwrite an entry without specifying that an overwrite is 992 // expected. 993 BOOST_CHECK_THROW( 994 view->AddCoin(outp, Coin(coin), /*possible_overwrite=*/ false), 995 std::logic_error); 996 997 // --- 6. Spend the coin. 998 // 999 BOOST_CHECK(view->SpendCoin(outp)); 1000 1001 // The coin should be in the cache, but spent and marked dirty. 1002 GetCoinsMapEntry(view->map(), value, flags, outp); 1003 BOOST_CHECK_EQUAL(value, SPENT); 1004 BOOST_CHECK_EQUAL(flags, DIRTY); 1005 BOOST_CHECK(!view->HaveCoin(outp)); // Coin should be considered spent in `view`. 1006 BOOST_CHECK(base.HaveCoin(outp)); // But coin should still be unspent in `base`. 1007 1008 flush_all(/*erase=*/ false); 1009 1010 // Coin should be considered spent in both views. 1011 BOOST_CHECK(!view->HaveCoin(outp)); 1012 BOOST_CHECK(!base.HaveCoin(outp)); 1013 1014 // Spent coin should not be spendable. 1015 BOOST_CHECK(!view->SpendCoin(outp)); 1016 1017 // --- Bonus check: ensure that a coin added to the base view via one cache 1018 // can be spent by another cache which has never seen it. 1019 // 1020 txid = Txid::FromUint256(InsecureRand256()); 1021 outp = COutPoint(txid, 0); 1022 coin = MakeCoin(); 1023 BOOST_CHECK(!base.HaveCoin(outp)); 1024 BOOST_CHECK(!all_caches[0]->HaveCoin(outp)); 1025 BOOST_CHECK(!all_caches[1]->HaveCoin(outp)); 1026 1027 all_caches[0]->AddCoin(outp, std::move(coin), false); 1028 all_caches[0]->Sync(); 1029 BOOST_CHECK(base.HaveCoin(outp)); 1030 BOOST_CHECK(all_caches[0]->HaveCoin(outp)); 1031 BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp)); 1032 1033 BOOST_CHECK(all_caches[1]->SpendCoin(outp)); 1034 flush_all(/*erase=*/ false); 1035 BOOST_CHECK(!base.HaveCoin(outp)); 1036 BOOST_CHECK(!all_caches[0]->HaveCoin(outp)); 1037 BOOST_CHECK(!all_caches[1]->HaveCoin(outp)); 1038 1039 flush_all(/*erase=*/ true); // Erase all cache content. 1040 1041 // --- Bonus check 2: ensure that a FRESH, spent coin is deleted by Sync() 1042 // 1043 txid = Txid::FromUint256(InsecureRand256()); 1044 outp = COutPoint(txid, 0); 1045 coin = MakeCoin(); 1046 CAmount coin_val = coin.out.nValue; 1047 BOOST_CHECK(!base.HaveCoin(outp)); 1048 BOOST_CHECK(!all_caches[0]->HaveCoin(outp)); 1049 BOOST_CHECK(!all_caches[1]->HaveCoin(outp)); 1050 1051 // Add and spend from same cache without flushing. 1052 all_caches[0]->AddCoin(outp, std::move(coin), false); 1053 1054 // Coin should be FRESH in the cache. 1055 GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp); 1056 BOOST_CHECK_EQUAL(value, coin_val); 1057 BOOST_CHECK_EQUAL(flags, DIRTY|FRESH); 1058 1059 // Base shouldn't have seen coin. 1060 BOOST_CHECK(!base.HaveCoin(outp)); 1061 1062 BOOST_CHECK(all_caches[0]->SpendCoin(outp)); 1063 all_caches[0]->Sync(); 1064 1065 // Ensure there is no sign of the coin after spend/flush. 1066 GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp); 1067 BOOST_CHECK_EQUAL(value, ABSENT); 1068 BOOST_CHECK_EQUAL(flags, NO_ENTRY); 1069 BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp)); 1070 BOOST_CHECK(!base.HaveCoin(outp)); 1071 } 1072 1073 BOOST_AUTO_TEST_CASE(ccoins_flush_behavior) 1074 { 1075 // Create two in-memory caches atop a leveldb view. 1076 CCoinsViewDB base{{.path = "test", .cache_bytes = 1 << 23, .memory_only = true}, {}}; 1077 std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches; 1078 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base)); 1079 caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get())); 1080 1081 for (const auto& view : caches) { 1082 TestFlushBehavior(view.get(), base, caches, /*do_erasing_flush=*/false); 1083 TestFlushBehavior(view.get(), base, caches, /*do_erasing_flush=*/true); 1084 } 1085 } 1086 1087 BOOST_AUTO_TEST_CASE(coins_resource_is_used) 1088 { 1089 CCoinsMapMemoryResource resource; 1090 PoolResourceTester::CheckAllDataAccountedFor(resource); 1091 1092 { 1093 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource}; 1094 BOOST_TEST(memusage::DynamicUsage(map) >= resource.ChunkSizeBytes()); 1095 1096 map.reserve(1000); 1097 1098 // The resource has preallocated a chunk, so we should have space for at several nodes without the need to allocate anything else. 1099 const auto usage_before = memusage::DynamicUsage(map); 1100 1101 COutPoint out_point{}; 1102 for (size_t i = 0; i < 1000; ++i) { 1103 out_point.n = i; 1104 map[out_point]; 1105 } 1106 BOOST_TEST(usage_before == memusage::DynamicUsage(map)); 1107 } 1108 1109 PoolResourceTester::CheckAllDataAccountedFor(resource); 1110 } 1111 1112 BOOST_AUTO_TEST_SUITE_END()