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(); ) {
61 map_[it->first] = it->second.coin;
64 map_.erase(it->first);
70 hashBestBlock_ = hashBlock;
85 for (
const auto& entry : cacheCoins) {
86 ret += entry.second.coin.DynamicMemoryUsage();
120 bool removed_all_caches =
false;
121 bool reached_4_caches =
false;
122 bool added_an_entry =
false;
123 bool added_an_unspendable_entry =
false;
124 bool removed_an_entry =
false;
125 bool updated_an_entry =
false;
126 bool found_an_entry =
false;
127 bool missed_an_entry =
false;
128 bool uncached_an_entry =
false;
131 std::map<COutPoint, Coin> result;
134 std::vector<CCoinsViewCacheTest*> stack;
135 stack.push_back(
new 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;
161 if (test_havecoin_after) {
173 added_an_unspendable_entry =
true;
176 (coin.IsSpent() ? added_an_entry : updated_an_entry) =
true;
181 removed_an_entry =
true;
191 stack[cacheid]->Uncache(out);
192 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
197 for (
const auto& entry : result) {
198 bool have = stack.back()->HaveCoin(entry.first);
199 const Coin& coin = stack.back()->AccessCoin(entry.first);
203 missed_an_entry =
true;
205 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
206 found_an_entry =
true;
209 for (
const CCoinsViewCacheTest *test : stack) {
218 if (fake_best_block) stack[flushIndex]->SetBestBlock(
InsecureRand256());
234 if (stack.size() > 0) {
237 removed_all_caches =
true;
239 stack.push_back(
new CCoinsViewCacheTest(tip));
240 if (stack.size() == 4) {
241 reached_4_caches =
true;
248 while (stack.size() > 0) {
276 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
282 if (utxoSetIt == utxoSet.end()) {
283 utxoSetIt = utxoSet.begin();
285 auto utxoDataIt =
utxoData.find(*utxoSetIt);
301 bool spent_a_duplicate_coinbase =
false;
303 std::map<COutPoint, Coin> result;
307 std::vector<CCoinsViewCacheTest*> stack;
308 stack.push_back(
new CCoinsViewCacheTest(&base));
311 std::set<COutPoint> coinbase_coins;
312 std::set<COutPoint> disconnected_coins;
313 std::set<COutPoint> duplicate_coins;
314 std::set<COutPoint> utxoset;
320 if (randiter % 20 < 19) {
324 tx.
vout[0].nValue = i;
330 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
337 disconnected_coins.erase(utxod->first);
339 duplicate_coins.insert(utxod->first);
352 if (randiter % 20 == 2 && disconnected_coins.size()) {
355 prevout = tx.
vin[0].prevout;
357 disconnected_coins.erase(utxod->first);
362 if (utxoset.count(utxod->first)) {
364 assert(duplicate_coins.count(utxod->first));
366 disconnected_coins.erase(utxod->first);
372 prevout = utxod->first;
375 tx.
vin[0].prevout = prevout;
379 old_coin = result[prevout];
381 result[prevout].
Clear();
383 utxoset.erase(prevout);
387 if (duplicate_coins.count(prevout)) {
388 spent_a_duplicate_coinbase =
true;
402 utxoset.insert(outpoint);
405 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
406 }
else if (utxoset.size()) {
411 CTxUndo &undo = std::get<1>(utxod->second);
412 Coin &orig_coin = std::get<2>(utxod->second);
416 result[utxod->first].
Clear();
419 result[tx.
vin[0].prevout] = orig_coin;
425 BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
429 Coin coin = undo.vprevout[0];
433 disconnected_coins.insert(utxod->first);
436 utxoset.erase(utxod->first);
438 utxoset.insert(tx.
vin[0].prevout);
443 for (
const auto& entry : result) {
444 bool have = stack.back()->HaveCoin(entry.first);
445 const Coin& coin = stack.back()->AccessCoin(entry.first);
478 if (stack.size() > 0) {
481 stack.push_back(
new CCoinsViewCacheTest(tip));
487 while (stack.size() > 0) {
532 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
533 }
catch (
const std::ios_base::failure&) {
538 uint64_t x = 3000000000ULL;
545 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
546 }
catch (
const std::ios_base::failure&) {
570 if (value !=
SPENT) {
587 auto inserted = map.emplace(
OUTPOINT, std::move(entry));
589 return inserted.first->second.coin.DynamicMemoryUsage();
595 if (it == map.end()) {
599 if (it->second.coin.IsSpent()) {
602 value = it->second.coin.out.nValue;
604 flags = it->second.flags;
634 test.
cache.SelfTest();
685 test.
cache.SelfTest();
740 output.
nValue = modify_value;
742 test.
cache.SelfTest();
744 }
catch (std::logic_error&) {
758 template <
typename... Args>
803 test.
cache.SelfTest();
805 }
catch (std::logic_error&) {
877 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
const Coin & AccessByTxid(const CCoinsViewCache &view, const uint256 &txid)
Utility function to find any unspent output with a given txid.
static const unsigned int NUM_SIMULATION_ITERATIONS
static bool InsecureRandBool()
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)
static const CAmount ABSENT
A Coin in one level of the coins database caching hierarchy.
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 size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
static const CAmount SPENT
size_t DynamicMemoryUsage() const
Calculate the size of the cache (in bytes)
CTxOut out
unspent transaction output
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock)
Do a bulk modification (multiple Coin changes + BestBlock change).
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)
Double ended buffer combining vector and stream-like interfaces.
static uint32_t InsecureRand32()
static const CAmount VALUE2
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...
bool IsUnspendable() const
Returns whether the script is guaranteed to fail at execution, regardless of the initial stack...
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
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 ...
static size_t InsertCoinsMapEntry(CCoinsMap &map, CAmount value, char flags)
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
std::vector< Byte > ParseHex(std::string_view str)
Parse the hex string into bytes (uint8_t or std::byte).
static uint64_t InsecureRandRange(uint64_t range)
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Abstract view on the open txout dataset.
BOOST_AUTO_TEST_SUITE_END()
static void SetCoinsValue(CAmount value, Coin &coin)
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher > CCoinsMap
void WriteCoinsViewEntry(CCoinsView &view, CAmount value, char flags)
static uint64_t InsecureRandBits(int bits)
An output of a transaction.
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
An outpoint - a combination of a transaction hash and an index n into its vout.
std::vector< CTxOut > vout
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight)
static const CAmount FAIL
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 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...
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
#define BOOST_CHECK_EQUAL(v1, v2)
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 void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
A mutable version of CTransaction.
static const auto CLEAN_FLAGS
The basic transaction that is broadcasted on the network and contained in blocks. ...
CCoinsView that adds a memory cache for transactions to another CCoinsView.
bool g_mock_deterministic_tests
Flag to make GetRand in random.h return the same number.
static uint256 InsecureRand256()
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
CCoinsViewCacheTest cache
void GetCoinsMapEntry(const CCoinsMap &map, CAmount &value, char &flags)
Seed with a compile time constant of zeros.
#define BOOST_CHECK(expr)