1 // Copyright (c) 2014 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.
12 #include <boost/test/unit_test.hpp>
16 class CCoinsViewTest : public CCoinsView
18 uint256 hashBestBlock_;
19 std::map<uint256, CCoins> map_;
22 bool GetCoins(const uint256& txid, CCoins& coins) const
24 std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
25 if (it == map_.end()) {
29 if (coins.IsPruned() && insecure_rand() % 2 == 0) {
30 // Randomly return false in case of an empty entry.
36 bool HaveCoins(const uint256& txid) const
39 return GetCoins(txid, coins);
42 uint256 GetBestBlock() const { return hashBestBlock_; }
44 bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock)
46 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
47 map_[it->first] = it->second.coins;
48 if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) {
49 // Randomly delete empty entries on write.
50 map_.erase(it->first);
55 hashBestBlock_ = hashBlock;
59 bool GetStats(CCoinsStats& stats) const { return false; }
63 BOOST_AUTO_TEST_SUITE(coins_tests)
65 static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
67 // This is a large randomized insert/remove simulation test on a variable-size
68 // stack of caches on top of CCoinsViewTest.
70 // It will randomly create/update/delete CCoins entries to a tip of caches, with
71 // txids picked from a limited list of random 256-bit hashes. Occasionally, a
72 // new tip is added to the stack of caches, or the tip is flushed and removed.
74 // During the process, booleans are kept to make sure that the randomized
75 // operation hits all branches.
76 BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
78 // Various coverage trackers.
79 bool removed_all_caches = false;
80 bool reached_4_caches = false;
81 bool added_an_entry = false;
82 bool removed_an_entry = false;
83 bool updated_an_entry = false;
84 bool found_an_entry = false;
85 bool missed_an_entry = false;
87 // A simple map to track what we expect the cache stack to represent.
88 std::map<uint256, CCoins> result;
91 CCoinsViewTest base; // A CCoinsViewTest at the bottom.
92 std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top.
93 stack.push_back(new CCoinsViewCache(&base)); // Start with one cache.
95 // Use a limited set of random transaction ids, so we do test overwriting entries.
96 std::vector<uint256> txids;
97 txids.resize(NUM_SIMULATION_ITERATIONS / 8);
98 for (unsigned int i = 0; i < txids.size(); i++) {
99 txids[i] = GetRandHash();
102 for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
103 // Do a random modification.
105 uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration.
106 CCoins& coins = result[txid];
107 CCoinsModifier entry = stack.back()->ModifyCoins(txid);
108 BOOST_CHECK(coins == *entry);
109 if (insecure_rand() % 5 == 0 || coins.IsPruned()) {
110 if (coins.IsPruned()) {
111 added_an_entry = true;
113 updated_an_entry = true;
115 coins.nVersion = insecure_rand();
116 coins.vout.resize(1);
117 coins.vout[0].nValue = insecure_rand();
122 removed_an_entry = true;
126 // Once every 1000 iterations and at the end, verify the full cache.
127 if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
128 for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
129 const CCoins* coins = stack.back()->AccessCoins(it->first);
131 BOOST_CHECK(*coins == it->second);
132 found_an_entry = true;
134 BOOST_CHECK(it->second.IsPruned());
135 missed_an_entry = true;
140 if (insecure_rand() % 100 == 0) {
141 // Every 100 iterations, change the cache stack.
142 if (stack.size() > 0 && insecure_rand() % 2 == 0) {
143 stack.back()->Flush();
147 if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
148 CCoinsView* tip = &base;
149 if (stack.size() > 0) {
152 removed_all_caches = true;
154 stack.push_back(new CCoinsViewCache(tip));
155 if (stack.size() == 4) {
156 reached_4_caches = true;
162 // Clean up the stack.
163 while (stack.size() > 0) {
169 BOOST_CHECK(removed_all_caches);
170 BOOST_CHECK(reached_4_caches);
171 BOOST_CHECK(added_an_entry);
172 BOOST_CHECK(removed_an_entry);
173 BOOST_CHECK(updated_an_entry);
174 BOOST_CHECK(found_an_entry);
175 BOOST_CHECK(missed_an_entry);
178 BOOST_AUTO_TEST_SUITE_END()