Changeset View
Changeset View
Standalone View
Standalone View
src/test/coins_tests.cpp
Show First 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) | BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) | ||||
static const unsigned int NUM_SIMULATION_ITERATIONS = 40000; | static const unsigned int NUM_SIMULATION_ITERATIONS = 40000; | ||||
// This is a large randomized insert/remove simulation test on a variable-size | // This is a large randomized insert/remove simulation test on a variable-size | ||||
// 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 | // txhashes 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) { | BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) { | ||||
// 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; | ||||
Show All 13 Lines | BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) { | ||||
CCoinsViewTest base; | 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<uint256> txids; | std::vector<unspentid_t> unspentids; | ||||
txids.resize(NUM_SIMULATION_ITERATIONS / 8); | unspentids.resize(NUM_SIMULATION_ITERATIONS / 8); | ||||
for (size_t i = 0; i < txids.size(); i++) { | for (size_t i = 0; i < unspentids.size(); i++) { | ||||
txids[i] = GetRandHash(); | unspentids[i] = unspentid_t(GetRandHash()); | ||||
} | } | ||||
for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | ||||
// Do a random modification. | // Do a random modification. | ||||
{ | { | ||||
// txid we're going to modify in this iteration. | // unspentid we're going to modify in this iteration. | ||||
uint256 txid = txids[insecure_rand() % txids.size()]; | unspentid_t unspentid = | ||||
Coin &coin = result[COutPoint(txid, 0)]; | unspentids[insecure_rand() % unspentids.size()]; | ||||
Coin &coin = result[COutPoint(unspentid, 0)]; | |||||
const Coin &entry = | const Coin &entry = | ||||
(insecure_rand() % 500 == 0) | stack.back()->AccessCoin(COutPoint(unspentid, 0)); | ||||
? AccessByTxid(*stack.back(), txid) | |||||
: stack.back()->AccessCoin(COutPoint(txid, 0)); | |||||
BOOST_CHECK(coin == entry); | BOOST_CHECK(coin == entry); | ||||
if (insecure_rand() % 5 == 0 || coin.IsSpent()) { | if (insecure_rand() % 5 == 0 || coin.IsSpent()) { | ||||
CTxOut txout; | CTxOut txout; | ||||
txout.nValue = Amount(int64_t(insecure_rand())); | txout.nValue = Amount(int64_t(insecure_rand())); | ||||
if (insecure_rand() % 16 == 0 && coin.IsSpent()) { | if (insecure_rand() % 16 == 0 && coin.IsSpent()) { | ||||
txout.scriptPubKey.assign(1 + (insecure_rand() & 0x3F), | txout.scriptPubKey.assign(1 + (insecure_rand() & 0x3F), | ||||
OP_RETURN); | OP_RETURN); | ||||
BOOST_CHECK(txout.scriptPubKey.IsUnspendable()); | BOOST_CHECK(txout.scriptPubKey.IsUnspendable()); | ||||
added_an_unspendable_entry = true; | added_an_unspendable_entry = true; | ||||
} else { | } else { | ||||
// Random sizes so we can test memory usage accounting | // Random sizes so we can test memory usage accounting | ||||
txout.scriptPubKey.assign(insecure_rand() & 0x3F, 0); | txout.scriptPubKey.assign(insecure_rand() & 0x3F, 0); | ||||
(coin.IsSpent() ? added_an_entry : updated_an_entry) = true; | (coin.IsSpent() ? added_an_entry : updated_an_entry) = true; | ||||
coin = Coin(txout, 1, false); | coin = Coin(txout, 1, false); | ||||
} | } | ||||
Coin newcoin(txout, 1, false); | Coin newcoin(txout, 1, false); | ||||
stack.back()->AddCoin(COutPoint(txid, 0), newcoin, | stack.back()->AddCoin(COutPoint(unspentid, 0), newcoin, | ||||
!coin.IsSpent() || insecure_rand() & 1); | !coin.IsSpent() || insecure_rand() & 1); | ||||
} else { | } else { | ||||
removed_an_entry = true; | removed_an_entry = true; | ||||
coin.Clear(); | coin.Clear(); | ||||
stack.back()->SpendCoin(COutPoint(txid, 0)); | stack.back()->SpendCoin(COutPoint(unspentid, 0)); | ||||
} | } | ||||
} | } | ||||
// One every 10 iterations, remove a random entry from the cache | // One every 10 iterations, remove a random entry from the cache | ||||
if (insecure_rand() % 10) { | if (insecure_rand() % 10) { | ||||
COutPoint out(txids[insecure_rand() % txids.size()], 0); | COutPoint out(unspentids[insecure_rand() % unspentids.size()], 0); | ||||
int cacheid = insecure_rand() % stack.size(); | int cacheid = insecure_rand() % stack.size(); | ||||
stack[cacheid]->Uncache(out); | stack[cacheid]->Uncache(out); | ||||
uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out); | uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out); | ||||
} | } | ||||
// Once every 1000 iterations and at the end, verify the full cache. | // Once every 1000 iterations and at the end, verify the full cache. | ||||
if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { | if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { | ||||
for (auto it = result.begin(); it != result.end(); it++) { | for (auto it = result.begin(); it != result.end(); it++) { | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
// 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(GetRandHash(), 0)); | auto utxoSetIt = | ||||
utxoSet.lower_bound(COutPoint(unspentid_t(GetRandHash()), 0)); | |||||
if (utxoSetIt == utxoSet.end()) { | if (utxoSetIt == utxoSet.end()) { | ||||
utxoSetIt = utxoSet.begin(); | utxoSetIt = utxoSet.begin(); | ||||
} | } | ||||
auto utxoDataIt = utxoData.find(*utxoSetIt); | auto utxoDataIt = utxoData.find(*utxoSetIt); | ||||
assert(utxoDataIt != utxoData.end()); | assert(utxoDataIt != utxoData.end()); | ||||
return utxoDataIt; | return utxoDataIt; | ||||
} | } | ||||
Show All 10 Lines | BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) { | ||||
// The cache stack. | // The cache stack. | ||||
// A CCoinsViewTest at the bottom. | // A CCoinsViewTest at the bottom. | ||||
CCoinsViewTest base; | 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)); | ||||
// Track the txids we've used in various sets | // Track the unspentids we've used in various sets | ||||
std::set<COutPoint> coinbase_coins; | std::set<COutPoint> coinbase_coins; | ||||
std::set<COutPoint> disconnected_coins; | std::set<COutPoint> disconnected_coins; | ||||
std::set<COutPoint> duplicate_coins; | std::set<COutPoint> duplicate_coins; | ||||
std::set<COutPoint> utxoset; | std::set<COutPoint> utxoset; | ||||
for (int64_t i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | for (int64_t i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | ||||
uint32_t randiter = insecure_rand(); | uint32_t randiter = insecure_rand(); | ||||
Show All 17 Lines | for (int64_t i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | ||||
// Reuse the exact same coinbase | // Reuse the exact same coinbase | ||||
tx = std::get<0>(utxod->second); | tx = std::get<0>(utxod->second); | ||||
// shouldn't be available for reconnection if its been | // shouldn't be available for reconnection if its been | ||||
// duplicated | // duplicated | ||||
disconnected_coins.erase(utxod->first); | disconnected_coins.erase(utxod->first); | ||||
duplicate_coins.insert(utxod->first); | duplicate_coins.insert(utxod->first); | ||||
} else { | } else { | ||||
coinbase_coins.insert(COutPoint(tx.GetId(), 0)); | coinbase_coins.insert(COutPoint(tx.Getunspentid(), 0)); | ||||
} | } | ||||
assert(CTransaction(tx).IsCoinBase()); | assert(CTransaction(tx).IsCoinBase()); | ||||
} | } | ||||
// 17/20 times reconnect previous or add a regular tx | // 17/20 times reconnect previous or add a regular tx | ||||
else { | else { | ||||
COutPoint prevout; | COutPoint prevout; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | for (int64_t i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { | ||||
// will work properly if that ever happens and not resurrect the | // will work properly if that ever happens and not resurrect the | ||||
// previously overwritten coinbase | // previously overwritten coinbase | ||||
if (duplicate_coins.count(prevout)) { | if (duplicate_coins.count(prevout)) { | ||||
spent_a_duplicate_coinbase = true; | spent_a_duplicate_coinbase = true; | ||||
} | } | ||||
} | } | ||||
// Update the expected result to know about the new output coins | // Update the expected result to know about the new output coins | ||||
assert(tx.vout.size() == 1); | assert(tx.vout.size() == 1); | ||||
const COutPoint outpoint(tx.GetId(), 0); | const COutPoint outpoint(tx.Getunspentid(), 0); | ||||
result[outpoint] = | result[outpoint] = | ||||
Coin(tx.vout[0], height, CTransaction(tx).IsCoinBase()); | Coin(tx.vout[0], height, CTransaction(tx).IsCoinBase()); | ||||
// Call UpdateCoins on the top cache | // Call UpdateCoins on the top cache | ||||
CTxUndo undo; | CTxUndo undo; | ||||
UpdateCoins(tx, *(stack.back()), undo, height); | UpdateCoins(tx, *(stack.back()), undo, height); | ||||
// Update the utxo set for future spends | // Update the utxo set for future spends | ||||
▲ Show 20 Lines • Show All 506 Lines • Show Last 20 Lines |