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)};
50 for (uint32_t i = 0; i < NUM_COINS; ++i) {
51 const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
62 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 3);
70 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
77 std::copy(hash.
begin(), hash.
begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
83 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
89 std::copy(hash.
begin(), hash.
begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
101 enum class EntryType : uint8_t
119 coinidx_type coinidx;
127 CacheEntry entry[NUM_OUTPOINTS];
130 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
143 class CoinsViewBottom final :
public CCoinsView
145 std::map<COutPoint, Coin> m_data;
150 auto it = m_data.find(outpoint);
151 if (it == m_data.end()) {
152 if ((outpoint.n % 5) == 3) {
165 return m_data.count(outpoint);
169 std::vector<uint256>
GetHeadBlocks() const final {
return {}; }
170 std::unique_ptr<CCoinsViewCursor>
Cursor() const final {
return {}; }
171 size_t EstimateSize() const final {
return m_data.size(); }
175 for (
auto it = data.begin(); it != data.end(); it = erase ? data.erase(it) : std::next(it)) {
177 if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
178 m_data.erase(it->first);
180 m_data[it->first] = std::move(it->second.coin);
182 m_data[it->first] = it->second.coin;
186 auto it2 = m_data.find(it->first);
187 if (it->second.coin.IsSpent()) {
188 assert(it2 == m_data.end() || it2->second.IsSpent());
190 assert(it2 != m_data.end());
191 assert(it->second.coin.out == it2->second.out);
192 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
193 assert(it->second.coin.nHeight == it2->second.nHeight);
206 static const PrecomputedData data;
209 CoinsViewBottom bottom;
211 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
213 CacheLevel sim_caches[MAX_CACHES + 1];
215 uint32_t current_height = 1U;
218 sim_caches[0].Wipe();
221 auto lookup = [&](uint32_t outpointidx,
int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
222 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
224 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
225 if (entry.entrytype == EntryType::UNSPENT) {
226 return {{entry.coinidx, entry.height}};
230 if (cache_idx == 0)
break;
238 assert(caches.size() >= 1);
239 auto& cache = sim_caches[caches.size()];
240 auto& prev_cache = sim_caches[caches.size() - 1];
241 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
243 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
257 if (caches.empty()) {
259 sim_caches[caches.size()].Wipe();
269 auto sim = lookup(outpointidx);
272 auto real = caches.back()->GetCoin(data.outpoints[outpointidx], realcoin);
274 if (!sim.has_value()) {
278 const auto& simcoin = data.coins[sim->first];
288 auto sim = lookup(outpointidx);
290 auto real = caches.back()->HaveCoin(data.outpoints[outpointidx]);
292 assert(sim.has_value() == real);
298 (void)caches.back()->HaveCoinInCache(data.outpoints[outpointidx]);
304 auto sim = lookup(outpointidx);
306 const auto& realcoin = caches.back()->AccessCoin(data.outpoints[outpointidx]);
308 if (!sim.has_value()) {
309 assert(realcoin.IsSpent());
311 assert(!realcoin.IsSpent());
312 const auto& simcoin = data.coins[sim->first];
313 assert(simcoin.out == realcoin.out);
314 assert(simcoin.fCoinBase == realcoin.fCoinBase);
315 assert(realcoin.nHeight == sim->second);
323 auto sim = lookup(outpointidx);
325 Coin coin = data.coins[coinidx];
327 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), sim.has_value());
329 auto& entry = sim_caches[caches.size()].entry[outpointidx];
330 entry.entrytype = EntryType::UNSPENT;
331 entry.coinidx = coinidx;
332 entry.height = current_height;
339 Coin coin = data.coins[coinidx];
340 coin.nHeight = current_height;
341 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin),
true);
343 auto& entry = sim_caches[caches.size()].entry[outpointidx];
344 entry.entrytype = EntryType::UNSPENT;
345 entry.coinidx = coinidx;
346 entry.height = current_height;
352 caches.back()->SpendCoin(data.outpoints[outpointidx],
nullptr);
360 auto sim = lookup(outpointidx);
363 caches.back()->SpendCoin(data.outpoints[outpointidx], &realcoin);
367 if (!sim.has_value()) {
371 const auto& simcoin = data.coins[sim->first];
381 caches.back()->Uncache(data.outpoints[outpointidx]);
385 if (caches.size() != MAX_CACHES) {
389 sim_caches[caches.size()].Wipe();
395 caches.back()->SanityCheck();
403 caches.back()->Flush();
410 caches.back()->Sync();
417 caches.back()->Flush();
418 caches.back()->ReallocateCache();
422 (void)caches.back()->GetCacheSize();
426 (void)caches.back()->DynamicMemoryUsage();
436 for (
const auto& cache : caches) {
437 cache->SanityCheck();
442 for (
unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
443 auto& cache = *caches[sim_idx - 1];
444 size_t cache_size = 0;
446 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
447 cache_size += cache.HaveCoinInCache(data.outpoints[outpointidx]);
448 const auto& real = cache.AccessCoin(data.outpoints[outpointidx]);
449 auto sim = lookup(outpointidx, sim_idx);
450 if (!sim.has_value()) {
454 assert(real.out == data.coins[sim->first].out);
455 assert(real.fCoinBase == data.coins[sim->first].fCoinBase);
456 assert(real.nHeight == sim->second);
461 assert(cache.GetCacheSize() >= cache_size);
465 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
467 bool real = bottom.GetCoin(data.outpoints[outpointidx], realcoin);
468 auto sim = lookup(outpointidx, 0);
469 if (!sim.has_value()) {
473 assert(realcoin.
out == data.coins[sim->first].out);
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.
Abstract view on the open txout dataset.
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const
Retrieve the Coin (unspent transaction output) for a given outpoint.
virtual std::vector< uint256 > GetHeadBlocks() const
Retrieve the range of blocks that may have been only partially written.
virtual bool HaveCoin(const COutPoint &outpoint) const
Just check whether a given outpoint is unspent.
virtual size_t EstimateSize() const
Estimate database size (0 if not implemented)
virtual std::unique_ptr< CCoinsViewCursor > Cursor() const
Get a cursor to iterate over the whole state.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase=true)
Do a bulk modification (multiple Coin changes + BestBlock change).
virtual uint256 GetBestBlock() const
Retrieve the block hash whose state this CCoinsView currently represents.
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.
uint32_t nHeight
at which height this containing transaction was included in the active block chain
unsigned int fCoinBase
whether containing transaction was a coinbase
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t GetUint64(int pos) const
constexpr unsigned char * begin()
void resize(size_type new_size)
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher, std::equal_to< COutPoint >, PoolAllocator< std::pair< const COutPoint, CCoinsCacheEntry >, sizeof(std::pair< const COutPoint, CCoinsCacheEntry >)+sizeof(void *) *4, alignof(void *)> > CCoinsMap
PoolAllocator's MAX_BLOCK_SIZE_BYTES parameter here uses sizeof the data, and adds the size of 4 poin...
static const CAmount SPENT
FUZZ_TARGET(coinscache_sim)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
@ DIRTY
DIRTY means the CCoinsCacheEntry is potentially different from the version in the parent cache.
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)