19#include <boost/test/unit_test.hpp>
38 std::map<COutPoint, Coin> map_;
43 std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
44 if (it == map_.end()) {
59 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
62 map_[it->first] = it->second.coin;
65 map_.erase(it->first);
71 hashBestBlock_ = hashBlock;
86 for (
const auto& entry : cacheCoins) {
87 ret += entry.second.coin.DynamicMemoryUsage();
121 bool removed_all_caches =
false;
122 bool reached_4_caches =
false;
123 bool added_an_entry =
false;
124 bool added_an_unspendable_entry =
false;
125 bool removed_an_entry =
false;
126 bool updated_an_entry =
false;
127 bool found_an_entry =
false;
128 bool missed_an_entry =
false;
129 bool uncached_an_entry =
false;
132 std::map<COutPoint, Coin> result;
135 std::vector<CCoinsViewCacheTest*> stack;
136 stack.push_back(
new CCoinsViewCacheTest(base));
139 std::vector<uint256> txids;
141 for (
unsigned int i = 0; i < txids.size(); i++) {
157 bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(
COutPoint(txid, 0)) :
false;
162 if (test_havecoin_after) {
163 bool ret = stack.back()->HaveCoin(
COutPoint(txid, 0));
174 added_an_unspendable_entry =
true;
177 (coin.
IsSpent() ? added_an_entry : updated_an_entry) =
true;
182 removed_an_entry =
true;
192 stack[cacheid]->Uncache(out);
193 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
198 for (
const auto& entry : result) {
199 bool have = stack.back()->HaveCoin(entry.first);
200 const Coin& coin = stack.back()->AccessCoin(entry.first);
204 missed_an_entry =
true;
206 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
207 found_an_entry =
true;
210 for (
const CCoinsViewCacheTest *test : stack) {
219 if (fake_best_block) stack[flushIndex]->SetBestBlock(
InsecureRand256());
235 if (stack.size() > 0) {
238 removed_all_caches =
true;
240 stack.push_back(
new CCoinsViewCacheTest(tip));
241 if (stack.size() == 4) {
242 reached_4_caches =
true;
249 while (stack.size() > 0) {
277typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
283 if (utxoSetIt == utxoSet.end()) {
284 utxoSetIt = utxoSet.begin();
286 auto utxoDataIt =
utxoData.find(*utxoSetIt);
302 bool spent_a_duplicate_coinbase =
false;
304 std::map<COutPoint, Coin> result;
308 std::vector<CCoinsViewCacheTest*> stack;
309 stack.push_back(
new CCoinsViewCacheTest(&base));
312 std::set<COutPoint> coinbase_coins;
313 std::set<COutPoint> disconnected_coins;
314 std::set<COutPoint> duplicate_coins;
315 std::set<COutPoint> utxoset;
321 if (randiter % 20 < 19) {
325 tx.
vout[0].nValue = i;
331 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
338 disconnected_coins.erase(utxod->first);
340 duplicate_coins.insert(utxod->first);
353 if (randiter % 20 == 2 && disconnected_coins.size()) {
356 prevout = tx.
vin[0].prevout;
357 if (!
CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
358 disconnected_coins.erase(utxod->first);
363 if (utxoset.count(utxod->first)) {
365 assert(duplicate_coins.count(utxod->first));
367 disconnected_coins.erase(utxod->first);
373 prevout = utxod->first;
376 tx.
vin[0].prevout = prevout;
380 old_coin = result[prevout];
382 result[prevout].
Clear();
384 utxoset.erase(prevout);
388 if (duplicate_coins.count(prevout)) {
389 spent_a_duplicate_coinbase =
true;
403 utxoset.insert(outpoint);
406 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
407 }
else if (utxoset.size()) {
412 CTxUndo &undo = std::get<1>(utxod->second);
413 Coin &orig_coin = std::get<2>(utxod->second);
417 result[utxod->first].Clear();
420 result[tx.
vin[0].prevout] = orig_coin;
426 BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
434 disconnected_coins.insert(utxod->first);
437 utxoset.erase(utxod->first);
439 utxoset.insert(tx.
vin[0].prevout);
444 for (
const auto& entry : result) {
445 bool have = stack.back()->HaveCoin(entry.first);
446 const Coin& coin = stack.back()->AccessCoin(entry.first);
479 if (stack.size() > 0) {
482 stack.push_back(
new CCoinsViewCacheTest(tip));
488 while (stack.size() > 0) {
533 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
534 }
catch (
const std::ios_base::failure&) {
539 uint64_t x = 3000000000ULL;
546 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
547 }
catch (
const std::ios_base::failure&) {
571 if (value !=
SPENT) {
588 auto inserted = map.emplace(
OUTPOINT, std::move(entry));
590 return inserted.first->second.coin.DynamicMemoryUsage();
596 if (it == map.end()) {
600 if (it->second.coin.IsSpent()) {
603 value = it->second.coin.out.nValue;
605 flags = it->second.flags;
635 test.
cache.SelfTest();
686 test.
cache.SelfTest();
741 output.
nValue = modify_value;
743 test.
cache.SelfTest();
745 }
catch (std::logic_error&) {
759template <
typename... Args>
804 test.
cache.SelfTest();
806 }
catch (std::logic_error&) {
878 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
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/)
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)
Do a bulk modification (multiple Coin changes + BestBlock change).
virtual uint256 GetBestBlock() const
Retrieve the block hash whose state this CCoinsView currently represents.
Double ended buffer combining vector and stream-like interfaces.
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
CCoinsViewCacheTest cache
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
void assign(size_type n, const T &val)
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
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 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
void GetCoinsMapEntry(const CCoinsMap &map, CAmount &value, char &flags)
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
static const auto ABSENT_FLAGS
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_FIXTURE_TEST_SUITE(a, b)
#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.
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< unsigned char > ParseHex(const char *psz)
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