20 #include <boost/test/unit_test.hpp> 39 std::map<COutPoint, Coin> map_;
44 std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
45 if (it == map_.end()) {
61 if (it->second.IsDirty()) {
63 map_[it->first] = it->second.coin;
66 map_.erase(it->first);
71 hashBestBlock_ = hashBlock;
86 for (
const auto& entry : cacheCoins) {
87 ret += entry.second.coin.DynamicMemoryUsage();
125 bool removed_all_caches =
false;
126 bool reached_4_caches =
false;
127 bool added_an_entry =
false;
128 bool added_an_unspendable_entry =
false;
129 bool removed_an_entry =
false;
130 bool updated_an_entry =
false;
131 bool found_an_entry =
false;
132 bool missed_an_entry =
false;
133 bool uncached_an_entry =
false;
134 bool flushed_without_erase =
false;
137 std::map<COutPoint, Coin> result;
140 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
141 stack.push_back(std::make_unique<CCoinsViewCacheTest>(base));
144 std::vector<Txid> txids;
146 for (
unsigned int i = 0; i < txids.size(); i++) {
162 bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(
COutPoint(txid, 0)) :
false;
170 if (test_havecoin_before) {
174 if (test_havecoin_after) {
188 added_an_unspendable_entry =
true;
192 (coin.IsSpent() ? added_an_entry : updated_an_entry) =
true;
196 stack.back()->AddCoin(
COutPoint(txid, 0), std::move(newcoin), is_overwrite);
199 removed_an_entry =
true;
209 stack[cacheid]->Uncache(
out);
210 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(
out);
215 for (
const auto& entry : result) {
216 bool have = stack.back()->HaveCoin(entry.first);
217 const Coin& coin = stack.back()->AccessCoin(entry.first);
221 missed_an_entry =
true;
223 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
224 found_an_entry =
true;
227 for (
const auto& test : stack) {
236 if (fake_best_block) stack[flushIndex]->SetBestBlock(
InsecureRand256());
238 BOOST_CHECK(should_erase ? stack[flushIndex]->Flush() : stack[flushIndex]->Sync());
239 flushed_without_erase |= !should_erase;
248 BOOST_CHECK(should_erase ? stack.back()->Flush() : stack.back()->Sync());
249 flushed_without_erase |= !should_erase;
255 if (stack.size() > 0) {
256 tip = stack.back().get();
258 removed_all_caches =
true;
260 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
261 if (stack.size() == 4) {
262 reached_4_caches =
true;
287 CCoinsViewDB db_base{{.path =
"test", .cache_bytes = 1 << 23, .memory_only =
true}, {}};
292 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
298 if (utxoSetIt == utxoSet.end()) {
299 utxoSetIt = utxoSet.begin();
301 auto utxoDataIt =
utxoData.find(*utxoSetIt);
316 bool spent_a_duplicate_coinbase =
false;
318 std::map<COutPoint, Coin> result;
322 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
323 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
326 std::set<COutPoint> coinbase_coins;
327 std::set<COutPoint> disconnected_coins;
328 std::set<COutPoint> duplicate_coins;
329 std::set<COutPoint> utxoset;
335 if (randiter % 20 < 19) {
339 tx.
vout[0].nValue = i;
345 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
352 disconnected_coins.erase(utxod->first);
354 duplicate_coins.insert(utxod->first);
367 if (randiter % 20 == 2 && disconnected_coins.size()) {
370 prevout = tx.
vin[0].prevout;
372 disconnected_coins.erase(utxod->first);
377 if (utxoset.count(utxod->first)) {
379 assert(duplicate_coins.count(utxod->first));
381 disconnected_coins.erase(utxod->first);
387 prevout = utxod->first;
390 tx.
vin[0].prevout = prevout;
394 old_coin = result[prevout];
396 result[prevout].
Clear();
398 utxoset.erase(prevout);
402 if (duplicate_coins.count(prevout)) {
403 spent_a_duplicate_coinbase =
true;
417 utxoset.insert(outpoint);
420 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
421 }
else if (utxoset.size()) {
426 CTxUndo &undo = std::get<1>(utxod->second);
427 Coin &orig_coin = std::get<2>(utxod->second);
431 result[utxod->first].
Clear();
434 result[tx.
vin[0].prevout] = orig_coin;
440 BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
444 Coin coin = undo.vprevout[0];
448 disconnected_coins.insert(utxod->first);
451 utxoset.erase(utxod->first);
453 utxoset.insert(tx.
vin[0].prevout);
458 for (
const auto& entry : result) {
459 bool have = stack.back()->HaveCoin(entry.first);
460 const Coin& coin = stack.back()->AccessCoin(entry.first);
492 if (stack.size() > 0) {
493 tip = stack.back().get();
495 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
538 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
539 }
catch (
const std::ios_base::failure&) {
544 uint64_t x = 3000000000ULL;
551 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
552 }
catch (
const std::ios_base::failure&) {
576 if (value !=
SPENT) {
592 auto inserted = map.emplace(
OUTPOINT, std::move(entry));
594 inserted.first->second.AddFlags(
flags, *inserted.first, sentinel);
595 return inserted.first->second.coin.DynamicMemoryUsage();
600 auto it = map.find(outp);
601 if (it == map.end()) {
605 if (it->second.coin.IsSpent()) {
608 value = it->second.coin.out.nValue;
610 flags = it->second.GetFlags();
618 sentinel.second.SelfRef(sentinel);
620 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
644 test.
cache.SelfTest(
false);
695 test.
cache.SelfTest();
750 output.
nValue = modify_value;
752 test.
cache.SelfTest();
754 }
catch (std::logic_error&) {
768 template <
typename... Args>
813 test.
cache.SelfTest(
false);
815 }
catch (std::logic_error&) {
887 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
912 CCoinsViewCacheTest* view,
914 std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,
915 bool do_erasing_flush)
922 auto flush_all = [&all_caches](
bool erase) {
924 for (
auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {
926 cache->SanityCheck();
930 erase ? cache->Flush() : cache->Sync();
943 view->AddCoin(outp,
Coin(coin),
false);
945 cache_usage = view->DynamicMemoryUsage();
946 cache_size = view->map().size();
974 if (do_erasing_flush) {
980 BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage);
982 BOOST_TEST(view->map().size() < cache_size);
990 view->AccessCoin(outp);
999 view->AddCoin(outp,
Coin(coin),
false),
1032 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1033 all_caches[0]->Sync();
1036 BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));
1057 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1068 all_caches[0]->Sync();
1074 BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
1081 CCoinsViewDB base{{.path =
"test", .cache_bytes = 1 << 23, .memory_only =
true}, {}};
1082 std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches;
1083 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1084 caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get()));
1086 for (
const auto& view : caches) {
1098 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
1107 for (
size_t i = 0; i < 1000; ++i) {
CoinsCachePair * NextAndMaybeErase(CoinsCachePair ¤t) noexcept
Return the next entry after current, possibly erasing current.
static const unsigned int NUM_SIMULATION_ITERATIONS
bool IsSpent() const
Either this coin never existed (see e.g.
static const auto ABSENT_FLAGS
int ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoint &out)
Restore the UTXO in a Coin at a given COutPoint.
void assign(size_type n, const T &val)
void GetCoinsMapEntry(const CCoinsMap &map, CAmount &value, char &flags, const COutPoint &outp=OUTPOINT)
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
static const CAmount ABSENT
CoinsCachePair m_sentinel
A Coin in one level of the coins database caching hierarchy.
#define BOOST_CHECK_THROW(stmt, excMatch)
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...
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const
Retrieve the Coin (unspent transaction output) for a given outpoint.
bool operator==(const CNetAddr &a, const CNetAddr &b)
static bool InsecureRandBool()
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...
CoinsCachePair * End() const noexcept
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
virtual bool BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &hashBlock)
Do a bulk modification (multiple Coin changes + BestBlock change).
static const CAmount SPENT
size_t DynamicMemoryUsage() const
Calculate the size of the cache (in bytes)
CTxOut out
unspent transaction output
unsigned int fCoinBase
whether containing transaction was a coinbase
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
static const CAmount VALUE2
CCoinsMap::allocator_type::ResourceType CCoinsMapMemoryResource
static const char NO_ENTRY
static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
const std::vector< CTxIn > vin
static const CAmount VALUE1
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
DIRTY means the CCoinsCacheEntry is potentially different from the version in the parent cache...
const Coin & AccessByTxid(const CCoinsViewCache &view, const Txid &txid)
Utility function to find any unspent output with a given txid.
bool IsUnspendable() const
Returns whether the script is guaranteed to fail at execution, regardless of the initial stack...
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 ...
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
std::pair< const COutPoint, CCoinsCacheEntry > CoinsCachePair
static uint64_t InsecureRandRange(uint64_t range)
Abstract view on the open txout dataset.
Double ended buffer combining vector and stream-like interfaces.
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
BOOST_AUTO_TEST_SUITE_END()
static void SetCoinsValue(CAmount value, Coin &coin)
void WriteCoinsViewEntry(CCoinsView &view, CAmount value, char flags)
An output of a transaction.
Txid GetHash() const
Compute the hash of this CMutableTransaction.
void SeedRandomForTest(SeedRand seedtype)
Seed the RNG for testing.
static uint256 InsecureRand256()
An outpoint - a combination of a transaction hash and an index n into its vout.
std::vector< CTxOut > vout
constexpr bool IsNull() const
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight)
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
static const CAmount FAIL
CoinsCachePair * Begin() const noexcept
void SimulationTest(CCoinsView *base, bool fake_best_block)
UtxoData::iterator FindRandomFrom(const std::set< COutPoint > &utxoSet)
std::map< COutPoint, std::tuple< CTransaction, CTxUndo, Coin > > UtxoData
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: ...
static bool sanity_check(const std::vector< CTransactionRef > &transactions, const std::map< COutPoint, CAmount > &bumpfees)
static const COutPoint OUTPOINT
static void CheckAddCoin(Args &&... args)
FRESH means the parent cache does not have this coin or that it is a spent coin in the parent cache...
#define BOOST_CHECK_EQUAL(v1, v2)
static transaction_identifier FromUint256(const uint256 &id)
Undo information for a CTransaction.
CCoinsView backed by the coin database (chainstate/)
static const CAmount VALUE3
virtual uint256 GetBestBlock() const
Retrieve the block hash whose state this CCoinsView currently represents.
static uint32_t InsecureRand32()
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
static uint64_t InsecureRandBits(int bits)
A mutable version of CTransaction.
static const auto CLEAN_FLAGS
The basic transaction that is broadcasted on the network and contained in blocks. ...
void SanityCheck() const
Run an internal sanity check on the cache data structure. */.
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Seed with a compile time constant of zeros.
CCoinsViewCacheTest cache
static size_t InsertCoinsMapEntry(CCoinsMap &map, CoinsCachePair &sentinel, CAmount value, char flags)
#define BOOST_CHECK(expr)
static CAmount InsecureRandMoneyAmount()