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) {
144 class CoinsViewBottom final :
public CCoinsView 146 std::map<COutPoint, Coin> m_data;
152 if (
auto it = m_data.find(outpoint); it != m_data.end())
return it->second;
158 return m_data.count(outpoint);
162 std::vector<uint256>
GetHeadBlocks() const final {
return {}; }
163 std::unique_ptr<CCoinsViewCursor>
Cursor() const final {
return {}; }
164 size_t EstimateSize() const final {
return m_data.size(); }
168 for (
auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
169 if (it->second.IsDirty()) {
170 if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
171 m_data.erase(it->first);
172 }
else if (cursor.WillErase(*it)) {
173 m_data[it->first] = std::move(it->second.coin);
175 m_data[it->first] = it->second.coin;
179 auto it2 = m_data.find(it->first);
180 if (it->second.coin.IsSpent()) {
181 assert(it2 == m_data.end() || it2->second.IsSpent());
183 assert(it2 != m_data.end());
184 assert(it->second.coin.out == it2->second.out);
185 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
186 assert(it->second.coin.nHeight == it2->second.nHeight);
199 static const PrecomputedData
data;
202 CoinsViewBottom bottom;
204 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
206 CacheLevel sim_caches[MAX_CACHES + 1];
208 uint32_t current_height = 1U;
211 sim_caches[0].Wipe();
214 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
215 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
217 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
218 if (entry.entrytype == EntryType::UNSPENT) {
219 return {{entry.coinidx, entry.height}};
223 if (cache_idx == 0)
break;
231 assert(caches.size() >= 1);
232 auto& cache = sim_caches[caches.size()];
233 auto& prev_cache = sim_caches[caches.size() - 1];
234 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
236 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
250 if (caches.empty()) {
252 sim_caches[caches.size()].Wipe();
260 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
262 auto sim = lookup(outpointidx);
264 auto realcoin = caches.back()->GetCoin(
data.outpoints[outpointidx]);
266 if (!sim.has_value()) {
267 assert(!realcoin || realcoin->IsSpent());
269 assert(realcoin && !realcoin->IsSpent());
270 const auto& simcoin =
data.coins[sim->first];
271 assert(realcoin->out == simcoin.out);
272 assert(realcoin->fCoinBase == simcoin.fCoinBase);
273 assert(realcoin->nHeight == sim->second);
278 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
280 auto sim = lookup(outpointidx);
282 auto real = caches.back()->HaveCoin(
data.outpoints[outpointidx]);
284 assert(sim.has_value() == real);
288 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
290 (void)caches.back()->HaveCoinInCache(
data.outpoints[outpointidx]);
294 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
296 auto sim = lookup(outpointidx);
298 const auto& realcoin = caches.back()->AccessCoin(
data.outpoints[outpointidx]);
300 if (!sim.has_value()) {
301 assert(realcoin.IsSpent());
303 assert(!realcoin.IsSpent());
304 const auto& simcoin =
data.coins[sim->first];
305 assert(simcoin.out == realcoin.out);
306 assert(simcoin.fCoinBase == realcoin.fCoinBase);
307 assert(realcoin.nHeight == sim->second);
312 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
313 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
315 auto sim = lookup(outpointidx);
319 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin), sim.has_value());
321 auto& entry = sim_caches[caches.size()].entry[outpointidx];
322 entry.entrytype = EntryType::UNSPENT;
323 entry.coinidx = coinidx;
324 entry.height = current_height;
328 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
329 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
332 coin.nHeight = current_height;
333 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin),
true);
335 auto& entry = sim_caches[caches.size()].entry[outpointidx];
336 entry.entrytype = EntryType::UNSPENT;
337 entry.coinidx = coinidx;
338 entry.height = current_height;
342 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
344 caches.back()->SpendCoin(
data.outpoints[outpointidx],
nullptr);
350 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
352 auto sim = lookup(outpointidx);
355 caches.back()->SpendCoin(
data.outpoints[outpointidx], &realcoin);
359 if (!sim.has_value()) {
363 const auto& simcoin =
data.coins[sim->first];
371 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
373 caches.back()->Uncache(
data.outpoints[outpointidx]);
377 if (caches.size() != MAX_CACHES) {
381 sim_caches[caches.size()].Wipe();
387 caches.back()->SanityCheck();
395 caches.back()->Flush();
402 caches.back()->Sync();
409 caches.back()->Flush();
410 caches.back()->ReallocateCache();
414 (void)caches.back()->GetCacheSize();
418 (void)caches.back()->DynamicMemoryUsage();
422 current_height = provider.ConsumeIntegralInRange<uint32_t>(1, current_height - 1);
428 for (
const auto& cache : caches) {
429 cache->SanityCheck();
434 for (
unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
435 auto& cache = *caches[sim_idx - 1];
436 size_t cache_size = 0;
438 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
439 cache_size += cache.HaveCoinInCache(
data.outpoints[outpointidx]);
440 const auto& real = cache.AccessCoin(
data.outpoints[outpointidx]);
441 auto sim = lookup(outpointidx, sim_idx);
442 if (!sim.has_value()) {
446 assert(real.out ==
data.coins[sim->first].out);
447 assert(real.fCoinBase ==
data.coins[sim->first].fCoinBase);
448 assert(real.nHeight == sim->second);
453 assert(cache.GetCacheSize() >= cache_size);
457 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
458 auto realcoin = bottom.GetCoin(
data.outpoints[outpointidx]);
459 auto sim = lookup(outpointidx, 0);
460 if (!sim.has_value()) {
461 assert(!realcoin || realcoin->IsSpent());
463 assert(realcoin && !realcoin->IsSpent());
464 assert(realcoin->out ==
data.coins[sim->first].out);
465 assert(realcoin->fCoinBase ==
data.coins[sim->first].fCoinBase);
466 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)
virtual bool BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &hashBlock)
Do a bulk modification (multiple Coin changes + BestBlock change).
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.
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.