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