Line data Source code
1 : // Copyright (c) 2014-2021 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.
4 :
5 : #include <clientversion.h>
6 : #include <coins.h>
7 : #include <script/standard.h>
8 : #include <streams.h>
9 : #include <test/util/poolresourcetester.h>
10 : #include <test/util/random.h>
11 : #include <test/util/setup_common.h>
12 : #include <txdb.h>
13 : #include <uint256.h>
14 : #include <undo.h>
15 : #include <util/strencodings.h>
16 :
17 : #include <map>
18 : #include <vector>
19 :
20 : #include <boost/test/unit_test.hpp>
21 :
22 : int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out);
23 : void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight);
24 :
25 : namespace
26 : {
27 : //! equality test
28 1322558 : bool operator==(const Coin &a, const Coin &b) {
29 : // Empty Coin objects are always equal.
30 1322558 : if (a.IsSpent() && b.IsSpent()) return true;
31 695212 : return a.fCoinBase == b.fCoinBase &&
32 347606 : a.nHeight == b.nHeight &&
33 347606 : a.out == b.out;
34 1322558 : }
35 :
36 2 : class CCoinsViewTest : public CCoinsView
37 : {
38 : uint256 hashBestBlock_;
39 : std::map<COutPoint, Coin> map_;
40 :
41 : public:
42 10438812 : [[nodiscard]] bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
43 : {
44 10438812 : std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
45 10438812 : if (it == map_.end()) {
46 9871517 : return false;
47 : }
48 567295 : coin = it->second;
49 567295 : if (coin.IsSpent() && InsecureRandBool() == 0) {
50 : // Randomly return false in case of an empty entry.
51 216802 : return false;
52 : }
53 350493 : return true;
54 10438812 : }
55 :
56 0 : uint256 GetBestBlock() const override { return hashBestBlock_; }
57 :
58 257 : bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool erase = true) override
59 : {
60 413881 : for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = erase ? mapCoins.erase(it) : std::next(it)) {
61 413624 : if (it->second.flags & CCoinsCacheEntry::DIRTY) {
62 : // Same optimization used in CCoinsViewDB is to only write dirty entries.
63 82239 : map_[it->first] = it->second.coin;
64 82239 : if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
65 : // Randomly delete empty entries on write.
66 12237 : map_.erase(it->first);
67 12237 : }
68 82239 : }
69 413624 : }
70 257 : if (!hashBlock.IsNull())
71 0 : hashBestBlock_ = hashBlock;
72 257 : return true;
73 : }
74 : };
75 :
76 : class CCoinsViewCacheTest : public CCoinsViewCache
77 : {
78 : public:
79 2062 : explicit CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {}
80 :
81 398 : void SelfTest() const
82 : {
83 : // Manually recompute the dynamic usage of the whole data, and compare it.
84 398 : size_t ret = memusage::DynamicUsage(cacheCoins);
85 398 : size_t count = 0;
86 560994 : for (const auto& entry : cacheCoins) {
87 560596 : ret += entry.second.coin.DynamicMemoryUsage();
88 560596 : ++count;
89 : }
90 398 : BOOST_CHECK_EQUAL(GetCacheSize(), count);
91 398 : BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
92 398 : }
93 :
94 410 : CCoinsMap& map() const { return cacheCoins; }
95 198 : size_t& usage() const { return cachedCoinsUsage; }
96 : };
97 :
98 : } // namespace
99 :
100 146 : BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
101 :
102 : static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
103 :
104 : // This is a large randomized insert/remove simulation test on a variable-size
105 : // stack of caches on top of CCoinsViewTest.
106 : //
107 : // It will randomly create/update/delete Coin entries to a tip of caches, with
108 : // txids picked from a limited list of random 256-bit hashes. Occasionally, a
109 : // new tip is added to the stack of caches, or the tip is flushed and removed.
110 : //
111 : // During the process, booleans are kept to make sure that the randomized
112 : // operation hits all branches.
113 : //
114 : // If fake_best_block is true, assign a random uint256 to mock the recording
115 : // of best block on flush. This is necessary when using CCoinsViewDB as the base,
116 : // otherwise we'll hit an assertion in BatchWrite.
117 : //
118 2 : void SimulationTest(CCoinsView* base, bool fake_best_block)
119 : {
120 : // Various coverage trackers.
121 2 : bool removed_all_caches = false;
122 2 : bool reached_4_caches = false;
123 2 : bool added_an_entry = false;
124 2 : bool added_an_unspendable_entry = false;
125 2 : bool removed_an_entry = false;
126 2 : bool updated_an_entry = false;
127 2 : bool found_an_entry = false;
128 2 : bool missed_an_entry = false;
129 2 : bool uncached_an_entry = false;
130 2 : bool flushed_without_erase = false;
131 :
132 : // A simple map to track what we expect the cache stack to represent.
133 2 : std::map<COutPoint, Coin> result;
134 :
135 : // The cache stack.
136 2 : std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top.
137 2 : stack.push_back(std::make_unique<CCoinsViewCacheTest>(base)); // Start with one cache.
138 :
139 : // Use a limited set of random transaction ids, so we do test overwriting entries.
140 2 : std::vector<uint256> txids;
141 2 : txids.resize(NUM_SIMULATION_ITERATIONS / 8);
142 10002 : for (unsigned int i = 0; i < txids.size(); i++) {
143 10000 : txids[i] = InsecureRand256();
144 10000 : }
145 :
146 80002 : for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
147 : // Do a random modification.
148 : {
149 80000 : uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration.
150 80000 : Coin& coin = result[COutPoint(txid, 0)];
151 :
152 : // Determine whether to test HaveCoin before or after Access* (or both). As these functions
153 : // can influence each other's behaviour by pulling things into the cache, all combinations
154 : // are tested.
155 80000 : bool test_havecoin_before = InsecureRandBits(2) == 0;
156 80000 : bool test_havecoin_after = InsecureRandBits(2) == 0;
157 :
158 80000 : bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false;
159 :
160 : // Infrequently, test usage of AccessByTxid instead of AccessCoin - the
161 : // former just delegates to the latter and returns the first unspent in a txn.
162 160000 : const Coin& entry = (InsecureRandRange(500) == 0) ?
163 80000 : AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0));
164 80000 : BOOST_CHECK(coin == entry);
165 :
166 80000 : if (test_havecoin_before) {
167 19977 : BOOST_CHECK(result_havecoin == !entry.IsSpent());
168 19977 : }
169 :
170 80000 : if (test_havecoin_after) {
171 19953 : bool ret = stack.back()->HaveCoin(COutPoint(txid, 0));
172 19953 : BOOST_CHECK(ret == !entry.IsSpent());
173 19953 : }
174 :
175 80000 : if (InsecureRandRange(5) == 0 || coin.IsSpent()) {
176 47956 : Coin newcoin;
177 47956 : newcoin.out.nValue = InsecureRandMoneyAmount();
178 47956 : newcoin.nHeight = 1;
179 :
180 : // Infrequently test adding unspendable coins.
181 47956 : if (InsecureRandRange(16) == 0 && coin.IsSpent()) {
182 2493 : newcoin.out.scriptPubKey.assign(1 + InsecureRandBits(6), OP_RETURN);
183 2493 : BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable());
184 2493 : added_an_unspendable_entry = true;
185 2493 : } else {
186 : // Random sizes so we can test memory usage accounting
187 45463 : newcoin.out.scriptPubKey.assign(InsecureRandBits(6), 0);
188 45463 : (coin.IsSpent() ? added_an_entry : updated_an_entry) = true;
189 45463 : coin = newcoin;
190 : }
191 47956 : bool is_overwrite = !coin.IsSpent() || InsecureRand32() & 1;
192 47956 : stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), is_overwrite);
193 47956 : } else {
194 : // Spend the coin.
195 32044 : removed_an_entry = true;
196 32044 : coin.Clear();
197 32044 : BOOST_CHECK(stack.back()->SpendCoin(COutPoint(txid, 0)));
198 : }
199 : }
200 :
201 : // Once every 10 iterations, remove a random entry from the cache
202 80000 : if (InsecureRandRange(10) == 0) {
203 8009 : COutPoint out(txids[InsecureRand32() % txids.size()], 0);
204 8009 : int cacheid = InsecureRand32() % stack.size();
205 8009 : stack[cacheid]->Uncache(out);
206 8009 : uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
207 8009 : }
208 :
209 : // Once every 1000 iterations and at the end, verify the full cache.
210 80000 : if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
211 360289 : for (const auto& entry : result) {
212 360208 : bool have = stack.back()->HaveCoin(entry.first);
213 360208 : const Coin& coin = stack.back()->AccessCoin(entry.first);
214 360208 : BOOST_CHECK(have == !coin.IsSpent());
215 360208 : BOOST_CHECK(coin == entry.second);
216 360208 : if (coin.IsSpent()) {
217 152972 : missed_an_entry = true;
218 152972 : } else {
219 207236 : BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
220 207236 : found_an_entry = true;
221 : }
222 : }
223 301 : for (const auto& test : stack) {
224 220 : test->SelfTest();
225 : }
226 81 : }
227 :
228 80000 : if (InsecureRandRange(100) == 0) {
229 : // Every 100 iterations, flush an intermediate cache
230 769 : if (stack.size() > 1 && InsecureRandBool() == 0) {
231 286 : unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
232 286 : if (fake_best_block) stack[flushIndex]->SetBestBlock(InsecureRand256());
233 286 : bool should_erase = InsecureRandRange(4) < 3;
234 286 : BOOST_CHECK(should_erase ? stack[flushIndex]->Flush() : stack[flushIndex]->Sync());
235 286 : flushed_without_erase |= !should_erase;
236 286 : }
237 769 : }
238 80000 : if (InsecureRandRange(100) == 0) {
239 : // Every 100 iterations, change the cache stack.
240 820 : if (stack.size() > 0 && InsecureRandBool() == 0) {
241 : //Remove the top cache
242 414 : if (fake_best_block) stack.back()->SetBestBlock(InsecureRand256());
243 414 : bool should_erase = InsecureRandRange(4) < 3;
244 414 : BOOST_CHECK(should_erase ? stack.back()->Flush() : stack.back()->Sync());
245 414 : flushed_without_erase |= !should_erase;
246 414 : stack.pop_back();
247 414 : }
248 820 : if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
249 : //Add a new cache
250 418 : CCoinsView* tip = base;
251 418 : if (stack.size() > 0) {
252 319 : tip = stack.back().get();
253 319 : } else {
254 99 : removed_all_caches = true;
255 : }
256 418 : stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
257 418 : if (stack.size() == 4) {
258 114 : reached_4_caches = true;
259 114 : }
260 418 : }
261 820 : }
262 80000 : }
263 :
264 : // Verify coverage.
265 2 : BOOST_CHECK(removed_all_caches);
266 2 : BOOST_CHECK(reached_4_caches);
267 2 : BOOST_CHECK(added_an_entry);
268 2 : BOOST_CHECK(added_an_unspendable_entry);
269 2 : BOOST_CHECK(removed_an_entry);
270 2 : BOOST_CHECK(updated_an_entry);
271 2 : BOOST_CHECK(found_an_entry);
272 2 : BOOST_CHECK(missed_an_entry);
273 2 : BOOST_CHECK(uncached_an_entry);
274 2 : BOOST_CHECK(flushed_without_erase);
275 2 : }
276 :
277 : // Run the above simulation for multiple base types.
278 149 : BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
279 : {
280 1 : CCoinsViewTest base;
281 1 : SimulationTest(&base, false);
282 :
283 1 : CCoinsViewDB db_base{"test", /*nCacheSize=*/1 << 23, /*fMemory=*/true, /*fWipe=*/false};
284 1 : SimulationTest(&db_base, true);
285 1 : }
286 :
287 : // Store of all necessary tx and undo data for next test
288 : typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData;
289 146 : UtxoData utxoData;
290 :
291 40215 : UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
292 40215 : assert(utxoSet.size());
293 40215 : auto utxoSetIt = utxoSet.lower_bound(COutPoint(InsecureRand256(), 0));
294 40215 : if (utxoSetIt == utxoSet.end()) {
295 288 : utxoSetIt = utxoSet.begin();
296 288 : }
297 40215 : auto utxoDataIt = utxoData.find(*utxoSetIt);
298 40215 : assert(utxoDataIt != utxoData.end());
299 40215 : return utxoDataIt;
300 : }
301 :
302 :
303 : // This test is similar to the previous test
304 : // except the emphasis is on testing the functionality of UpdateCoins
305 : // random txs are created and UpdateCoins is used to update the cache stack
306 : // In particular it is tested that spending a duplicate coinbase tx
307 : // has the expected effect (the other duplicate is overwritten at all cache levels)
308 149 : BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
309 : {
310 1 : SeedInsecureRand(SeedRand::ZEROS);
311 1 : g_mock_deterministic_tests = true;
312 :
313 1 : bool spent_a_duplicate_coinbase = false;
314 : // A simple map to track what we expect the cache stack to represent.
315 1 : std::map<COutPoint, Coin> result;
316 :
317 : // The cache stack.
318 1 : CCoinsViewTest base; // A CCoinsViewTest at the bottom.
319 1 : std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top.
320 1 : stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base)); // Start with one cache.
321 :
322 : // Track the txids we've used in various sets
323 1 : std::set<COutPoint> coinbase_coins;
324 1 : std::set<COutPoint> disconnected_coins;
325 1 : std::set<COutPoint> duplicate_coins;
326 1 : std::set<COutPoint> utxoset;
327 :
328 40001 : for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
329 40000 : uint32_t randiter = InsecureRand32();
330 :
331 : // 19/20 txs add a new transaction
332 40000 : if (randiter % 20 < 19) {
333 37962 : CMutableTransaction tx;
334 37962 : tx.vin.resize(1);
335 37962 : tx.vout.resize(1);
336 37962 : tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
337 37962 : tx.vout[0].scriptPubKey.assign(InsecureRand32() & 0x3F, 0); // Random sizes so we can test memory usage accounting
338 37962 : const int height{int(InsecureRand32() >> 1)};
339 37962 : Coin old_coin;
340 :
341 : // 2/20 times create a new coinbase
342 37962 : if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
343 : // 1/10 of those times create a duplicate coinbase
344 4081 : if (InsecureRandRange(10) == 0 && coinbase_coins.size()) {
345 399 : auto utxod = FindRandomFrom(coinbase_coins);
346 : // Reuse the exact same coinbase
347 399 : tx = CMutableTransaction{std::get<0>(utxod->second)};
348 : // shouldn't be available for reconnection if it's been duplicated
349 399 : disconnected_coins.erase(utxod->first);
350 :
351 399 : duplicate_coins.insert(utxod->first);
352 399 : }
353 : else {
354 3682 : coinbase_coins.insert(COutPoint(tx.GetHash(), 0));
355 : }
356 4081 : assert(CTransaction(tx).IsCoinBase());
357 4081 : }
358 :
359 : // 17/20 times reconnect previous or add a regular tx
360 : else {
361 :
362 33881 : COutPoint prevout;
363 : // 1/20 times reconnect a previously disconnected tx
364 33881 : if (randiter % 20 == 2 && disconnected_coins.size()) {
365 1970 : auto utxod = FindRandomFrom(disconnected_coins);
366 1970 : tx = CMutableTransaction{std::get<0>(utxod->second)};
367 1970 : prevout = tx.vin[0].prevout;
368 1970 : if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
369 695 : disconnected_coins.erase(utxod->first);
370 695 : continue;
371 : }
372 :
373 : // If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
374 1275 : if (utxoset.count(utxod->first)) {
375 0 : assert(CTransaction(tx).IsCoinBase());
376 0 : assert(duplicate_coins.count(utxod->first));
377 0 : }
378 1275 : disconnected_coins.erase(utxod->first);
379 1275 : }
380 :
381 : // 16/20 times create a regular tx
382 : else {
383 31911 : auto utxod = FindRandomFrom(utxoset);
384 31911 : prevout = utxod->first;
385 :
386 : // Construct the tx to spend the coins of prevouthash
387 31911 : tx.vin[0].prevout = prevout;
388 31911 : assert(!CTransaction(tx).IsCoinBase());
389 : }
390 : // In this simple test coins only have two states, spent or unspent, save the unspent state to restore
391 33186 : old_coin = result[prevout];
392 : // Update the expected result of prevouthash to know these coins are spent
393 33186 : result[prevout].Clear();
394 :
395 33186 : utxoset.erase(prevout);
396 :
397 : // The test is designed to ensure spending a duplicate coinbase will work properly
398 : // if that ever happens and not resurrect the previously overwritten coinbase
399 33186 : if (duplicate_coins.count(prevout)) {
400 367 : spent_a_duplicate_coinbase = true;
401 367 : }
402 :
403 : }
404 : // Update the expected result to know about the new output coins
405 37267 : assert(tx.vout.size() == 1);
406 37267 : const COutPoint outpoint(tx.GetHash(), 0);
407 37267 : result[outpoint] = Coin{tx.vout[0], int(height), CTransaction{tx}.IsCoinBase()};
408 :
409 : // Call UpdateCoins on the top cache
410 37267 : CTxUndo undo;
411 37267 : UpdateCoins(CTransaction{tx}, *(stack.back()), undo, int(height));
412 :
413 : // Update the utxo set for future spends
414 37267 : utxoset.insert(outpoint);
415 :
416 : // Track this tx and undo info to use later
417 37267 : utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
418 40000 : } else if (utxoset.size()) {
419 : //1/20 times undo a previous transaction
420 2038 : auto utxod = FindRandomFrom(utxoset);
421 :
422 2038 : CTransaction &tx = std::get<0>(utxod->second);
423 2038 : CTxUndo &undo = std::get<1>(utxod->second);
424 2038 : Coin &orig_coin = std::get<2>(utxod->second);
425 :
426 : // Update the expected result
427 : // Remove new outputs
428 2038 : result[utxod->first].Clear();
429 : // If not coinbase restore prevout
430 2038 : if (!tx.IsCoinBase()) {
431 1833 : result[tx.vin[0].prevout] = orig_coin;
432 1833 : }
433 :
434 : // Disconnect the tx from the current UTXO
435 : // See code in DisconnectBlock
436 : // remove outputs
437 2038 : BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
438 : // restore inputs
439 2038 : if (!tx.IsCoinBase()) {
440 1833 : const COutPoint &out = tx.vin[0].prevout;
441 1833 : Coin coin = undo.vprevout[0];
442 1833 : ApplyTxInUndo(std::move(coin), *(stack.back()), out);
443 1833 : }
444 : // Store as a candidate for reconnection
445 2038 : disconnected_coins.insert(utxod->first);
446 :
447 : // Update the utxoset
448 2038 : utxoset.erase(utxod->first);
449 2038 : if (!tx.IsCoinBase())
450 1833 : utxoset.insert(tx.vin[0].prevout);
451 2038 : }
452 :
453 : // Once every 1000 iterations and at the end, verify the full cache.
454 39305 : if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
455 882396 : for (const auto& entry : result) {
456 882350 : bool have = stack.back()->HaveCoin(entry.first);
457 882350 : const Coin& coin = stack.back()->AccessCoin(entry.first);
458 882350 : BOOST_CHECK(have == !coin.IsSpent());
459 882350 : BOOST_CHECK(coin == entry.second);
460 : }
461 46 : }
462 :
463 : // One every 10 iterations, remove a random entry from the cache
464 39305 : if (utxoset.size() > 1 && InsecureRandRange(30) == 0) {
465 1360 : stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first);
466 1360 : }
467 39305 : if (disconnected_coins.size() > 1 && InsecureRandRange(30) == 0) {
468 1272 : stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(disconnected_coins)->first);
469 1272 : }
470 39305 : if (duplicate_coins.size() > 1 && InsecureRandRange(30) == 0) {
471 1265 : stack[InsecureRand32() % stack.size()]->Uncache(FindRandomFrom(duplicate_coins)->first);
472 1265 : }
473 :
474 39305 : if (InsecureRandRange(100) == 0) {
475 : // Every 100 iterations, flush an intermediate cache
476 378 : if (stack.size() > 1 && InsecureRandBool() == 0) {
477 154 : unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
478 154 : BOOST_CHECK(stack[flushIndex]->Flush());
479 154 : }
480 378 : }
481 39305 : if (InsecureRandRange(100) == 0) {
482 : // Every 100 iterations, change the cache stack.
483 419 : if (stack.size() > 0 && InsecureRandBool() == 0) {
484 211 : BOOST_CHECK(stack.back()->Flush());
485 211 : stack.pop_back();
486 211 : }
487 419 : if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
488 212 : CCoinsView* tip = &base;
489 212 : if (stack.size() > 0) {
490 176 : tip = stack.back().get();
491 176 : }
492 212 : stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
493 212 : }
494 419 : }
495 39305 : }
496 :
497 : // Verify coverage.
498 1 : BOOST_CHECK(spent_a_duplicate_coinbase);
499 :
500 1 : g_mock_deterministic_tests = false;
501 1 : }
502 :
503 149 : BOOST_AUTO_TEST_CASE(ccoins_serialization)
504 : {
505 : // Good example
506 1 : CDataStream ss1(ParseHex("97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"), SER_DISK, CLIENT_VERSION);
507 1 : Coin cc1;
508 1 : ss1 >> cc1;
509 1 : BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
510 1 : BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
511 1 : BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
512 1 : BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
513 :
514 : // Good example
515 1 : CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
516 1 : Coin cc2;
517 1 : ss2 >> cc2;
518 1 : BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
519 1 : BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
520 1 : BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
521 1 : BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
522 :
523 : // Smallest possible example
524 1 : CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
525 1 : Coin cc3;
526 1 : ss3 >> cc3;
527 1 : BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
528 1 : BOOST_CHECK_EQUAL(cc3.nHeight, 0U);
529 1 : BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
530 1 : BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U);
531 :
532 : // scriptPubKey that ends beyond the end of the stream
533 1 : CDataStream ss4(ParseHex("000007"), SER_DISK, CLIENT_VERSION);
534 : try {
535 1 : Coin cc4;
536 1 : ss4 >> cc4;
537 0 : BOOST_CHECK_MESSAGE(false, "We should have thrown");
538 1 : } catch (const std::ios_base::failure&) {
539 1 : }
540 :
541 : // Very large scriptPubKey (3*10^9 bytes) past the end of the stream
542 1 : CDataStream tmp(SER_DISK, CLIENT_VERSION);
543 1 : uint64_t x = 3000000000ULL;
544 1 : tmp << VARINT(x);
545 1 : BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
546 1 : CDataStream ss5(ParseHex("00008a95c0bb00"), SER_DISK, CLIENT_VERSION);
547 : try {
548 1 : Coin cc5;
549 1 : ss5 >> cc5;
550 0 : BOOST_CHECK_MESSAGE(false, "We should have thrown");
551 1 : } catch (const std::ios_base::failure&) {
552 1 : }
553 3 : }
554 :
555 146 : const static COutPoint OUTPOINT;
556 : const static CAmount SPENT = -1;
557 : const static CAmount ABSENT = -2;
558 : const static CAmount FAIL = -3;
559 : const static CAmount VALUE1 = 100;
560 : const static CAmount VALUE2 = 200;
561 : const static CAmount VALUE3 = 300;
562 : const static char DIRTY = CCoinsCacheEntry::DIRTY;
563 : const static char FRESH = CCoinsCacheEntry::FRESH;
564 : const static char NO_ENTRY = -1;
565 :
566 : const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
567 : const static auto CLEAN_FLAGS = {char(0), FRESH};
568 : const static auto ABSENT_FLAGS = {NO_ENTRY};
569 :
570 320 : static void SetCoinsValue(CAmount value, Coin& coin)
571 : {
572 320 : assert(value != ABSENT);
573 320 : coin.Clear();
574 320 : assert(coin.IsSpent());
575 320 : if (value != SPENT) {
576 160 : coin.out.nValue = value;
577 160 : coin.nHeight = 1;
578 160 : assert(!coin.IsSpent());
579 160 : }
580 320 : }
581 :
582 486 : static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
583 : {
584 486 : if (value == ABSENT) {
585 166 : assert(flags == NO_ENTRY);
586 166 : return 0;
587 : }
588 320 : assert(flags != NO_ENTRY);
589 320 : CCoinsCacheEntry entry;
590 320 : entry.flags = flags;
591 320 : SetCoinsValue(value, entry.coin);
592 320 : auto inserted = map.emplace(OUTPOINT, std::move(entry));
593 320 : assert(inserted.second);
594 320 : return inserted.first->second.coin.DynamicMemoryUsage();
595 486 : }
596 :
597 202 : void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags, const COutPoint& outp = OUTPOINT)
598 : {
599 202 : auto it = map.find(outp);
600 202 : if (it == map.end()) {
601 35 : value = ABSENT;
602 35 : flags = NO_ENTRY;
603 35 : } else {
604 167 : if (it->second.coin.IsSpent()) {
605 60 : value = SPENT;
606 60 : } else {
607 107 : value = it->second.coin.out.nValue;
608 : }
609 167 : flags = it->second.flags;
610 167 : assert(flags != NO_ENTRY);
611 : }
612 202 : }
613 :
614 288 : void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
615 : {
616 288 : CCoinsMapMemoryResource resource;
617 288 : CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
618 288 : InsertCoinsMapEntry(map, value, flags);
619 288 : BOOST_CHECK(view.BatchWrite(map, {}));
620 288 : }
621 :
622 : class SingleEntryCacheTest
623 : {
624 : public:
625 396 : SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
626 198 : {
627 198 : WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
628 198 : cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
629 396 : }
630 :
631 : CCoinsView root;
632 198 : CCoinsViewCacheTest base{&root};
633 198 : CCoinsViewCacheTest cache{&base};
634 : };
635 :
636 27 : static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
637 : {
638 27 : SingleEntryCacheTest test(base_value, cache_value, cache_flags);
639 27 : test.cache.AccessCoin(OUTPOINT);
640 27 : test.cache.SelfTest();
641 :
642 : CAmount result_value;
643 : char result_flags;
644 27 : GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
645 27 : BOOST_CHECK_EQUAL(result_value, expected_value);
646 27 : BOOST_CHECK_EQUAL(result_flags, expected_flags);
647 27 : }
648 :
649 149 : BOOST_AUTO_TEST_CASE(ccoins_access)
650 : {
651 : /* Check AccessCoin behavior, requesting a coin from a cache view layered on
652 : * top of a base view, and checking the resulting entry in the cache after
653 : * the access.
654 : *
655 : * Base Cache Result Cache Result
656 : * Value Value Value Flags Flags
657 : */
658 1 : CheckAccessCoin(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
659 1 : CheckAccessCoin(ABSENT, SPENT , SPENT , 0 , 0 );
660 1 : CheckAccessCoin(ABSENT, SPENT , SPENT , FRESH , FRESH );
661 1 : CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY , DIRTY );
662 1 : CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
663 1 : CheckAccessCoin(ABSENT, VALUE2, VALUE2, 0 , 0 );
664 1 : CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH , FRESH );
665 1 : CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY );
666 1 : CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
667 1 : CheckAccessCoin(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
668 1 : CheckAccessCoin(SPENT , SPENT , SPENT , 0 , 0 );
669 1 : CheckAccessCoin(SPENT , SPENT , SPENT , FRESH , FRESH );
670 1 : CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY , DIRTY );
671 1 : CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
672 1 : CheckAccessCoin(SPENT , VALUE2, VALUE2, 0 , 0 );
673 1 : CheckAccessCoin(SPENT , VALUE2, VALUE2, FRESH , FRESH );
674 1 : CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY , DIRTY );
675 1 : CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
676 1 : CheckAccessCoin(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 );
677 1 : CheckAccessCoin(VALUE1, SPENT , SPENT , 0 , 0 );
678 1 : CheckAccessCoin(VALUE1, SPENT , SPENT , FRESH , FRESH );
679 1 : CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY , DIRTY );
680 1 : CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
681 1 : CheckAccessCoin(VALUE1, VALUE2, VALUE2, 0 , 0 );
682 1 : CheckAccessCoin(VALUE1, VALUE2, VALUE2, FRESH , FRESH );
683 1 : CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY );
684 1 : CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
685 1 : }
686 :
687 27 : static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
688 : {
689 27 : SingleEntryCacheTest test(base_value, cache_value, cache_flags);
690 27 : test.cache.SpendCoin(OUTPOINT);
691 27 : test.cache.SelfTest();
692 :
693 : CAmount result_value;
694 : char result_flags;
695 27 : GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
696 27 : BOOST_CHECK_EQUAL(result_value, expected_value);
697 27 : BOOST_CHECK_EQUAL(result_flags, expected_flags);
698 27 : };
699 :
700 149 : BOOST_AUTO_TEST_CASE(ccoins_spend)
701 : {
702 : /* Check SpendCoin behavior, requesting a coin from a cache view layered on
703 : * top of a base view, spending, and then checking
704 : * the resulting entry in the cache after the modification.
705 : *
706 : * Base Cache Result Cache Result
707 : * Value Value Value Flags Flags
708 : */
709 1 : CheckSpendCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
710 1 : CheckSpendCoins(ABSENT, SPENT , SPENT , 0 , DIRTY );
711 1 : CheckSpendCoins(ABSENT, SPENT , ABSENT, FRESH , NO_ENTRY );
712 1 : CheckSpendCoins(ABSENT, SPENT , SPENT , DIRTY , DIRTY );
713 1 : CheckSpendCoins(ABSENT, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
714 1 : CheckSpendCoins(ABSENT, VALUE2, SPENT , 0 , DIRTY );
715 1 : CheckSpendCoins(ABSENT, VALUE2, ABSENT, FRESH , NO_ENTRY );
716 1 : CheckSpendCoins(ABSENT, VALUE2, SPENT , DIRTY , DIRTY );
717 1 : CheckSpendCoins(ABSENT, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
718 1 : CheckSpendCoins(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
719 1 : CheckSpendCoins(SPENT , SPENT , SPENT , 0 , DIRTY );
720 1 : CheckSpendCoins(SPENT , SPENT , ABSENT, FRESH , NO_ENTRY );
721 1 : CheckSpendCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY );
722 1 : CheckSpendCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
723 1 : CheckSpendCoins(SPENT , VALUE2, SPENT , 0 , DIRTY );
724 1 : CheckSpendCoins(SPENT , VALUE2, ABSENT, FRESH , NO_ENTRY );
725 1 : CheckSpendCoins(SPENT , VALUE2, SPENT , DIRTY , DIRTY );
726 1 : CheckSpendCoins(SPENT , VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
727 1 : CheckSpendCoins(VALUE1, ABSENT, SPENT , NO_ENTRY , DIRTY );
728 1 : CheckSpendCoins(VALUE1, SPENT , SPENT , 0 , DIRTY );
729 1 : CheckSpendCoins(VALUE1, SPENT , ABSENT, FRESH , NO_ENTRY );
730 1 : CheckSpendCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY );
731 1 : CheckSpendCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
732 1 : CheckSpendCoins(VALUE1, VALUE2, SPENT , 0 , DIRTY );
733 1 : CheckSpendCoins(VALUE1, VALUE2, ABSENT, FRESH , NO_ENTRY );
734 1 : CheckSpendCoins(VALUE1, VALUE2, SPENT , DIRTY , DIRTY );
735 1 : CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
736 1 : }
737 :
738 54 : static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
739 : {
740 54 : SingleEntryCacheTest test(base_value, cache_value, cache_flags);
741 :
742 : CAmount result_value;
743 : char result_flags;
744 : try {
745 54 : CTxOut output;
746 54 : output.nValue = modify_value;
747 54 : test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase);
748 42 : test.cache.SelfTest();
749 42 : GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
750 54 : } catch (std::logic_error&) {
751 12 : result_value = FAIL;
752 12 : result_flags = NO_ENTRY;
753 12 : }
754 :
755 54 : BOOST_CHECK_EQUAL(result_value, expected_value);
756 54 : BOOST_CHECK_EQUAL(result_flags, expected_flags);
757 66 : }
758 :
759 : // Simple wrapper for CheckAddCoinBase function above that loops through
760 : // different possible base_values, making sure each one gives the same results.
761 : // This wrapper lets the coins_add test below be shorter and less repetitive,
762 : // while still verifying that the CoinsViewCache::AddCoin implementation
763 : // ignores base values.
764 : template <typename... Args>
765 18 : static void CheckAddCoin(const Args&... args)
766 : {
767 72 : for (const CAmount base_value : {ABSENT, SPENT, VALUE1})
768 54 : CheckAddCoinBase(base_value, args...);
769 18 : }
770 :
771 149 : BOOST_AUTO_TEST_CASE(ccoins_add)
772 : {
773 : /* Check AddCoin behavior, requesting a new coin from a cache view,
774 : * writing a modification to the coin, and then checking the resulting
775 : * entry in the cache after the modification. Verify behavior with the
776 : * AddCoin possible_overwrite argument set to false, and to true.
777 : *
778 : * Cache Write Result Cache Result possible_overwrite
779 : * Value Value Value Flags Flags
780 : */
781 1 : CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH, false);
782 1 : CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY , true );
783 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
784 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY , true );
785 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
786 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
787 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , false);
788 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , true );
789 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
790 1 : CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
791 1 : CheckAddCoin(VALUE2, VALUE3, FAIL , 0 , NO_ENTRY , false);
792 1 : CheckAddCoin(VALUE2, VALUE3, VALUE3, 0 , DIRTY , true );
793 1 : CheckAddCoin(VALUE2, VALUE3, FAIL , FRESH , NO_ENTRY , false);
794 1 : CheckAddCoin(VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
795 1 : CheckAddCoin(VALUE2, VALUE3, FAIL , DIRTY , NO_ENTRY , false);
796 1 : CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY , DIRTY , true );
797 1 : CheckAddCoin(VALUE2, VALUE3, FAIL , DIRTY|FRESH, NO_ENTRY , false);
798 1 : CheckAddCoin(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
799 1 : }
800 :
801 90 : void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
802 : {
803 90 : SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
804 :
805 : CAmount result_value;
806 : char result_flags;
807 : try {
808 90 : WriteCoinsViewEntry(test.cache, child_value, child_flags);
809 82 : test.cache.SelfTest();
810 82 : GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
811 90 : } catch (std::logic_error&) {
812 8 : result_value = FAIL;
813 8 : result_flags = NO_ENTRY;
814 8 : }
815 :
816 90 : BOOST_CHECK_EQUAL(result_value, expected_value);
817 90 : BOOST_CHECK_EQUAL(result_flags, expected_flags);
818 98 : }
819 :
820 149 : BOOST_AUTO_TEST_CASE(ccoins_write)
821 : {
822 : /* Check BatchWrite behavior, flushing one entry from a child cache to a
823 : * parent cache, and checking the resulting entry in the parent cache
824 : * after the write.
825 : *
826 : * Parent Child Result Parent Child Result
827 : * Value Value Value Flags Flags Flags
828 : */
829 1 : CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY );
830 1 : CheckWriteCoins(ABSENT, SPENT , SPENT , NO_ENTRY , DIRTY , DIRTY );
831 1 : CheckWriteCoins(ABSENT, SPENT , ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY );
832 1 : CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY , DIRTY );
833 1 : CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH);
834 1 : CheckWriteCoins(SPENT , ABSENT, SPENT , 0 , NO_ENTRY , 0 );
835 1 : CheckWriteCoins(SPENT , ABSENT, SPENT , FRESH , NO_ENTRY , FRESH );
836 1 : CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY , NO_ENTRY , DIRTY );
837 1 : CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
838 1 : CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY , DIRTY );
839 1 : CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY|FRESH, DIRTY );
840 1 : CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY );
841 1 : CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
842 1 : CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY , DIRTY );
843 1 : CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY|FRESH, DIRTY );
844 1 : CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
845 1 : CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
846 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY , DIRTY );
847 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
848 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
849 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
850 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
851 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
852 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
853 1 : CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
854 1 : CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0 , NO_ENTRY , 0 );
855 1 : CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH , NO_ENTRY , FRESH );
856 1 : CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
857 1 : CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
858 1 : CheckWriteCoins(VALUE1, SPENT , SPENT , 0 , DIRTY , DIRTY );
859 1 : CheckWriteCoins(VALUE1, SPENT , FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
860 1 : CheckWriteCoins(VALUE1, SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY );
861 1 : CheckWriteCoins(VALUE1, SPENT , FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
862 1 : CheckWriteCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY , DIRTY );
863 1 : CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
864 1 : CheckWriteCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
865 1 : CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
866 1 : CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
867 1 : CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
868 1 : CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
869 1 : CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
870 1 : CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
871 1 : CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
872 1 : CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
873 1 : CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
874 :
875 : // The checks above omit cases where the child flags are not DIRTY, since
876 : // they would be too repetitive (the parent cache is never updated in these
877 : // cases). The loop below covers these cases and makes sure the parent cache
878 : // is always left unchanged.
879 4 : for (const CAmount parent_value : {ABSENT, SPENT, VALUE1})
880 12 : for (const CAmount child_value : {ABSENT, SPENT, VALUE2})
881 36 : for (const char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
882 72 : for (const char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
883 45 : CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
884 1 : }
885 :
886 :
887 12 : Coin MakeCoin()
888 : {
889 12 : Coin coin;
890 12 : coin.out.nValue = InsecureRand32();
891 12 : coin.nHeight = InsecureRandRange(4096);
892 12 : coin.fCoinBase = 0;
893 12 : return coin;
894 12 : }
895 :
896 :
897 : //! For CCoinsViewCache instances backed by either another cache instance or
898 : //! leveldb, test cache behavior and flag state (DIRTY/FRESH) by
899 : //!
900 : //! 1. Adding a random coin to the child-most cache,
901 : //! 2. Flushing all caches (without erasing),
902 : //! 3. Ensure the entry still exists in the cache and has been written to parent,
903 : //! 4. (if `do_erasing_flush`) Flushing the caches again (with erasing),
904 : //! 5. (if `do_erasing_flush`) Ensure the entry has been written to the parent and is no longer in the cache,
905 : //! 6. Spend the coin, ensure it no longer exists in the parent.
906 : //!
907 4 : void TestFlushBehavior(
908 : CCoinsViewCacheTest* view,
909 : CCoinsViewDB& base,
910 : std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,
911 : bool do_erasing_flush)
912 : {
913 : CAmount value;
914 : char flags;
915 : size_t cache_usage;
916 : size_t cache_size;
917 :
918 22 : auto flush_all = [&all_caches](bool erase) {
919 : // Flush in reverse order to ensure that flushes happen from children up.
920 54 : for (auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {
921 36 : auto& cache = *i;
922 : // hashBlock must be filled before flushing to disk; value is
923 : // unimportant here. This is normally done during connect/disconnect block.
924 36 : cache->SetBestBlock(InsecureRand256());
925 36 : erase ? cache->Flush() : cache->Sync();
926 36 : }
927 18 : };
928 :
929 4 : uint256 txid = InsecureRand256();
930 4 : COutPoint outp = COutPoint(txid, 0);
931 4 : Coin coin = MakeCoin();
932 : // Ensure the coins views haven't seen this coin before.
933 4 : BOOST_CHECK(!base.HaveCoin(outp));
934 4 : BOOST_CHECK(!view->HaveCoin(outp));
935 :
936 : // --- 1. Adding a random coin to the child cache
937 : //
938 4 : view->AddCoin(outp, Coin(coin), false);
939 :
940 4 : cache_usage = view->DynamicMemoryUsage();
941 4 : cache_size = view->map().size();
942 :
943 : // `base` shouldn't have coin (no flush yet) but `view` should have cached it.
944 4 : BOOST_CHECK(!base.HaveCoin(outp));
945 4 : BOOST_CHECK(view->HaveCoin(outp));
946 :
947 4 : GetCoinsMapEntry(view->map(), value, flags, outp);
948 4 : BOOST_CHECK_EQUAL(value, coin.out.nValue);
949 4 : BOOST_CHECK_EQUAL(flags, DIRTY|FRESH);
950 :
951 : // --- 2. Flushing all caches (without erasing)
952 : //
953 4 : flush_all(/*erase=*/ false);
954 :
955 : // CoinsMap usage should be unchanged since we didn't erase anything.
956 4 : BOOST_CHECK_EQUAL(cache_usage, view->DynamicMemoryUsage());
957 4 : BOOST_CHECK_EQUAL(cache_size, view->map().size());
958 :
959 : // --- 3. Ensuring the entry still exists in the cache and has been written to parent
960 : //
961 4 : GetCoinsMapEntry(view->map(), value, flags, outp);
962 4 : BOOST_CHECK_EQUAL(value, coin.out.nValue);
963 4 : BOOST_CHECK_EQUAL(flags, 0); // Flags should have been wiped.
964 :
965 : // Both views should now have the coin.
966 4 : BOOST_CHECK(base.HaveCoin(outp));
967 4 : BOOST_CHECK(view->HaveCoin(outp));
968 :
969 4 : if (do_erasing_flush) {
970 : // --- 4. Flushing the caches again (with erasing)
971 : //
972 2 : flush_all(/*erase=*/ true);
973 :
974 : // Memory does not necessarily go down due to the map using a memory pool
975 2 : BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage);
976 : // Size of the cache must go down though
977 2 : BOOST_TEST(view->map().size() < cache_size);
978 :
979 : // --- 5. Ensuring the entry is no longer in the cache
980 : //
981 2 : GetCoinsMapEntry(view->map(), value, flags, outp);
982 2 : BOOST_CHECK_EQUAL(value, ABSENT);
983 2 : BOOST_CHECK_EQUAL(flags, NO_ENTRY);
984 :
985 2 : view->AccessCoin(outp);
986 2 : GetCoinsMapEntry(view->map(), value, flags, outp);
987 2 : BOOST_CHECK_EQUAL(value, coin.out.nValue);
988 2 : BOOST_CHECK_EQUAL(flags, 0);
989 2 : }
990 :
991 : // Can't overwrite an entry without specifying that an overwrite is
992 : // expected.
993 4 : BOOST_CHECK_THROW(
994 : view->AddCoin(outp, Coin(coin), /*possible_overwrite=*/ false),
995 : std::logic_error);
996 :
997 : // --- 6. Spend the coin.
998 : //
999 4 : BOOST_CHECK(view->SpendCoin(outp));
1000 :
1001 : // The coin should be in the cache, but spent and marked dirty.
1002 4 : GetCoinsMapEntry(view->map(), value, flags, outp);
1003 4 : BOOST_CHECK_EQUAL(value, SPENT);
1004 4 : BOOST_CHECK_EQUAL(flags, DIRTY);
1005 4 : BOOST_CHECK(!view->HaveCoin(outp)); // Coin should be considered spent in `view`.
1006 4 : BOOST_CHECK(base.HaveCoin(outp)); // But coin should still be unspent in `base`.
1007 :
1008 4 : flush_all(/*erase=*/ false);
1009 :
1010 : // Coin should be considered spent in both views.
1011 4 : BOOST_CHECK(!view->HaveCoin(outp));
1012 4 : BOOST_CHECK(!base.HaveCoin(outp));
1013 :
1014 : // Spent coin should not be spendable.
1015 4 : BOOST_CHECK(!view->SpendCoin(outp));
1016 :
1017 : // --- Bonus check: ensure that a coin added to the base view via one cache
1018 : // can be spent by another cache which has never seen it.
1019 : //
1020 4 : txid = InsecureRand256();
1021 4 : outp = COutPoint(txid, 0);
1022 4 : coin = MakeCoin();
1023 4 : BOOST_CHECK(!base.HaveCoin(outp));
1024 4 : BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
1025 4 : BOOST_CHECK(!all_caches[1]->HaveCoin(outp));
1026 :
1027 4 : all_caches[0]->AddCoin(outp, std::move(coin), false);
1028 4 : all_caches[0]->Sync();
1029 4 : BOOST_CHECK(base.HaveCoin(outp));
1030 4 : BOOST_CHECK(all_caches[0]->HaveCoin(outp));
1031 4 : BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));
1032 :
1033 4 : BOOST_CHECK(all_caches[1]->SpendCoin(outp));
1034 4 : flush_all(/*erase=*/ false);
1035 4 : BOOST_CHECK(!base.HaveCoin(outp));
1036 4 : BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
1037 4 : BOOST_CHECK(!all_caches[1]->HaveCoin(outp));
1038 :
1039 4 : flush_all(/*erase=*/ true); // Erase all cache content.
1040 :
1041 : // --- Bonus check 2: ensure that a FRESH, spent coin is deleted by Sync()
1042 : //
1043 4 : txid = InsecureRand256();
1044 4 : outp = COutPoint(txid, 0);
1045 4 : coin = MakeCoin();
1046 4 : CAmount coin_val = coin.out.nValue;
1047 4 : BOOST_CHECK(!base.HaveCoin(outp));
1048 4 : BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
1049 4 : BOOST_CHECK(!all_caches[1]->HaveCoin(outp));
1050 :
1051 : // Add and spend from same cache without flushing.
1052 4 : all_caches[0]->AddCoin(outp, std::move(coin), false);
1053 :
1054 : // Coin should be FRESH in the cache.
1055 4 : GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
1056 4 : BOOST_CHECK_EQUAL(value, coin_val);
1057 4 : BOOST_CHECK_EQUAL(flags, DIRTY|FRESH);
1058 :
1059 : // Base shouldn't have seen coin.
1060 4 : BOOST_CHECK(!base.HaveCoin(outp));
1061 :
1062 4 : BOOST_CHECK(all_caches[0]->SpendCoin(outp));
1063 4 : all_caches[0]->Sync();
1064 :
1065 : // Ensure there is no sign of the coin after spend/flush.
1066 4 : GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
1067 4 : BOOST_CHECK_EQUAL(value, ABSENT);
1068 4 : BOOST_CHECK_EQUAL(flags, NO_ENTRY);
1069 4 : BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
1070 4 : BOOST_CHECK(!base.HaveCoin(outp));
1071 8 : }
1072 :
1073 149 : BOOST_AUTO_TEST_CASE(ccoins_flush_behavior)
1074 : {
1075 : // Create two in-memory caches atop a leveldb view.
1076 1 : CCoinsViewDB base{"test", /*nCacheSize=*/ 1 << 23, /*fMemory=*/ true, /*fWipe=*/ false};
1077 1 : std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches;
1078 1 : caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1079 1 : caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get()));
1080 :
1081 3 : for (const auto& view : caches) {
1082 2 : TestFlushBehavior(view.get(), base, caches, /*do_erasing_flush=*/false);
1083 2 : TestFlushBehavior(view.get(), base, caches, /*do_erasing_flush=*/true);
1084 : }
1085 1 : }
1086 :
1087 149 : BOOST_AUTO_TEST_CASE(coins_resource_is_used)
1088 : {
1089 1 : CCoinsMapMemoryResource resource;
1090 1 : PoolResourceTester::CheckAllDataAccountedFor(resource);
1091 :
1092 : {
1093 1 : CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
1094 1 : BOOST_TEST(memusage::DynamicUsage(map) >= resource.ChunkSizeBytes());
1095 :
1096 1 : map.reserve(1000);
1097 :
1098 : // The resource has preallocated a chunk, so we should have space for at several nodes without the need to allocate anything else.
1099 1 : const auto usage_before = memusage::DynamicUsage(map);
1100 :
1101 1 : COutPoint out_point{};
1102 1001 : for (size_t i = 0; i < 1000; ++i) {
1103 1000 : out_point.n = i;
1104 1000 : map[out_point];
1105 1000 : }
1106 1 : BOOST_TEST(usage_before == memusage::DynamicUsage(map));
1107 1 : }
1108 :
1109 1 : PoolResourceTester::CheckAllDataAccountedFor(resource);
1110 1 : }
1111 :
1112 146 : BOOST_AUTO_TEST_SUITE_END()
|