21 constexpr uint32_t NUM_OUTPOINTS = 256;
23 constexpr uint32_t NUM_COINS = 256;
25 constexpr uint32_t MAX_CACHES = 4;
27 using coinidx_type = uint8_t;
29 struct PrecomputedData
35 Coin coins[NUM_COINS];
39 static const uint8_t PREFIX_O[1] = {
'o'};
40 static const uint8_t PREFIX_S[1] = {
's'};
41 static const uint8_t PREFIX_M[1] = {
'm'};
43 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
44 uint32_t idx = (i * 1200U) >> 12;
45 const uint8_t ser[4] = {uint8_t(idx), uint8_t(idx >> 8), uint8_t(idx >> 16), uint8_t(idx >> 24)};
52 for (uint32_t i = 0; i < NUM_COINS; ++i) {
53 const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
121 coinidx_type coinidx;
129 CacheEntry entry[NUM_OUTPOINTS];
132 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
142 class CoinsViewBottom final :
public CCoinsView 144 std::map<COutPoint, Coin> m_data;
149 if (
auto it{m_data.find(outpoint)}; it != m_data.end()) {
150 assert(!it->second.IsSpent());
157 std::vector<uint256>
GetHeadBlocks() const final {
return {}; }
158 std::unique_ptr<CCoinsViewCursor>
Cursor() const final {
return {}; }
159 size_t EstimateSize() const final {
return m_data.size(); }
163 for (
auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
164 if (it->second.IsDirty()) {
165 if (it->second.coin.IsSpent()) {
166 m_data.erase(it->first);
168 if (cursor.WillErase(*it)) {
169 m_data[it->first] = std::move(it->second.coin);
171 m_data[it->first] = it->second.coin;
176 auto it2 = m_data.find(it->first);
177 if (it->second.coin.IsSpent()) {
178 assert(it2 == m_data.end());
180 assert(it2 != m_data.end());
181 assert(it->second.coin.out == it2->second.out);
182 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
183 assert(it->second.coin.nHeight == it2->second.nHeight);
195 static const PrecomputedData
data;
198 CoinsViewBottom bottom;
200 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
202 CacheLevel sim_caches[MAX_CACHES + 1];
204 uint32_t current_height = 1U;
207 sim_caches[0].Wipe();
210 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
211 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
213 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
214 if (entry.entrytype == EntryType::UNSPENT) {
215 return {{entry.coinidx, entry.height}};
219 if (cache_idx == 0)
break;
227 assert(caches.size() >= 1);
228 auto& cache = sim_caches[caches.size()];
229 auto& prev_cache = sim_caches[caches.size() - 1];
230 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
232 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
246 if (caches.empty()) {
248 sim_caches[caches.size()].Wipe();
256 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
258 auto sim = lookup(outpointidx);
260 auto realcoin = provider.ConsumeBool() ?
261 caches.back()->PeekCoin(
data.outpoints[outpointidx]) :
262 caches.back()->GetCoin(
data.outpoints[outpointidx]);
264 if (!sim.has_value()) {
267 assert(realcoin && !realcoin->IsSpent());
268 const auto& simcoin =
data.coins[sim->first];
269 assert(realcoin->out == simcoin.out);
270 assert(realcoin->fCoinBase == simcoin.fCoinBase);
271 assert(realcoin->nHeight == sim->second);
276 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
278 auto sim = lookup(outpointidx);
280 auto real = caches.back()->HaveCoin(
data.outpoints[outpointidx]);
282 assert(sim.has_value() == real);
286 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
288 (void)caches.back()->HaveCoinInCache(
data.outpoints[outpointidx]);
292 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
294 auto sim = lookup(outpointidx);
296 const auto& realcoin = caches.back()->AccessCoin(
data.outpoints[outpointidx]);
298 if (!sim.has_value()) {
299 assert(realcoin.IsSpent());
301 assert(!realcoin.IsSpent());
302 const auto& simcoin =
data.coins[sim->first];
303 assert(simcoin.out == realcoin.out);
304 assert(simcoin.fCoinBase == realcoin.fCoinBase);
305 assert(realcoin.nHeight == sim->second);
310 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
311 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
313 auto sim = lookup(outpointidx);
317 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin), sim.has_value());
319 auto& entry = sim_caches[caches.size()].entry[outpointidx];
320 entry.entrytype = EntryType::UNSPENT;
321 entry.coinidx = coinidx;
322 entry.height = current_height;
326 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
327 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
330 coin.nHeight = current_height;
331 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin),
true);
333 auto& entry = sim_caches[caches.size()].entry[outpointidx];
334 entry.entrytype = EntryType::UNSPENT;
335 entry.coinidx = coinidx;
336 entry.height = current_height;
340 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
342 caches.back()->SpendCoin(
data.outpoints[outpointidx],
nullptr);
348 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
350 auto sim = lookup(outpointidx);
353 caches.back()->SpendCoin(
data.outpoints[outpointidx], &realcoin);
357 if (!sim.has_value()) {
361 const auto& simcoin =
data.coins[sim->first];
369 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
371 caches.back()->Uncache(
data.outpoints[outpointidx]);
375 if (caches.size() != MAX_CACHES) {
377 if (provider.ConsumeBool()) {
383 sim_caches[caches.size()].Wipe();
389 caches.back()->SanityCheck();
397 caches.back()->Flush(provider.ConsumeBool());
404 caches.back()->Sync();
408 sim_caches[caches.size()].Wipe();
411 const auto reset_guard{caches.back()->CreateResetGuard()};
416 (void)caches.back()->GetCacheSize();
420 (void)caches.back()->DynamicMemoryUsage();
424 current_height = provider.ConsumeIntegralInRange<uint32_t>(1, current_height - 1);
430 for (
const auto& cache : caches) {
431 cache->SanityCheck();
436 for (
unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
437 auto& cache = *caches[sim_idx - 1];
438 size_t cache_size = 0;
440 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
441 cache_size += cache.HaveCoinInCache(
data.outpoints[outpointidx]);
442 const auto& real = cache.AccessCoin(
data.outpoints[outpointidx]);
443 auto sim = lookup(outpointidx, sim_idx);
444 if (!sim.has_value()) {
448 assert(real.out ==
data.coins[sim->first].out);
449 assert(real.fCoinBase ==
data.coins[sim->first].fCoinBase);
450 assert(real.nHeight == sim->second);
455 assert(cache.GetCacheSize() >= cache_size);
459 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
460 auto realcoin = bottom.GetCoin(
data.outpoints[outpointidx]);
461 auto sim = lookup(outpointidx, 0);
462 if (!sim.has_value()) {
465 assert(realcoin && !realcoin->IsSpent());
466 assert(realcoin->out ==
data.coins[sim->first].out);
467 assert(realcoin->fCoinBase ==
data.coins[sim->first].fCoinBase);
468 assert(realcoin->nHeight == sim->second);
CSHA256 & Write(const unsigned char *data, size_t len)
bool IsSpent() const
Either this coin never existed (see e.g.
void resize(size_type new_size)
CTxOut out
unspent transaction output
unsigned int fCoinBase
whether containing transaction was a coinbase
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
CCoinsViewCache overlay that avoids populating/mutating parent cache layers on cache misses...
constexpr unsigned char * begin()
int64_t CAmount
Amount in satoshis (Can be negative)
uint32_t nHeight
at which height this containing transaction was included in the active block chain ...
Abstract view on the open txout dataset.
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
void Finalize(unsigned char hash[OUTPUT_SIZE])
virtual std::vector< uint256 > GetHeadBlocks() const
Retrieve the range of blocks that may have been only partially written.
virtual void BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &hashBlock)
Do a bulk modification (multiple Coin changes + BestBlock change).
FUZZ_TARGET(coinscache_sim)
An outpoint - a combination of a transaction hash and an index n into its vout.
virtual size_t EstimateSize() const
Estimate database size (0 if not implemented)
static transaction_identifier FromUint256(const uint256 &id)
virtual uint256 GetBestBlock() const
Retrieve the block hash whose state this CCoinsView currently represents.
virtual std::optional< Coin > GetCoin(const COutPoint &outpoint) const
Retrieve the Coin (unspent transaction output) for a given outpoint.
constexpr uint64_t GetUint64(int pos) const
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
virtual std::unique_ptr< CCoinsViewCursor > Cursor() const
Get a cursor to iterate over the whole state.
CCoinsView that adds a memory cache for transactions to another CCoinsView.
A hasher class for SHA-256.