26#include <boost/test/unit_test.hpp>
40 std::map<COutPoint, Coin> map_;
47 if (
auto it{map_.find(outpoint)}; it != map_.end() && !it->second.IsSpent())
return it->second;
56 if (it->second.IsDirty()) {
58 map_[it->first] = it->second.coin;
59 if (it->second.coin.IsSpent() && m_rng.
randrange(3) == 0) {
61 map_.erase(it->first);
66 hashBestBlock_ = block_hash;
80 for (
const auto& entry : cacheCoins) {
81 ret += entry.second.coin.DynamicMemoryUsage();
131 std::map<COutPoint, Coin> result;
134 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
135 stack.push_back(std::make_unique<CCoinsViewCacheTest>(base));
138 std::vector<Txid> txids;
140 for (
unsigned int i = 0; i < txids.size(); i++) {
190 stack.back()->EmplaceCoinInternalDANGER(op, std::move(
newcoin));
212 for (
const auto& entry : result) {
213 bool have = stack.back()->HaveCoin(entry.first);
214 const Coin& coin = stack.back()->AccessCoin(entry.first);
220 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
224 for (
const auto& test : stack) {
245 should_erase ? stack.back()->Flush() : stack.back()->Sync();
249 if (stack.size() == 0 || (stack.size() < 4 &&
m_rng.
randbool())) {
252 if (stack.size() > 0) {
253 tip = stack.back().get();
257 stack.push_back(std::make_unique<CCoinsViewCacheTest>(
tip));
258 if (stack.size() == 4) {
284 CCoinsViewTest base{m_rng};
285 SimulationTest(&base,
false);
295 SimulationTest(&
db_base,
true);
304typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
331 std::map<COutPoint, Coin> result;
334 CCoinsViewTest base{m_rng};
335 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
336 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
352 tx.
vout[0].nValue = i;
353 tx.
vout[0].scriptPubKey.assign(m_rng.
rand32() & 0x3F, 0);
354 const int height{
int(m_rng.
rand32() >> 1)};
383 prevout = tx.
vin[0].prevout;
400 prevout =
utxod->first;
403 tx.
vin[0].prevout = prevout;
409 result[prevout].Clear();
433 utxoData.emplace(outpoint, std::make_tuple(tx,
undo,
old_coin));
444 result[
utxod->first].Clear();
471 for (
const auto& entry : result) {
472 bool have = stack.back()->HaveCoin(entry.first);
473 const Coin& coin = stack.back()->AccessCoin(entry.first);
492 if (stack.size() > 1 && m_rng.
randbool() == 0) {
499 if (stack.size() > 0 && m_rng.
randbool() == 0) {
500 stack.back()->Flush();
503 if (stack.size() == 0 || (stack.size() < 4 && m_rng.
randbool())) {
505 if (stack.size() > 0) {
506 tip = stack.back().get();
508 stack.push_back(std::make_unique<CCoinsViewCacheTest>(
tip));
603constexpr auto EX_OVERWRITE_UNSPENT{
"Attempted to overwrite an unspent coin (when possible_overwrite is false)"};
611 if (value !=
SPENT) {
626 return iter->second.coin.DynamicMemoryUsage();
631 if (
auto it{
map.find(
outp)}; it !=
map.end()) {
633 it->second.coin.IsSpent() ?
SPENT : it->second.coin.out.nValue,
644 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
674 test.cache.SelfTest(
false);
704 test.cache.SelfTest();
735 if (
auto*
expected_coin{std::get_if<MaybeCoin>(&expected)}) {
737 test.cache.SelfTest();
780 if (
auto*
expected_coin{std::get_if<MaybeCoin>(&expected)}) {
782 test.cache.SelfTest(
false);
884 CCoinsViewCacheTest*
view,
886 std::vector<std::unique_ptr<CCoinsViewCacheTest>>&
all_caches,
896 cache->SanityCheck();
900 erase ? cache->Flush() : cache->Sync();
1036 CCoinsViewDB base{{.path =
"test", .cache_bytes = 8
_MiB, .memory_only =
true}, {}};
1037 std::vector<std::unique_ptr<CCoinsViewCacheTest>>
caches;
1038 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1039 caches.push_back(std::make_unique<CCoinsViewCacheTest>(
caches.back().get()));
1042 TestFlushBehavior(
view.get(), base,
caches,
false);
1043 TestFlushBehavior(
view.get(), base,
caches,
true);
1053 const Coin coin{MakeCoin()};
1056 CCoinsViewDB base{{.path = m_args.GetDataDirBase() /
"coins_db_leveldb_layout", .cache_bytes = 1
_MiB, .wipe_data =
true}, {}};
1060 cache.SetBestBlock(block_hash);
1077 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
1086 for (
size_t i = 0; i < 1000; ++i) {
1103 cache.AddCoin(outpoint,
Coin{
coin1},
false);
1120 cache.EmplaceCoinInternalDANGER(outpoint,
Coin{
coin1});
1124 cache.EmplaceCoinInternalDANGER(outpoint,
Coin{
coin2});
1132 CCoinsViewTest root{m_rng};
1143 cache.EmplaceCoinInternalDANGER(outpoint,
Coin{coin});
1152 BOOST_CHECK(!cache.AccessCoin(outpoint).IsSpent());
1159 BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
1170 BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
1183 CCoinsViewTest base{m_rng};
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
int64_t CAmount
Amount in satoshis (Can be negative)
#define Assert(val)
Identity function.
CCoinsView that adds a memory cache for transactions to another CCoinsView.
void EmplaceCoinInternalDANGER(const COutPoint &outpoint, Coin &&coin)
Emplace a coin into cacheCoins without performing any checks, marking the emplaced coin as dirty.
void AddCoin(const COutPoint &outpoint, Coin &&coin, bool possible_overwrite)
Add a coin.
unsigned int GetCacheSize() const
Size of the cache (in number of transaction outputs)
CoinsCachePair m_sentinel
size_t DynamicMemoryUsage() const
Calculate the size of the cache (in bytes)
void SanityCheck() const
Run an internal sanity check on the cache data structure. */.
CCoinsView backed by the coin database (chainstate/)
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
Pure abstract view on the open txout dataset.
An outpoint - a combination of a transaction hash and an index n into its vout.
Serialized script, used inside transaction inputs and outputs.
The basic transaction that is broadcasted on the network and contained in blocks.
const std::vector< CTxIn > vin
An output of a transaction.
Undo information for a CTransaction.
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).
uint256 GetBestBlock() const override
Retrieve the block hash whose state this CCoinsView currently represents.
std::optional< Coin > GetCoin(const COutPoint &) const override
Retrieve the Coin (unspent transaction output) for a given outpoint.
static CoinsViewEmpty & Get()
Double ended buffer combining vector and stream-like interfaces.
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
static void CheckAllDataAccountedFor(const PoolResource< MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES > &resource)
Once all blocks are given back to the resource, tests that the freelists are consistent:
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
uint256 rand256() noexcept
generate a random uint256.
bool randbool() noexcept
Generate a random boolean.
std::vector< B > randbytes(size_t len) noexcept
Generate random bytes.
uint32_t rand32() noexcept
Generate a random 32-bit integer.
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
CCoinsViewCacheTest cache
SingleEntryCacheTest(const CAmount base_value, const MaybeCoin &cache_coin)
Minimal stream for reading from an existing byte array by std::span.
constexpr bool IsNull() const
static constexpr unsigned int STATIC_SIZE
static transaction_identifier FromUint256(const uint256 &id)
const Coin & AccessByTxid(const CCoinsViewCache &view, const Txid &txid)
Utility function to find any unspent output with a given txid.
std::pair< const COutPoint, CCoinsCacheEntry > CoinsCachePair
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedCoinsCacheHasher, std::equal_to< COutPoint >, PoolAllocator< CoinsCachePair, sizeof(CoinsCachePair)+sizeof(void *) *4 > > CCoinsMap
PoolAllocator's MAX_BLOCK_SIZE_BYTES parameter here uses sizeof the data, and adds the size of 4 poin...
CCoinsMap::allocator_type::ResourceType CCoinsMapMemoryResource
BOOST_AUTO_TEST_CASE(ccoins_serialization)
constexpr MaybeCoin VALUE2_DIRTY
std::optional< CoinEntry > MaybeCoin
constexpr MaybeCoin VALUE2_CLEAN
constexpr MaybeCoin MISSING
static MaybeCoin GetCoinsMapEntry(const CCoinsMap &map, const COutPoint &outp=OUTPOINT)
static const COutPoint OUTPOINT
constexpr MaybeCoin VALUE2_DIRTY_FRESH
static void WriteCoinsViewEntry(CCoinsView &view, const MaybeCoin &cache_coin)
static void CheckWriteCoins(const MaybeCoin &parent, const MaybeCoin &child, const CoinOrError &expected)
int ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoint &out)
Restore the UTXO in a Coin at a given COutPoint.
static const unsigned int NUM_SIMULATION_ITERATIONS
constexpr MaybeCoin VALUE1_CLEAN
constexpr MaybeCoin SPENT_DIRTY_FRESH
constexpr MaybeCoin SPENT_CLEAN
constexpr auto EX_OVERWRITE_UNSPENT
constexpr MaybeCoin VALUE1_DIRTY
constexpr MaybeCoin VALUE1_FRESH
static size_t InsertCoinsMapEntry(CCoinsMap &map, CoinsCachePair &sentinel, const CoinEntry &cache_coin)
static void CheckSpendCoins(const CAmount base_value, const MaybeCoin &cache_coin, const MaybeCoin &expected)
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight)
constexpr MaybeCoin VALUE2_FRESH
constexpr MaybeCoin VALUE3_DIRTY_FRESH
static void CheckAccessCoin(const CAmount base_value, const MaybeCoin &cache_coin, const MaybeCoin &expected)
constexpr MaybeCoin VALUE3_DIRTY
constexpr MaybeCoin VALUE1_DIRTY_FRESH
constexpr MaybeCoin SPENT_DIRTY
constexpr auto EX_FRESH_MISAPPLIED
std::variant< MaybeCoin, std::string > CoinOrError
static void SetCoinsValue(const CAmount value, Coin &coin)
BOOST_FIXTURE_TEST_CASE(coins_cache_base_simulation_test, CacheTest)
constexpr MaybeCoin SPENT_FRESH
static void CheckAddCoin(const CAmount base_value, const MaybeCoin &cache_coin, const CAmount modify_value, const CoinOrError &expected, const bool coinbase)
std::vector< std::unique_ptr< CCoinsViewCache > > caches
Real CCoinsViewCache objects.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"Empty HTTP header name"})
static bool sanity_check(const std::vector< CTransactionRef > &transactions, const std::map< COutPoint, CAmount > &bumpfees)
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
""_hex is a compile-time user-defined literal returning a std::array<std::byte>, equivalent to ParseH...
#define BOOST_CHECK_THROW(stmt, excMatch)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
A Coin in one level of the coins database caching hierarchy.
static void SetFresh(CoinsCachePair &pair, CoinsCachePair &sentinel) noexcept
static void SetDirty(CoinsCachePair &pair, CoinsCachePair &sentinel) noexcept
A mutable version of CTransaction.
std::vector< CTxOut > vout
Txid GetHash() const
Compute the hash of this CMutableTransaction.
void SimulationTest(CCoinsView *base, bool fake_best_block)
constexpr bool IsDirty() const
friend std::ostream & operator<<(std::ostream &os, const CoinEntry &e)
bool operator==(const CoinEntry &o) const =default
constexpr bool IsDirtyFresh() const
static constexpr State ToState(const bool is_dirty, const bool is_fresh)
constexpr bool IsFresh() const
constexpr CoinEntry(const CAmount v, const State s)
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
void TestFlushBehavior(CCoinsViewCacheTest *view, CCoinsViewDB &base, std::vector< std::unique_ptr< CCoinsViewCacheTest > > &all_caches, bool do_erasing_flush)
For CCoinsViewCache instances backed by either another cache instance or leveldb, test cache behavior...
std::map< COutPoint, std::tuple< CTransaction, CTxUndo, Coin > > UtxoData
UtxoData::iterator FindRandomFrom(const std::set< COutPoint > &utxoSet)
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
@ ZEROS
Seed with a compile time constant of zeros.
CAmount RandMoney(Rng &&rng)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.