21constexpr uint32_t NUM_OUTPOINTS = 256;
23constexpr uint32_t NUM_COINS = 256;
25constexpr uint32_t MAX_CACHES = 4;
27using coinidx_type = uint8_t;
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)};
64 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 3);
72 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
79 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
85 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
91 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
103enum class EntryType : uint8_t
121 coinidx_type coinidx;
129 CacheEntry entry[NUM_OUTPOINTS];
132 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
144 std::map<COutPoint, Coin> m_data;
149 if (
auto it{m_data.find(outpoint)}; it != m_data.end()) {
150 assert(!it->second.IsSpent());
158 for (
auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
159 if (it->second.IsDirty()) {
160 if (it->second.coin.IsSpent()) {
161 m_data.erase(it->first);
163 if (cursor.WillErase(*it)) {
164 m_data[it->first] = std::move(it->second.coin);
166 m_data[it->first] = it->second.coin;
171 auto it2 = m_data.find(it->first);
172 if (it->second.coin.IsSpent()) {
173 assert(it2 == m_data.end());
175 assert(it2 != m_data.end());
176 assert(it->second.coin.out == it2->second.out);
177 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
178 assert(it->second.coin.nHeight == it2->second.nHeight);
190 static const PrecomputedData
data;
193 CoinsViewBottom bottom;
195 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
197 CacheLevel sim_caches[MAX_CACHES + 1];
199 uint32_t current_height = 1U;
202 sim_caches[0].Wipe();
205 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
206 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
208 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
209 if (entry.entrytype == EntryType::UNSPENT) {
210 return {{entry.coinidx, entry.height}};
214 if (cache_idx == 0)
break;
222 assert(caches.size() >= 1);
223 auto& cache = sim_caches[caches.size()];
224 auto& prev_cache = sim_caches[caches.size() - 1];
225 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
227 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
241 if (caches.empty()) {
243 sim_caches[caches.size()].Wipe();
253 auto sim = lookup(outpointidx);
256 caches.back()->PeekCoin(
data.outpoints[outpointidx]) :
257 caches.back()->GetCoin(
data.outpoints[outpointidx]);
259 if (!sim.has_value()) {
262 assert(realcoin && !realcoin->IsSpent());
263 const auto& simcoin =
data.coins[sim->first];
264 assert(realcoin->out == simcoin.out);
265 assert(realcoin->fCoinBase == simcoin.fCoinBase);
266 assert(realcoin->nHeight == sim->second);
273 auto sim = lookup(outpointidx);
275 auto real = caches.back()->HaveCoin(
data.outpoints[outpointidx]);
277 assert(sim.has_value() == real);
283 (void)caches.back()->HaveCoinInCache(
data.outpoints[outpointidx]);
289 auto sim = lookup(outpointidx);
291 const auto& realcoin = caches.back()->AccessCoin(
data.outpoints[outpointidx]);
293 if (!sim.has_value()) {
294 assert(realcoin.IsSpent());
296 assert(!realcoin.IsSpent());
297 const auto& simcoin =
data.coins[sim->first];
298 assert(simcoin.out == realcoin.out);
299 assert(simcoin.fCoinBase == realcoin.fCoinBase);
300 assert(realcoin.nHeight == sim->second);
308 auto sim = lookup(outpointidx);
312 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin), sim.has_value());
314 auto& entry = sim_caches[caches.size()].entry[outpointidx];
315 entry.entrytype = EntryType::UNSPENT;
316 entry.coinidx = coinidx;
317 entry.height = current_height;
325 coin.nHeight = current_height;
326 caches.back()->AddCoin(
data.outpoints[outpointidx], std::move(coin),
true);
328 auto& entry = sim_caches[caches.size()].entry[outpointidx];
329 entry.entrytype = EntryType::UNSPENT;
330 entry.coinidx = coinidx;
331 entry.height = current_height;
337 caches.back()->SpendCoin(
data.outpoints[outpointidx],
nullptr);
345 auto sim = lookup(outpointidx);
348 caches.back()->SpendCoin(
data.outpoints[outpointidx], &realcoin);
352 if (!sim.has_value()) {
356 const auto& simcoin =
data.coins[sim->first];
366 caches.back()->Uncache(
data.outpoints[outpointidx]);
370 if (caches.size() != MAX_CACHES) {
378 sim_caches[caches.size()].Wipe();
384 caches.back()->SanityCheck();
399 caches.back()->Sync();
403 sim_caches[caches.size()].Wipe();
406 const auto reset_guard{caches.back()->CreateResetGuard()};
411 (void)caches.back()->GetCacheSize();
415 (void)caches.back()->DynamicMemoryUsage();
425 for (
const auto& cache : caches) {
426 cache->SanityCheck();
431 for (
unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
432 auto& cache = *caches[sim_idx - 1];
433 size_t cache_size = 0;
435 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
436 cache_size += cache.HaveCoinInCache(
data.outpoints[outpointidx]);
437 const auto& real = cache.AccessCoin(
data.outpoints[outpointidx]);
438 auto sim = lookup(outpointidx, sim_idx);
439 if (!sim.has_value()) {
443 assert(real.out ==
data.coins[sim->first].out);
444 assert(real.fCoinBase ==
data.coins[sim->first].fCoinBase);
445 assert(real.nHeight == sim->second);
450 assert(cache.GetCacheSize() >= cache_size);
454 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
455 auto realcoin = bottom.GetCoin(
data.outpoints[outpointidx]);
456 auto sim = lookup(outpointidx, 0);
457 if (!sim.has_value()) {
460 assert(realcoin && !realcoin->IsSpent());
461 assert(realcoin->out ==
data.coins[sim->first].out);
462 assert(realcoin->fCoinBase ==
data.coins[sim->first].fCoinBase);
463 assert(realcoin->nHeight == sim->second);
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
int64_t CAmount
Amount in satoshis (Can be negative)
CCoinsView that adds a memory cache for transactions to another CCoinsView.
An outpoint - a combination of a transaction hash and an index n into its vout.
A hasher class for SHA-256.
void Finalize(unsigned char hash[OUTPUT_SIZE])
CSHA256 & Write(const unsigned char *data, size_t len)
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
void BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &) override
Do a bulk modification (multiple Coin changes + BestBlock change).
std::optional< Coin > GetCoin(const COutPoint &) const override
Retrieve the Coin (unspent transaction output) for a given outpoint.
CCoinsViewCache overlay that avoids populating/mutating parent cache layers on cache misses.
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t GetUint64(int pos) const
constexpr unsigned char * begin()
void resize(size_type new_size)
static transaction_identifier FromUint256(const uint256 &id)
FUZZ_TARGET(coinscache_sim)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)