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 Coin 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 th
| 116 | // otherwise we'll hit an assertion in BatchWrite. |
| 117 | // |
| 118 | void SimulationTest(CCoinsView* base, bool fake_best_block) |
| 119 | { |
| 120 | // Various coverage trackers. |
| 121 | bool removed_all_caches = false; |
| 122 | bool reached_4_caches = false; |
| 123 | bool added_an_entry = false; |
| 124 | bool added_an_unspendable_entry = false; |
| 125 | bool removed_an_entry = false; |
| 126 | bool updated_an_entry = false; |
| 127 | bool found_an_entry = false; |
| 128 | bool missed_an_entry = false; |
| 129 | bool uncached_an_entry = false; |
| 130 | |
| 131 | // A simple map to track what we expect the cache stack to represent. |
| 132 | std::map<COutPoint, Coin> result; |
| 133 | |
| 134 | // The cache stack. |
| 135 | std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top. |
| 136 | stack.push_back(new CCoinsViewCacheTest(base)); // Start with one cache. |
| 137 | |
| 138 | // Use a limited set of random transaction ids, so we do test overwriting entries. |
| 139 | std::vector<uint256> txids; |
| 140 | txids.resize(NUM_SIMULATION_ITERATIONS / 8); |
| 141 | for (unsigned int i = 0; i < txids.size(); i++) { |
| 142 | txids[i] = InsecureRand256(); |
| 143 | } |
| 144 | |
| 145 | for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { |
| 146 | // Do a random modification. |
| 147 | { |
| 148 | uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration. |
| 149 | Coin& coin = result[COutPoint(txid, 0)]; |
| 150 | |
| 151 | // Determine whether to test HaveCoin before or after Access* (or both). As these functions |
| 152 | // can influence each other's behaviour by pulling things into the cache, all combinations |
| 153 | // are tested. |
| 154 | bool test_havecoin_before = InsecureRandBits(2) == 0; |
| 155 | bool test_havecoin_after = InsecureRandBits(2) == 0; |
| 156 | |
| 157 | bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false; |
| 158 | const Coin& entry = (InsecureRandRange(500) == 0) ? AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0)); |
| 159 | BOOST_CHECK(coin == entry); |
| 160 | BOOST_CHECK(!test_havecoin_before || result_havecoin == !entry.IsSpent()); |
| 161 | |
| 162 | if (test_havecoin_after) { |
| 163 | bool ret = stack.back()->HaveCoin(COutPoint(txid, 0)); |
| 164 | BOOST_CHECK(ret == !entry.IsSpent()); |
| 165 | } |
| 166 | |
| 167 | if (InsecureRandRange(5) == 0 || coin.IsSpent()) { |
| 168 | Coin newcoin; |
| 169 | newcoin.out.nValue = InsecureRand32(); |
| 170 | newcoin.nHeight = 1; |
| 171 | if (InsecureRandRange(16) == 0 && coin.IsSpent()) { |
| 172 | newcoin.out.scriptPubKey.assign(1 + InsecureRandBits(6), OP_RETURN); |
| 173 | BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable()); |
| 174 | added_an_unspendable_entry = true; |
| 175 | } else { |
no test coverage detected