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 BlockHash to mock the recording | |||||
// 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 |