Branch data Line data Source code
1 : : // Copyright (c) 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 <coins.h>
6 : : #include <primitives/block.h>
7 : : #include <primitives/transaction.h>
8 : : #include <primitives/transaction_identifier.h>
9 : : #include <txdb.h>
10 : : #include <uint256.h>
11 : : #include <util/byte_units.h>
12 : : #include <util/hasher.h>
13 : :
14 : : #include <boost/test/unit_test.hpp>
15 : :
16 : : #include <cstdint>
17 : : #include <cstring>
18 : : #include <ranges>
19 : :
20 : : BOOST_AUTO_TEST_SUITE(coinsviewoverlay_tests)
21 : :
22 : : namespace {
23 : :
24 : 4 : CBlock CreateBlock() noexcept
25 : : {
26 : 4 : static constexpr auto NUM_TXS{100};
27 : 4 : CBlock block;
28 : 4 : CMutableTransaction coinbase;
29 : 4 : coinbase.vin.emplace_back();
30 [ - + ]: 4 : block.vtx.push_back(MakeTransactionRef(coinbase));
31 : :
32 [ + + ]: 400 : for (const auto i : std::views::iota(1, NUM_TXS)) {
33 : 396 : CMutableTransaction tx;
34 : 396 : Txid txid{Txid::FromUint256(uint256(i))};
35 : 396 : tx.vin.emplace_back(txid, 0);
36 [ - + ]: 396 : block.vtx.push_back(MakeTransactionRef(tx));
37 : 396 : }
38 : :
39 : 4 : return block;
40 : 4 : }
41 : :
42 : 4 : void PopulateView(const CBlock& block, CCoinsView& view, bool spent = false)
43 : : {
44 : 4 : CCoinsViewCache cache{&view};
45 [ + - ]: 4 : cache.SetBestBlock(uint256::ONE);
46 : :
47 [ + + ]: 400 : for (const auto& tx : block.vtx | std::views::drop(1)) {
48 [ + + ]: 792 : for (const auto& in : tx->vin) {
49 : 396 : Coin coin{};
50 [ + + ]: 396 : if (!spent) coin.out.nValue = 1;
51 [ + - ]: 396 : cache.EmplaceCoinInternalDANGER(COutPoint{in.prevout}, std::move(coin));
52 : 396 : }
53 : : }
54 : :
55 [ + - ]: 4 : cache.Flush();
56 : 4 : }
57 : :
58 : 2 : void CheckCache(const CBlock& block, const CCoinsViewCache& cache)
59 : : {
60 : 2 : uint32_t counter{0};
61 : :
62 [ + + ]: 202 : for (const auto& tx : block.vtx) {
63 [ + + ]: 200 : if (tx->IsCoinBase()) {
64 [ + - + - ]: 4 : BOOST_CHECK(!cache.HaveCoinInCache(tx->vin[0].prevout));
65 : : } else {
66 [ + + ]: 396 : for (const auto& in : tx->vin) {
67 : 198 : const auto& outpoint{in.prevout};
68 : 198 : const auto& first{cache.AccessCoin(outpoint)};
69 : 198 : const auto& second{cache.AccessCoin(outpoint)};
70 [ + - ]: 198 : BOOST_CHECK_EQUAL(&first, &second);
71 : 198 : ++counter;
72 [ + - + - ]: 396 : BOOST_CHECK(cache.HaveCoinInCache(outpoint));
73 : : }
74 : : }
75 : : }
76 [ + - ]: 2 : BOOST_CHECK_EQUAL(cache.GetCacheSize(), counter);
77 : 2 : }
78 : :
79 : : } // namespace
80 : :
81 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(fetch_inputs_from_db)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
82 : : {
83 : 1 : const auto block{CreateBlock()};
84 : 0 : CCoinsViewDB db{{.path = "", .cache_bytes = 1_MiB, .memory_only = true}, {}};
85 [ + - ]: 1 : PopulateView(block, db);
86 [ + - ]: 1 : CCoinsViewCache main_cache{&db};
87 [ + - ]: 1 : CoinsViewOverlay view{&main_cache};
88 [ + - ]: 1 : const auto& outpoint{block.vtx[1]->vin[0].prevout};
89 : :
90 [ + - + - : 2 : BOOST_CHECK(view.HaveCoin(outpoint));
+ - + - ]
91 [ + - + - : 2 : BOOST_CHECK(view.GetCoin(outpoint).has_value());
+ - + - ]
92 [ + - + - : 2 : BOOST_CHECK(!main_cache.HaveCoinInCache(outpoint));
+ - + - ]
93 : :
94 [ + - ]: 1 : CheckCache(block, view);
95 : : // Check that no coins have been moved up to main cache from db
96 [ + + ]: 101 : for (const auto& tx : block.vtx) {
97 [ + + ]: 200 : for (const auto& in : tx->vin) {
98 [ + - + - : 200 : BOOST_CHECK(!main_cache.HaveCoinInCache(in.prevout));
+ - ]
99 : : }
100 : : }
101 : :
102 [ + - ]: 1 : view.SetBestBlock(uint256::ONE);
103 [ + - + - : 2 : BOOST_CHECK(view.SpendCoin(outpoint));
+ - + - ]
104 [ + - ]: 1 : view.Flush();
105 [ + - + - : 2 : BOOST_CHECK(!main_cache.PeekCoin(outpoint).has_value());
+ - ]
106 [ + - + - ]: 2 : }
107 : :
108 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(fetch_inputs_from_cache)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
109 : : {
110 : 1 : const auto block{CreateBlock()};
111 : 0 : CCoinsViewDB db{{.path = "", .cache_bytes = 1_MiB, .memory_only = true}, {}};
112 [ + - ]: 1 : CCoinsViewCache main_cache{&db};
113 [ + - ]: 1 : PopulateView(block, main_cache);
114 [ + - ]: 1 : CoinsViewOverlay view{&main_cache};
115 [ + - ]: 1 : CheckCache(block, view);
116 : :
117 [ + - ]: 1 : const auto& outpoint{block.vtx[1]->vin[0].prevout};
118 [ + - ]: 1 : view.SetBestBlock(uint256::ONE);
119 [ + - + - : 2 : BOOST_CHECK(view.SpendCoin(outpoint));
+ - + - ]
120 [ + - ]: 1 : view.Flush();
121 [ + - + - : 2 : BOOST_CHECK(!main_cache.PeekCoin(outpoint).has_value());
+ - ]
122 [ + - + - ]: 2 : }
123 : :
124 : : // Test for the case where a block spends coins that are spent in the cache, but
125 : : // the spentness has not been flushed to the db.
126 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(fetch_no_double_spend)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
127 : : {
128 : 1 : const auto block{CreateBlock()};
129 : 0 : CCoinsViewDB db{{.path = "", .cache_bytes = 1_MiB, .memory_only = true}, {}};
130 [ + - ]: 1 : PopulateView(block, db);
131 [ + - ]: 1 : CCoinsViewCache main_cache{&db};
132 : : // Add all inputs as spent already in cache
133 [ + - ]: 1 : PopulateView(block, main_cache, /*spent=*/true);
134 [ + - ]: 1 : CoinsViewOverlay view{&main_cache};
135 [ + + ]: 101 : for (const auto& tx : block.vtx) {
136 [ + + ]: 200 : for (const auto& in : tx->vin) {
137 [ + - ]: 100 : const auto& c{view.AccessCoin(in.prevout)};
138 [ + - + - : 200 : BOOST_CHECK(c.IsSpent());
+ - ]
139 [ + - + - : 200 : BOOST_CHECK(!view.HaveCoin(in.prevout));
+ - + - ]
140 [ + - + - : 200 : BOOST_CHECK(!view.GetCoin(in.prevout));
+ - ]
141 : : }
142 : : }
143 : : // Coins are not added to the view, even though they exist unspent in the parent db
144 [ + - + - : 1 : BOOST_CHECK_EQUAL(view.GetCacheSize(), 0);
+ - ]
145 [ + - + - ]: 2 : }
146 : :
147 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(fetch_no_inputs)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
148 : : {
149 : 1 : const auto block{CreateBlock()};
150 : 0 : CCoinsViewDB db{{.path = "", .cache_bytes = 1_MiB, .memory_only = true}, {}};
151 [ + - ]: 1 : CCoinsViewCache main_cache{&db};
152 [ + - ]: 1 : CoinsViewOverlay view{&main_cache};
153 [ + + ]: 101 : for (const auto& tx : block.vtx) {
154 [ + + ]: 200 : for (const auto& in : tx->vin) {
155 [ + - ]: 100 : const auto& c{view.AccessCoin(in.prevout)};
156 [ + - + - : 200 : BOOST_CHECK(c.IsSpent());
+ - ]
157 [ + - + - : 200 : BOOST_CHECK(!view.HaveCoin(in.prevout));
+ - + - ]
158 [ + - + - : 200 : BOOST_CHECK(!view.GetCoin(in.prevout));
+ - ]
159 : : }
160 : : }
161 [ + - + - : 1 : BOOST_CHECK_EQUAL(view.GetCacheSize(), 0);
+ - ]
162 [ + - + - ]: 2 : }
163 : :
164 : : BOOST_AUTO_TEST_SUITE_END()
165 : :
|