diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2014-10-08 14:57:31 -0700 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2014-10-08 14:58:16 -0700 |
commit | d4a42334d447cad48fb3996cad0fd5c945b75571 (patch) | |
tree | ed1c616af0038cf7fb5a5b95e3c2dc5eddbf5fdc /src/test | |
parent | 97a34c28d568cb8b2171d8dd8eb12ca8afb08a7b (diff) | |
parent | 7c70438dc67547e83953ba0343a071fae304ce65 (diff) |
Merge pull request #4834
7c70438 Get rid of the dummy CCoinsViewCache constructor arg (Pieter Wuille)
ed27e53 Add coins_tests with a large randomized CCoinViewCache test. (Pieter Wuille)
058b08c Do not keep fully spent but unwritten CCoins entries cached. (Pieter Wuille)
c9d1a81 Get rid of CCoinsView's SetCoins and SetBestBlock. (Pieter Wuille)
f28aec0 Use ModifyCoins instead of mutable GetCoins. (Pieter Wuille)
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/coins_tests.cpp | 178 | ||||
-rw-r--r-- | src/test/script_P2SH_tests.cpp | 5 | ||||
-rw-r--r-- | src/test/test_bitcoin.cpp | 2 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 10 |
4 files changed, 186 insertions, 9 deletions
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp new file mode 100644 index 0000000000..3ecd301bc7 --- /dev/null +++ b/src/test/coins_tests.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "coins.h" +#include "random.h" +#include "uint256.h" + +#include <vector> +#include <map> + +#include <boost/test/unit_test.hpp> + +namespace +{ +class CCoinsViewTest : public CCoinsView +{ + uint256 hashBestBlock_; + std::map<uint256, CCoins> map_; + +public: + bool GetCoins(const uint256& txid, CCoins& coins) const + { + std::map<uint256, CCoins>::const_iterator it = map_.find(txid); + if (it == map_.end()) { + return false; + } + coins = it->second; + if (coins.IsPruned() && insecure_rand() % 2 == 0) { + // Randomly return false in case of an empty entry. + return false; + } + return true; + } + + bool HaveCoins(const uint256& txid) const + { + CCoins coins; + return GetCoins(txid, coins); + } + + uint256 GetBestBlock() const { return hashBestBlock_; } + + bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) + { + for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) { + map_[it->first] = it->second.coins; + if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) { + // Randomly delete empty entries on write. + map_.erase(it->first); + } + mapCoins.erase(it++); + } + mapCoins.clear(); + hashBestBlock_ = hashBlock; + return true; + } + + bool GetStats(CCoinsStats& stats) const { return false; } +}; +} + +BOOST_AUTO_TEST_SUITE(coins_tests) + +static const unsigned int NUM_SIMULATION_ITERATIONS = 40000; + +// This is a large randomized insert/remove simulation test on a variable-size +// stack of caches on top of CCoinsViewTest. +// +// It will randomly create/update/delete CCoins entries to a tip of caches, with +// 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. +// +// During the process, booleans are kept to make sure that the randomized +// operation hits all branches. +BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) +{ + // Various coverage trackers. + bool removed_all_caches = false; + bool reached_4_caches = false; + bool added_an_entry = false; + bool removed_an_entry = false; + bool updated_an_entry = false; + bool found_an_entry = false; + bool missed_an_entry = false; + + // A simple map to track what we expect the cache stack to represent. + std::map<uint256, CCoins> result; + + // The cache stack. + CCoinsViewTest base; // A CCoinsViewTest at the bottom. + std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top. + stack.push_back(new CCoinsViewCache(&base)); // Start with one cache. + + // Use a limited set of random transaction ids, so we do test overwriting entries. + std::vector<uint256> txids; + txids.resize(NUM_SIMULATION_ITERATIONS / 8); + for (unsigned int i = 0; i < txids.size(); i++) { + txids[i] = GetRandHash(); + } + + for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { + // Do a random modification. + { + uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. + CCoins& coins = result[txid]; + CCoinsModifier entry = stack.back()->ModifyCoins(txid); + BOOST_CHECK(coins == *entry); + if (insecure_rand() % 5 == 0 || coins.IsPruned()) { + if (coins.IsPruned()) { + added_an_entry = true; + } else { + updated_an_entry = true; + } + coins.nVersion = insecure_rand(); + coins.vout.resize(1); + coins.vout[0].nValue = insecure_rand(); + *entry = coins; + } else { + coins.Clear(); + entry->Clear(); + removed_an_entry = true; + } + } + + // Once every 1000 iterations and at the end, verify the full cache. + if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) { + const CCoins* coins = stack.back()->AccessCoins(it->first); + if (coins) { + BOOST_CHECK(*coins == it->second); + found_an_entry = true; + } else { + BOOST_CHECK(it->second.IsPruned()); + missed_an_entry = true; + } + } + } + + if (insecure_rand() % 100 == 0) { + // Every 100 iterations, change the cache stack. + if (stack.size() > 0 && insecure_rand() % 2 == 0) { + stack.back()->Flush(); + delete stack.back(); + stack.pop_back(); + } + if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + CCoinsView* tip = &base; + if (stack.size() > 0) { + tip = stack.back(); + } else { + removed_all_caches = true; + } + stack.push_back(new CCoinsViewCache(tip)); + if (stack.size() == 4) { + reached_4_caches = true; + } + } + } + } + + // Clean up the stack. + while (stack.size() > 0) { + delete stack.back(); + stack.pop_back(); + } + + // Verify coverage. + BOOST_CHECK(removed_all_caches); + BOOST_CHECK(reached_4_caches); + BOOST_CHECK(added_an_entry); + BOOST_CHECK(removed_an_entry); + BOOST_CHECK(updated_an_entry); + BOOST_CHECK(found_an_entry); + BOOST_CHECK(missed_an_entry); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 6c32a263a9..f8361a0dc8 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) { LOCK(cs_main); CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); CBasicKeyStore keystore; CKey key[6]; vector<CPubKey> keys; @@ -312,8 +312,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txFrom.vout[6].scriptPubKey = GetScriptForDestination(twentySigops.GetID()); txFrom.vout[6].nValue = 6000; - - coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); + coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0); CMutableTransaction txTo; txTo.vout.resize(1); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 6e5f0e3fac..afd63d2717 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -41,7 +41,7 @@ struct TestingSetup { mapArgs["-datadir"] = pathTemp.string(); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); - pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(); #ifdef ENABLE_WALLET bool fFirstRun; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 823afa1680..f14f600bbe 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) // paid to a TX_PUBKEYHASH. // static std::vector<CMutableTransaction> -SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) +SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) { std::vector<CMutableTransaction> dummyTransactions; dummyTransactions.resize(2); @@ -261,14 +261,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG; dummyTransactions[0].vout[1].nValue = 50*CENT; dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG; - coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0)); + coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0); dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21*CENT; dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); dummyTransactions[1].vout[1].nValue = 22*CENT; dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); - coinsRet.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0)); + coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0); return dummyTransactions; } @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(test_Get) { CBasicKeyStore keystore; CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CMutableTransaction t1; @@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) LOCK(cs_main); CBasicKeyStore keystore; CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CMutableTransaction t; |