25constexpr uint32_t NUM_OUTPOINTS = 256;
27constexpr uint32_t NUM_COINS = 256;
29constexpr uint32_t MAX_CACHES = 4;
31using coinidx_type = uint8_t;
36 COutPoint outpoints[NUM_OUTPOINTS];
39 Coin coins[NUM_COINS];
46 static const uint8_t PREFIX_O[1] = {
'o'};
47 static const uint8_t PREFIX_S[1] = {
's'};
48 static const uint8_t PREFIX_M[1] = {
'm'};
50 CMutableTransaction coinbase;
51 coinbase.
vin.emplace_back();
52 block.vtx.push_back(MakeTransactionRef(coinbase));
54 CMutableTransaction tx;
55 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
56 uint32_t idx = (i * 1200U) >> 12;
57 const uint8_t ser[4] = {uint8_t(idx), uint8_t(idx >> 8), uint8_t(idx >> 16), uint8_t(idx >> 24)};
59 CSHA256().Write(PREFIX_O, 1).Write(ser,
sizeof(ser)).Finalize(txid.
begin());
62 tx.
vin.emplace_back(outpoints[i]);
64 block.vtx.push_back(MakeTransactionRef(tx));
66 for (uint32_t i = 0; i < NUM_COINS; ++i) {
67 const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
69 CSHA256().Write(PREFIX_S, 1).Write(ser,
sizeof(ser)).Finalize(hash.
begin());
74 coins[i].out.scriptPubKey.resize(25);
75 coins[i].out.scriptPubKey[0] =
OP_DUP;
77 coins[i].out.scriptPubKey[2] = 20;
78 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 3);
83 coins[i].out.scriptPubKey.resize(23);
85 coins[i].out.scriptPubKey[1] = 20;
86 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
87 coins[i].out.scriptPubKey[22] =
OP_EQUAL;
90 coins[i].out.scriptPubKey.resize(22);
91 coins[i].out.scriptPubKey[0] =
OP_0;
92 coins[i].out.scriptPubKey[1] = 20;
93 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
96 coins[i].out.scriptPubKey.resize(34);
97 coins[i].out.scriptPubKey[0] =
OP_0;
98 coins[i].out.scriptPubKey[1] = 32;
99 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
102 coins[i].out.scriptPubKey.resize(34);
103 coins[i].out.scriptPubKey[0] =
OP_1;
104 coins[i].out.scriptPubKey[1] = 32;
105 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
109 CSHA256().Write(PREFIX_M, 1).Write(ser,
sizeof(ser)).Finalize(hash.
begin());
111 coins[i].fCoinBase = (hash.
GetUint64(1) & 7) == 0;
112 coins[i].nHeight = 0;
117enum class EntryType : uint8_t
135 coinidx_type coinidx;
143 CacheEntry entry[NUM_OUTPOINTS];
146 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
147 entry[i].entrytype = EntryType::NONE;
158 std::map<COutPoint, Coin> m_data;
161 std::optional<Coin> GetCoin(
const COutPoint& outpoint)
const final
163 if (
auto it{m_data.find(outpoint)}; it != m_data.end()) {
164 assert(!it->second.IsSpent());
170 void BatchWrite(CoinsViewCacheCursor& cursor,
const uint256&)
final
172 for (
auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
173 if (it->second.IsDirty()) {
174 if (it->second.coin.IsSpent()) {
175 m_data.erase(it->first);
177 if (cursor.WillErase(*it)) {
178 m_data[it->first] = std::move(it->second.coin);
180 m_data[it->first] = it->second.coin;
185 auto it2 = m_data.find(it->first);
186 if (it->second.coin.IsSpent()) {
187 assert(it2 == m_data.end());
189 assert(it2 != m_data.end());
190 assert(it->second.coin.out == it2->second.out);
191 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
192 assert(it->second.coin.nHeight == it2->second.nHeight);
201struct OverlayFetchScope
203 CCoinsViewCache::ResetGuard guard;
204 OverlayFetchScope(CoinsViewOverlay& view,
const CBlock& block) : guard(view.StartFetching(block)) {}
209std::shared_ptr<ThreadPool> g_thread_pool{std::make_shared<ThreadPool>(
"cache_fuzz")};
223 static const PrecomputedData
data;
228 std::vector<std::unique_ptr<CCoinsViewCache>>
caches;
240 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
241 uint32_t cache_idx = sim_idx == -1 ?
caches.size() : sim_idx;
243 const auto& entry =
sim_caches[cache_idx].entry[outpointidx];
244 if (entry.entrytype == EntryType::UNSPENT) {
245 return {{entry.coinidx, entry.height}};
246 }
else if (entry.entrytype == EntryType::SPENT) {
249 if (cache_idx == 0)
break;
260 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
261 if (cache.entry[outpointidx].entrytype != EntryType::NONE) {
262 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
263 cache.entry[outpointidx].entrytype = EntryType::NONE;
271 return std::make_unique<OverlayFetchScope>(overlay,
data.block);
296 auto sim =
lookup(outpointidx);
299 caches.back()->PeekCoin(
data.outpoints[outpointidx]) :
300 caches.back()->GetCoin(
data.outpoints[outpointidx]);
302 if (!sim.has_value()) {
305 assert(realcoin && !realcoin->IsSpent());
306 const auto& simcoin =
data.coins[sim->first];
307 assert(realcoin->out == simcoin.out);
308 assert(realcoin->fCoinBase == simcoin.fCoinBase);
309 assert(realcoin->nHeight == sim->second);
316 auto sim =
lookup(outpointidx);
318 auto real =
caches.back()->HaveCoin(
data.outpoints[outpointidx]);
320 assert(sim.has_value() == real);
326 (void)
caches.back()->HaveCoinInCache(
data.outpoints[outpointidx]);
332 auto sim =
lookup(outpointidx);
334 const auto& realcoin =
caches.back()->AccessCoin(
data.outpoints[outpointidx]);
336 if (!sim.has_value()) {
337 assert(realcoin.IsSpent());
339 assert(!realcoin.IsSpent());
340 const auto& simcoin =
data.coins[sim->first];
341 assert(simcoin.out == realcoin.out);
342 assert(simcoin.fCoinBase == realcoin.fCoinBase);
343 assert(realcoin.nHeight == sim->second);
351 auto sim =
lookup(outpointidx);
355 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin), sim.has_value());
358 entry.entrytype = EntryType::UNSPENT;
359 entry.coinidx = coinidx;
369 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin),
true);
372 entry.entrytype = EntryType::UNSPENT;
373 entry.coinidx = coinidx;
380 caches.back()->SpendCoin(
data.outpoints[outpointidx],
nullptr);
388 auto sim =
lookup(outpointidx);
391 caches.back()->SpendCoin(
data.outpoints[outpointidx], &realcoin);
395 if (!sim.has_value()) {
399 const auto& simcoin =
data.coins[sim->first];
409 caches.back()->Uncache(
data.outpoints[outpointidx]);
413 if (
caches.size() != MAX_CACHES) {
429 caches.back()->SanityCheck();
437 overlay && !overlay->AllInputsConsumed()) {
461 (void)
caches.back()->CreateResetGuard();
466 (void)
caches.back()->GetCacheSize();
470 (void)
caches.back()->DynamicMemoryUsage();
481 cache->SanityCheck();
485 for (
unsigned sim_idx = 1; sim_idx <=
caches.size(); ++sim_idx) {
486 auto& cache = *
caches[sim_idx - 1];
487 size_t cache_size = 0;
489 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
490 cache_size += cache.HaveCoinInCache(
data.outpoints[outpointidx]);
491 const auto real{cache.PeekCoin(
data.outpoints[outpointidx])};
492 auto sim =
lookup(outpointidx, sim_idx);
493 if (!sim.has_value()) {
497 assert(real->out ==
data.coins[sim->first].out);
498 assert(real->fCoinBase ==
data.coins[sim->first].fCoinBase);
499 assert(real->nHeight == sim->second);
504 assert(cache.GetCacheSize() >= cache_size);
508 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
509 auto realcoin =
bottom.GetCoin(
data.outpoints[outpointidx]);
510 auto sim =
lookup(outpointidx, 0);
511 if (!sim.has_value()) {
514 assert(realcoin && !realcoin->IsSpent());
515 assert(realcoin->out ==
data.coins[sim->first].out);
516 assert(realcoin->fCoinBase ==
data.coins[sim->first].fCoinBase);
517 assert(realcoin->nHeight == sim->second);
constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
int64_t CAmount
Amount in satoshis (Can be negative).
constexpr int32_t DEFAULT_PREVOUTFETCH_THREADS
CCoinsViewCache(CCoinsView *in_base, bool deterministic=false)
CTxOut out
unspent transaction output
bool IsSpent() const
Either this coin never existed (see e.g.
bool fCoinBase
whether containing transaction was a coinbase
uint32_t nHeight
at which height this containing transaction was included in the active block chain
CCoinsViewCache subclass that asynchronously fetches most block input prevouts in parallel during Con...
CoinsViewOverlay(CCoinsView *in_base, std::shared_ptr< ThreadPool > thread_pool, bool deterministic=false) noexcept
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t GetUint64(int pos) const
constexpr unsigned char * begin()
static transaction_identifier FromUint256(const uint256 &id)
std::vector< std::unique_ptr< CCoinsViewCache > > caches
Real CCoinsViewCache objects.
uint32_t current_height
Current height in the simulation.
CacheLevel sim_caches[MAX_CACHES+1]
Simulated cache data (sim_caches[0] matches bottom, sim_caches[i+1] matches caches[i]).
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
auto flush
Flush changes in top cache to the one below.
std::vector< std::unique_ptr< OverlayFetchScope > > fetch_scopes
Long-lived StartFetching guards, parallel to caches (entries are nullptr unless corresponding level i...
const auto make_fetch_scope
Helper creating a fetch scope for the top cache (which must be a CoinsViewOverlay).
CoinsViewBottom bottom
Dummy coinsview instance (base of the hierarchy).
auto lookup
Helper lookup function in the simulated cache stack.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
SeedRandomStateForTest(SeedRand::ZEROS)
FuzzedDataProvider provider
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
@ ZEROS
Seed with a compile time constant of zeros.