Changeset View
Changeset View
Standalone View
Standalone View
src/test/coins_tests.cpp
// Copyright (c) 2014-2019 The Bitcoin Core developers | // Copyright (c) 2014-2019 The Bitcoin Core developers | |||||||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | |||||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |||||||||
#include <coins.h> | #include <coins.h> | |||||||||
#include <attributes.h> | #include <attributes.h> | |||||||||
#include <clientversion.h> | #include <clientversion.h> | |||||||||
#include <script/standard.h> | #include <script/standard.h> | |||||||||
#include <streams.h> | #include <streams.h> | |||||||||
#include <txdb.h> | ||||||||||
#include <undo.h> | #include <undo.h> | |||||||||
#include <util/strencodings.h> | #include <util/strencodings.h> | |||||||||
#include <test/util/setup_common.h> | #include <test/util/setup_common.h> | |||||||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | |||||||||
#include <map> | #include <map> | |||||||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | ||||||||||
// stack of caches on top of CCoinsViewTest. | // stack of caches on top of CCoinsViewTest. | |||||||||
// | // | |||||||||
// It will randomly create/update/delete Coin entries to a tip of caches, with | // It will randomly create/update/delete Coin entries to a tip of caches, with | |||||||||
// txids picked from a limited list of random 256-bit hashes. Occasionally, a | // txids picked from a limited list of random 256-bit hashes. Occasionally, a | |||||||||
// new tip is added to the stack of caches, or the tip is flushed and removed. | // new tip is added to the stack of caches, or the tip is flushed and removed. | |||||||||
// | // | |||||||||
// During the process, booleans are kept to make sure that the randomized | // During the process, booleans are kept to make sure that the randomized | |||||||||
// operation hits all branches. | // operation hits all branches. | |||||||||
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) { | // | |||||||||
// If fake_best_block is true, assign a random uint256 to mock the recording | ||||||||||
majcostaUnsubmitted Not Done Inline Actions
majcosta: | ||||||||||
// of best block on flush. This is necessary when using CCoinsViewDB as the | ||||||||||
// base, otherwise we'll hit an assertion in BatchWrite. | ||||||||||
// | ||||||||||
void SimulationTest(CCoinsView *base, bool fake_best_block) { | ||||||||||
// Various coverage trackers. | // Various coverage trackers. | |||||||||
bool removed_all_caches = false; | bool removed_all_caches = false; | |||||||||
bool reached_4_caches = false; | bool reached_4_caches = false; | |||||||||
bool added_an_entry = false; | bool added_an_entry = false; | |||||||||
bool added_an_unspendable_entry = false; | bool added_an_unspendable_entry = false; | |||||||||
bool removed_an_entry = false; | bool removed_an_entry = false; | |||||||||
bool updated_an_entry = false; | bool updated_an_entry = false; | |||||||||
bool found_an_entry = false; | bool found_an_entry = false; | |||||||||
bool missed_an_entry = false; | bool missed_an_entry = false; | |||||||||
bool uncached_an_entry = false; | bool uncached_an_entry = false; | |||||||||
// A simple map to track what we expect the cache stack to represent. | // A simple map to track what we expect the cache stack to represent. | |||||||||
std::map<COutPoint, Coin> result; | std::map<COutPoint, Coin> result; | |||||||||
// The cache stack. | // The cache stack. | |||||||||
// A CCoinsViewTest at the bottom. | ||||||||||
CCoinsViewTest base; | ||||||||||
// A stack of CCoinsViewCaches on top. | // A stack of CCoinsViewCaches on top. | |||||||||
std::vector<CCoinsViewCacheTest *> stack; | std::vector<CCoinsViewCacheTest *> stack; | |||||||||
// Start with one cache. | // Start with one cache. | |||||||||
stack.push_back(new CCoinsViewCacheTest(&base)); | stack.push_back(new CCoinsViewCacheTest(base)); | |||||||||
// Use a limited set of random transaction ids, so we do test overwriting | // Use a limited set of random transaction ids, so we do test overwriting | |||||||||
// entries. | // entries. | |||||||||
std::vector<TxId> txids; | std::vector<TxId> txids; | |||||||||
txids.resize(NUM_SIMULATION_ITERATIONS / 8); | txids.resize(NUM_SIMULATION_ITERATIONS / 8); | |||||||||
for (size_t i = 0; i < txids.size(); i++) { | for (size_t i = 0; i < txids.size(); i++) { | |||||||||
txids[i] = TxId(InsecureRand256()); | txids[i] = TxId(InsecureRand256()); | |||||||||
} | } | |||||||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | |||||||||
test->SelfTest(); | test->SelfTest(); | |||||||||
} | } | |||||||||
} | } | |||||||||
// Every 100 iterations, flush an intermediate cache | // Every 100 iterations, flush an intermediate cache | |||||||||
if (InsecureRandRange(100) == 0) { | if (InsecureRandRange(100) == 0) { | |||||||||
if (stack.size() > 1 && InsecureRandBool() == 0) { | if (stack.size() > 1 && InsecureRandBool() == 0) { | |||||||||
unsigned int flushIndex = InsecureRandRange(stack.size() - 1); | unsigned int flushIndex = InsecureRandRange(stack.size() - 1); | |||||||||
if (fake_best_block) { | ||||||||||
stack[flushIndex]->SetBestBlock( | ||||||||||
BlockHash(InsecureRand256())); | ||||||||||
} | ||||||||||
BOOST_CHECK(stack[flushIndex]->Flush()); | BOOST_CHECK(stack[flushIndex]->Flush()); | |||||||||
} | } | |||||||||
} | } | |||||||||
if (InsecureRandRange(100) == 0) { | if (InsecureRandRange(100) == 0) { | |||||||||
// Every 100 iterations, change the cache stack. | // Every 100 iterations, change the cache stack. | |||||||||
if (stack.size() > 0 && InsecureRandBool() == 0) { | if (stack.size() > 0 && InsecureRandBool() == 0) { | |||||||||
// Remove the top cache | // Remove the top cache | |||||||||
if (fake_best_block) { | ||||||||||
stack.back()->SetBestBlock(BlockHash(InsecureRand256())); | ||||||||||
} | ||||||||||
BOOST_CHECK(stack.back()->Flush()); | BOOST_CHECK(stack.back()->Flush()); | |||||||||
delete stack.back(); | delete stack.back(); | |||||||||
stack.pop_back(); | stack.pop_back(); | |||||||||
} | } | |||||||||
if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { | if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { | |||||||||
// Add a new cache | // Add a new cache | |||||||||
CCoinsView *tip = &base; | CCoinsView *tip = base; | |||||||||
if (stack.size() > 0) { | if (stack.size() > 0) { | |||||||||
tip = stack.back(); | tip = stack.back(); | |||||||||
} else { | } else { | |||||||||
removed_all_caches = true; | removed_all_caches = true; | |||||||||
} | } | |||||||||
stack.push_back(new CCoinsViewCacheTest(tip)); | stack.push_back(new CCoinsViewCacheTest(tip)); | |||||||||
if (stack.size() == 4) { | if (stack.size() == 4) { | |||||||||
reached_4_caches = true; | reached_4_caches = true; | |||||||||
Show All 15 Lines | void SimulationTest(CCoinsView *base, bool fake_best_block) { | |||||||||
BOOST_CHECK(added_an_unspendable_entry); | BOOST_CHECK(added_an_unspendable_entry); | |||||||||
BOOST_CHECK(removed_an_entry); | BOOST_CHECK(removed_an_entry); | |||||||||
BOOST_CHECK(updated_an_entry); | BOOST_CHECK(updated_an_entry); | |||||||||
BOOST_CHECK(found_an_entry); | BOOST_CHECK(found_an_entry); | |||||||||
BOOST_CHECK(missed_an_entry); | BOOST_CHECK(missed_an_entry); | |||||||||
BOOST_CHECK(uncached_an_entry); | BOOST_CHECK(uncached_an_entry); | |||||||||
} | } | |||||||||
// Run the above simulation for multiple base types. | ||||||||||
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) { | ||||||||||
CCoinsViewTest base; | ||||||||||
SimulationTest(&base, false); | ||||||||||
CCoinsViewDB db_base{"test", /*nCacheSize*/ 1 << 23, /*fMemory*/ true, | ||||||||||
/*fWipe*/ false}; | ||||||||||
SimulationTest(&db_base, true); | ||||||||||
} | ||||||||||
// Store of all necessary tx and undo data for next test | // Store of all necessary tx and undo data for next test | |||||||||
typedef std::map<COutPoint, std::tuple<CTransaction, CTxUndo, Coin>> UtxoData; | typedef std::map<COutPoint, std::tuple<CTransaction, CTxUndo, Coin>> UtxoData; | |||||||||
UtxoData utxoData; | UtxoData utxoData; | |||||||||
UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) { | UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) { | |||||||||
assert(utxoSet.size()); | assert(utxoSet.size()); | |||||||||
auto utxoSetIt = utxoSet.lower_bound(COutPoint(TxId(InsecureRand256()), 0)); | auto utxoSetIt = utxoSet.lower_bound(COutPoint(TxId(InsecureRand256()), 0)); | |||||||||
if (utxoSetIt == utxoSet.end()) { | if (utxoSetIt == utxoSet.end()) { | |||||||||
▲ Show 20 Lines • Show All 638 Lines • Show Last 20 Lines |