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) {
145 class CoinsViewBottom final :
public CCoinsView 147 std::map<COutPoint, Coin> m_data;
152 auto it = m_data.find(outpoint);
153 if (it == m_data.end()) {
154 if ((outpoint.n % 5) == 3) {
167 return m_data.count(outpoint);
171 std::vector<uint256>
GetHeadBlocks() const final {
return {}; }
172 std::unique_ptr<CCoinsViewCursor>
Cursor() const final {
return {}; }
173 size_t EstimateSize() const final {
return m_data.size(); }
177 for (
auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
178 if (it->second.IsDirty()) {
179 if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
180 m_data.erase(it->first);
181 }
else if (cursor.WillErase(*it)) {
182 m_data[it->first] = std::move(it->second.coin);
184 m_data[it->first] = it->second.coin;
188 auto it2 = m_data.find(it->first);
189 if (it->second.coin.IsSpent()) {
190 assert(it2 == m_data.end() || it2->second.IsSpent());
192 assert(it2 != m_data.end());
193 assert(it->second.coin.out == it2->second.out);
194 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
195 assert(it->second.coin.nHeight == it2->second.nHeight);
208 static const PrecomputedData data;
211 CoinsViewBottom bottom;
213 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
215 CacheLevel sim_caches[MAX_CACHES + 1];
217 uint32_t current_height = 1U;
220 sim_caches[0].Wipe();
223 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
224 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
226 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
227 if (entry.entrytype == EntryType::UNSPENT) {
228 return {{entry.coinidx, entry.height}};
232 if (cache_idx == 0)
break;
240 assert(caches.size() >= 1);
241 auto& cache = sim_caches[caches.size()];
242 auto& prev_cache = sim_caches[caches.size() - 1];
243 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
245 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
259 if (caches.empty()) {
261 sim_caches[caches.size()].Wipe();
269 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
271 auto sim = lookup(outpointidx);
274 auto real = caches.back()->GetCoin(data.outpoints[outpointidx], realcoin);
276 if (!sim.has_value()) {
280 const auto& simcoin = data.coins[sim->first];
288 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
290 auto sim = lookup(outpointidx);
292 auto real = caches.back()->HaveCoin(data.outpoints[outpointidx]);
294 assert(sim.has_value() == real);
298 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
300 (void)caches.back()->HaveCoinInCache(data.outpoints[outpointidx]);
304 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
306 auto sim = lookup(outpointidx);
308 const auto& realcoin = caches.back()->AccessCoin(data.outpoints[outpointidx]);
310 if (!sim.has_value()) {
311 assert(realcoin.IsSpent());
313 assert(!realcoin.IsSpent());
314 const auto& simcoin = data.coins[sim->first];
315 assert(simcoin.out == realcoin.out);
316 assert(simcoin.fCoinBase == realcoin.fCoinBase);
317 assert(realcoin.nHeight == sim->second);
322 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
323 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
325 auto sim = lookup(outpointidx);
327 Coin coin = data.coins[coinidx];
329 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), sim.has_value());
331 auto& entry = sim_caches[caches.size()].entry[outpointidx];
332 entry.entrytype = EntryType::UNSPENT;
333 entry.coinidx = coinidx;
334 entry.height = current_height;
338 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
339 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
341 Coin coin = data.coins[coinidx];
342 coin.nHeight = current_height;
343 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin),
true);
345 auto& entry = sim_caches[caches.size()].entry[outpointidx];
346 entry.entrytype = EntryType::UNSPENT;
347 entry.coinidx = coinidx;
348 entry.height = current_height;
352 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
354 caches.back()->SpendCoin(data.outpoints[outpointidx],
nullptr);
360 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
362 auto sim = lookup(outpointidx);
365 caches.back()->SpendCoin(data.outpoints[outpointidx], &realcoin);
369 if (!sim.has_value()) {
373 const auto& simcoin = data.coins[sim->first];
381 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
383 caches.back()->Uncache(data.outpoints[outpointidx]);
387 if (caches.size() != MAX_CACHES) {
391 sim_caches[caches.size()].Wipe();
397 caches.back()->SanityCheck();
405 caches.back()->Flush();
412 caches.back()->Sync();
419 caches.back()->Flush();
420 caches.back()->ReallocateCache();
424 (void)caches.back()->GetCacheSize();
428 (void)caches.back()->DynamicMemoryUsage();
432 current_height = provider.ConsumeIntegralInRange<uint32_t>(1, current_height - 1);
438 for (
const auto& cache : caches) {
439 cache->SanityCheck();
444 for (
unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
445 auto& cache = *caches[sim_idx - 1];
446 size_t cache_size = 0;
448 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
449 cache_size += cache.HaveCoinInCache(data.outpoints[outpointidx]);
450 const auto& real = cache.AccessCoin(data.outpoints[outpointidx]);
451 auto sim = lookup(outpointidx, sim_idx);
452 if (!sim.has_value()) {
456 assert(real.out == data.coins[sim->first].out);
457 assert(real.fCoinBase == data.coins[sim->first].fCoinBase);
458 assert(real.nHeight == sim->second);
463 assert(cache.GetCacheSize() >= cache_size);
467 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
469 bool real = bottom.GetCoin(data.outpoints[outpointidx], realcoin);
470 auto sim = lookup(outpointidx, 0);
471 if (!sim.has_value()) {
475 assert(realcoin.
out == data.coins[sim->first].out);
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)
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const
Retrieve the Coin (unspent transaction output) for a given outpoint.
virtual bool BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &hashBlock)
Do a bulk modification (multiple Coin changes + BestBlock change).
static const CAmount SPENT
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.
virtual bool HaveCoin(const COutPoint &outpoint) const
Just check whether a given outpoint is unspent.
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.
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.
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.