18 #include <boost/test/unit_test.hpp>
37 std::map<COutPoint, Coin> map_;
42 std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
43 if (it == map_.end()) {
58 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = erase ? mapCoins.erase(it) : std::next(it)) {
61 map_[it->first] = it->second.coin;
64 map_.erase(it->first);
69 hashBestBlock_ = hashBlock;
84 for (
const auto& entry : cacheCoins) {
85 ret += entry.second.coin.DynamicMemoryUsage();
119 bool removed_all_caches =
false;
120 bool reached_4_caches =
false;
121 bool added_an_entry =
false;
122 bool added_an_unspendable_entry =
false;
123 bool removed_an_entry =
false;
124 bool updated_an_entry =
false;
125 bool found_an_entry =
false;
126 bool missed_an_entry =
false;
127 bool uncached_an_entry =
false;
128 bool flushed_without_erase =
false;
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<uint256> txids;
140 for (
unsigned int i = 0; i < txids.size(); i++) {
156 bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(
COutPoint(txid, 0)) :
false;
164 if (test_havecoin_before) {
168 if (test_havecoin_after) {
182 added_an_unspendable_entry =
true;
186 (coin.
IsSpent() ? added_an_entry : updated_an_entry) =
true;
190 stack.back()->AddCoin(
COutPoint(txid, 0), std::move(newcoin), is_overwrite);
193 removed_an_entry =
true;
203 stack[cacheid]->Uncache(out);
204 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
209 for (
const auto& entry : result) {
210 bool have = stack.back()->HaveCoin(entry.first);
211 const Coin& coin = stack.back()->AccessCoin(entry.first);
215 missed_an_entry =
true;
217 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
218 found_an_entry =
true;
221 for (
const auto& test : stack) {
230 if (fake_best_block) stack[flushIndex]->SetBestBlock(
InsecureRand256());
232 BOOST_CHECK(should_erase ? stack[flushIndex]->Flush() : stack[flushIndex]->Sync());
233 flushed_without_erase |= !should_erase;
242 BOOST_CHECK(should_erase ? stack.back()->Flush() : stack.back()->Sync());
243 flushed_without_erase |= !should_erase;
249 if (stack.size() > 0) {
250 tip = stack.back().get();
252 removed_all_caches =
true;
254 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
255 if (stack.size() == 4) {
256 reached_4_caches =
true;
286 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
292 if (utxoSetIt == utxoSet.end()) {
293 utxoSetIt = utxoSet.begin();
295 auto utxoDataIt =
utxoData.find(*utxoSetIt);
311 bool spent_a_duplicate_coinbase =
false;
313 std::map<COutPoint, Coin> result;
317 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
318 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
321 std::set<COutPoint> coinbase_coins;
322 std::set<COutPoint> disconnected_coins;
323 std::set<COutPoint> duplicate_coins;
324 std::set<COutPoint> utxoset;
330 if (randiter % 20 < 19) {
334 tx.
vout[0].nValue = i;
340 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
347 disconnected_coins.erase(utxod->first);
349 duplicate_coins.insert(utxod->first);
362 if (randiter % 20 == 2 && disconnected_coins.size()) {
365 prevout = tx.
vin[0].prevout;
366 if (!
CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
367 disconnected_coins.erase(utxod->first);
372 if (utxoset.count(utxod->first)) {
374 assert(duplicate_coins.count(utxod->first));
376 disconnected_coins.erase(utxod->first);
382 prevout = utxod->first;
385 tx.
vin[0].prevout = prevout;
389 old_coin = result[prevout];
391 result[prevout].
Clear();
393 utxoset.erase(prevout);
397 if (duplicate_coins.count(prevout)) {
398 spent_a_duplicate_coinbase =
true;
412 utxoset.insert(outpoint);
415 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
416 }
else if (utxoset.size()) {
421 CTxUndo &undo = std::get<1>(utxod->second);
422 Coin &orig_coin = std::get<2>(utxod->second);
426 result[utxod->first].Clear();
429 result[tx.
vin[0].prevout] = orig_coin;
435 BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
443 disconnected_coins.insert(utxod->first);
446 utxoset.erase(utxod->first);
448 utxoset.insert(tx.
vin[0].prevout);
453 for (
const auto& entry : result) {
454 bool have = stack.back()->HaveCoin(entry.first);
455 const Coin& coin = stack.back()->AccessCoin(entry.first);
487 if (stack.size() > 0) {
488 tip = stack.back().get();
490 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
535 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
536 }
catch (
const std::ios_base::failure&) {
541 uint64_t x = 3000000000ULL;
548 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
549 }
catch (
const std::ios_base::failure&) {
573 if (value !=
SPENT) {
590 auto inserted = map.emplace(
OUTPOINT, std::move(entry));
592 return inserted.first->second.coin.DynamicMemoryUsage();
597 auto it = map.find(outp);
598 if (it == map.end()) {
602 if (it->second.coin.IsSpent()) {
605 value = it->second.coin.out.nValue;
607 flags = it->second.flags;
637 test.
cache.SelfTest();
688 test.
cache.SelfTest();
743 output.
nValue = modify_value;
745 test.
cache.SelfTest();
747 }
catch (std::logic_error&) {
761 template <
typename... Args>
806 test.
cache.SelfTest();
808 }
catch (std::logic_error&) {
880 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
905 CCoinsViewCacheTest* view,
907 std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,
908 bool do_erasing_flush)
914 auto flush_all = [&all_caches](
bool erase) {
916 for (
auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {
921 erase ? cache->Flush() : cache->Sync();
934 view->AddCoin(outp,
Coin(coin),
false);
936 cache_usage = view->DynamicMemoryUsage();
962 if (do_erasing_flush) {
968 BOOST_CHECK(view->DynamicMemoryUsage() < cache_usage);
976 view->AccessCoin(outp);
985 view->AddCoin(outp,
Coin(coin),
false),
1018 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1019 all_caches[0]->Sync();
1022 BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));
1043 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1054 all_caches[0]->Sync();
1060 BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
1068 std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches;
1069 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1070 caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get()));
1072 for (
const auto& view : caches) {
int64_t CAmount
Amount in satoshis (Can be negative)
CCoinsView that adds a memory cache for transactions to another CCoinsView.
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
size_t DynamicMemoryUsage() const
Calculate the size of the cache (in bytes)
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.
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const
Retrieve the Coin (unspent transaction output) for a given outpoint.
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.
bool IsUnspendable() const
Returns whether the script is guaranteed to fail at execution, regardless of the initial stack.
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.
std::vector< Coin > vprevout
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.
CCoinsViewCacheTest cache
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
constexpr bool IsNull() const
void assign(size_type n, const T &val)
const Coin & AccessByTxid(const CCoinsViewCache &view, const uint256 &txid)
Utility function to find any unspent output with a given txid.
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher > CCoinsMap
static void CheckAddCoin(Args &&... args)
static const COutPoint OUTPOINT
static const CAmount ABSENT
void WriteCoinsViewEntry(CCoinsView &view, CAmount value, char flags)
static const CAmount VALUE2
static const CAmount SPENT
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
std::map< COutPoint, std::tuple< CTransaction, CTxUndo, Coin > > UtxoData
static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
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...
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight)
static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
static void SetCoinsValue(CAmount value, Coin &coin)
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
static const CAmount VALUE1
static const char NO_ENTRY
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
static const auto ABSENT_FLAGS
void GetCoinsMapEntry(const CCoinsMap &map, CAmount &value, char &flags, const COutPoint &outp=OUTPOINT)
static const CAmount VALUE3
static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
static size_t InsertCoinsMapEntry(CCoinsMap &map, CAmount value, char flags)
void SimulationTest(CCoinsView *base, bool fake_best_block)
UtxoData::iterator FindRandomFrom(const std::set< COutPoint > &utxoSet)
static const auto CLEAN_FLAGS
static const CAmount FAIL
BOOST_AUTO_TEST_SUITE_END()
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
bool operator==(const CNetAddr &a, const CNetAddr &b)
#define BOOST_CHECK_THROW(stmt, excMatch)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
bool g_mock_deterministic_tests
Flag to make GetRand in random.h return the same number.
@ ZEROS
Seed with a compile time constant of zeros.
static uint64_t InsecureRandRange(uint64_t range)
static uint256 InsecureRand256()
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
static uint64_t InsecureRandBits(int bits)
static uint32_t InsecureRand32()
static bool InsecureRandBool()
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
A Coin in one level of the coins database caching hierarchy.
@ FRESH
FRESH means the parent cache does not have this coin or that it is a spent coin in the parent cache.
@ DIRTY
DIRTY means the CCoinsCacheEntry is potentially different from the version in the parent cache.
A mutable version of CTransaction.
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
std::vector< CTxOut > vout
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< Byte > ParseHex(std::string_view str)
Parse the hex string into bytes (uint8_t or std::byte).