23#include <boost/test/unit_test.hpp>
35 if (
a.IsSpent() && b.
IsSpent())
return true;
45 std::map<COutPoint, Coin> map_;
50 std::optional<Coin> GetCoin(
const COutPoint& outpoint)
const override
52 if (
auto it{map_.find(outpoint)}; it != map_.end() && !it->second.IsSpent())
return it->second;
56 uint256 GetBestBlock()
const override {
return hashBestBlock_; }
61 if (it->second.IsDirty()) {
63 map_[it->first] = it->second.coin;
64 if (it->second.coin.IsSpent() && m_rng.
randrange(3) == 0) {
66 map_.erase(it->first);
71 hashBestBlock_ = hashBlock;
85 for (
const auto& entry : cacheCoins) {
86 ret += entry.second.coin.DynamicMemoryUsage();
98 size_t&
usage()
const {
return cachedCoinsUsage; }
99 size_t& dirty()
const {
return m_dirty_count; }
136 std::map<COutPoint, Coin> result;
139 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
140 stack.push_back(std::make_unique<CCoinsViewCacheTest>(base));
143 std::vector<Txid> txids;
145 for (
unsigned int i = 0; i < txids.size(); i++) {
195 stack.back()->EmplaceCoinInternalDANGER(std::move(op), std::move(
newcoin));
217 for (
const auto& entry : result) {
218 bool have = stack.back()->HaveCoin(entry.first);
219 const Coin& coin = stack.back()->AccessCoin(entry.first);
225 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
229 for (
const auto& test : stack) {
250 should_erase ? stack.back()->Flush() : stack.back()->Sync();
254 if (stack.size() == 0 || (stack.size() < 4 &&
m_rng.
randbool())) {
257 if (stack.size() > 0) {
258 tip = stack.back().get();
262 stack.push_back(std::make_unique<CCoinsViewCacheTest>(
tip));
263 if (stack.size() == 4) {
289 CCoinsViewTest base{m_rng};
290 SimulationTest(&base,
false);
300 SimulationTest(&
db_base,
true);
309typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
336 std::map<COutPoint, Coin> result;
339 CCoinsViewTest base{m_rng};
340 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
341 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
357 tx.
vout[0].nValue = i;
358 tx.
vout[0].scriptPubKey.assign(m_rng.
rand32() & 0x3F, 0);
359 const int height{
int(m_rng.
rand32() >> 1)};
388 prevout = tx.
vin[0].prevout;
405 prevout =
utxod->first;
408 tx.
vin[0].prevout = prevout;
414 result[prevout].Clear();
438 utxoData.emplace(outpoint, std::make_tuple(tx,
undo,
old_coin));
449 result[
utxod->first].Clear();
476 for (
const auto& entry : result) {
477 bool have = stack.back()->HaveCoin(entry.first);
478 const Coin& coin = stack.back()->AccessCoin(entry.first);
497 if (stack.size() > 1 && m_rng.
randbool() == 0) {
504 if (stack.size() > 0 && m_rng.
randbool() == 0) {
505 stack.back()->Flush();
508 if (stack.size() == 0 || (stack.size() < 4 && m_rng.
randbool())) {
510 if (stack.size() > 0) {
511 tip = stack.back().get();
513 stack.push_back(std::make_unique<CCoinsViewCacheTest>(
tip));
553 }
catch (
const std::ios_base::failure&) {
565 }
catch (
const std::ios_base::failure&) {
618constexpr auto EX_OVERWRITE_UNSPENT{
"Attempted to overwrite an unspent coin (when possible_overwrite is false)"};
626 if (value !=
SPENT) {
641 return iter->second.coin.DynamicMemoryUsage();
646 if (
auto it{
map.find(
outp)}; it !=
map.end()) {
648 it->second.coin.IsSpent() ?
SPENT : it->second.coin.out.nValue,
659 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
690 test.cache.SelfTest(
false);
720 test.cache.SelfTest();
751 if (
auto*
expected_coin{std::get_if<MaybeCoin>(&expected)}) {
753 test.cache.SelfTest();
796 if (
auto*
expected_coin{std::get_if<MaybeCoin>(&expected)}) {
798 test.cache.SelfTest(
false);
900 CCoinsViewCacheTest*
view,
902 std::vector<std::unique_ptr<CCoinsViewCacheTest>>&
all_caches,
912 cache->SanityCheck();
916 erase ? cache->Flush() : cache->Sync();
1052 CCoinsViewDB base{{.path =
"test", .cache_bytes = 1 << 23, .memory_only =
true}, {}};
1053 std::vector<std::unique_ptr<CCoinsViewCacheTest>>
caches;
1054 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1055 caches.push_back(std::make_unique<CCoinsViewCacheTest>(
caches.back().get()));
1058 TestFlushBehavior(
view.get(), base,
caches,
false);
1059 TestFlushBehavior(
view.get(), base,
caches,
true);
1069 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
1078 for (
size_t i = 0; i < 1000; ++i) {
1091 CCoinsViewCacheTest cache{&root};
1096 cache.AddCoin(outpoint,
Coin{
coin1},
false);
1109 CCoinsViewCacheTest cache{&root};
1126 CCoinsViewTest root{m_rng};
1137 cache.EmplaceCoinInternalDANGER(
COutPoint{outpoint},
Coin{coin});
1146 BOOST_CHECK(!cache.AccessCoin(outpoint).IsSpent());
1153 BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
1164 BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
1177 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)
CCoinsView that adds a memory cache for transactions to another CCoinsView.
void AddCoin(const COutPoint &outpoint, Coin &&coin, bool possible_overwrite)
Add a coin.
CCoinsView backed by the coin database (chainstate/)
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
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.
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
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)
static void add_coin(const CAmount &nValue, int nInput, std::vector< OutputGroup > &set)
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, SaltedOutpointHasher, 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)
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.
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...
bool operator==(const CNetAddr &a, const CNetAddr &b)
#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)
@ 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.