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()) {
60 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = erase ? mapCoins.erase(it) : std::next(it)) {
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();
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;
130 bool flushed_without_erase =
false;
133 std::map<COutPoint, Coin> result;
136 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
137 stack.push_back(std::make_unique<CCoinsViewCacheTest>(base));
140 std::vector<Txid> txids;
142 for (
unsigned int i = 0; i < txids.size(); i++) {
158 bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(
COutPoint(txid, 0)) :
false;
166 if (test_havecoin_before) {
170 if (test_havecoin_after) {
184 added_an_unspendable_entry =
true;
188 (coin.
IsSpent() ? added_an_entry : updated_an_entry) =
true;
192 stack.back()->AddCoin(
COutPoint(txid, 0), std::move(newcoin), is_overwrite);
195 removed_an_entry =
true;
205 stack[cacheid]->Uncache(
out);
206 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(
out);
211 for (
const auto& entry : result) {
212 bool have = stack.back()->HaveCoin(entry.first);
213 const Coin& coin = stack.back()->AccessCoin(entry.first);
217 missed_an_entry =
true;
219 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
220 found_an_entry =
true;
223 for (
const auto& test : stack) {
232 if (fake_best_block) stack[flushIndex]->SetBestBlock(
InsecureRand256());
234 BOOST_CHECK(should_erase ? stack[flushIndex]->Flush() : stack[flushIndex]->Sync());
235 flushed_without_erase |= !should_erase;
244 BOOST_CHECK(should_erase ? stack.back()->Flush() : stack.back()->Sync());
245 flushed_without_erase |= !should_erase;
251 if (stack.size() > 0) {
252 tip = stack.back().get();
254 removed_all_caches =
true;
256 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
257 if (stack.size() == 4) {
258 reached_4_caches =
true;
283 CCoinsViewDB db_base{{.path =
"test", .cache_bytes = 1 << 23, .memory_only =
true}, {}};
288 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
294 if (utxoSetIt == utxoSet.end()) {
295 utxoSetIt = utxoSet.begin();
297 auto utxoDataIt =
utxoData.find(*utxoSetIt);
313 bool spent_a_duplicate_coinbase =
false;
315 std::map<COutPoint, Coin> result;
319 std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack;
320 stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
323 std::set<COutPoint> coinbase_coins;
324 std::set<COutPoint> disconnected_coins;
325 std::set<COutPoint> duplicate_coins;
326 std::set<COutPoint> utxoset;
332 if (randiter % 20 < 19) {
336 tx.
vout[0].nValue = i;
342 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
349 disconnected_coins.erase(utxod->first);
351 duplicate_coins.insert(utxod->first);
364 if (randiter % 20 == 2 && disconnected_coins.size()) {
367 prevout = tx.
vin[0].prevout;
368 if (!
CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
369 disconnected_coins.erase(utxod->first);
374 if (utxoset.count(utxod->first)) {
376 assert(duplicate_coins.count(utxod->first));
378 disconnected_coins.erase(utxod->first);
384 prevout = utxod->first;
387 tx.
vin[0].prevout = prevout;
391 old_coin = result[prevout];
393 result[prevout].
Clear();
395 utxoset.erase(prevout);
399 if (duplicate_coins.count(prevout)) {
400 spent_a_duplicate_coinbase =
true;
414 utxoset.insert(outpoint);
417 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
418 }
else if (utxoset.size()) {
423 CTxUndo &undo = std::get<1>(utxod->second);
424 Coin &orig_coin = std::get<2>(utxod->second);
428 result[utxod->first].Clear();
431 result[tx.
vin[0].prevout] = orig_coin;
437 BOOST_CHECK(stack.back()->SpendCoin(utxod->first));
445 disconnected_coins.insert(utxod->first);
448 utxoset.erase(utxod->first);
450 utxoset.insert(tx.
vin[0].prevout);
455 for (
const auto& entry : result) {
456 bool have = stack.back()->HaveCoin(entry.first);
457 const Coin& coin = stack.back()->AccessCoin(entry.first);
489 if (stack.size() > 0) {
490 tip = stack.back().get();
492 stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));
537 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
538 }
catch (
const std::ios_base::failure&) {
543 uint64_t x = 3000000000ULL;
550 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
551 }
catch (
const std::ios_base::failure&) {
575 if (value !=
SPENT) {
592 auto inserted = map.emplace(
OUTPOINT, std::move(entry));
594 return inserted.first->second.coin.DynamicMemoryUsage();
599 auto it = map.find(outp);
600 if (it == map.end()) {
604 if (it->second.coin.IsSpent()) {
607 value = it->second.coin.out.nValue;
609 flags = it->second.flags;
617 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
640 test.
cache.SelfTest();
691 test.
cache.SelfTest();
746 output.
nValue = modify_value;
748 test.
cache.SelfTest();
750 }
catch (std::logic_error&) {
764 template <
typename... Args>
809 test.
cache.SelfTest();
811 }
catch (std::logic_error&) {
883 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
908 CCoinsViewCacheTest* view,
910 std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,
911 bool do_erasing_flush)
918 auto flush_all = [&all_caches](
bool erase) {
920 for (
auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {
925 erase ? cache->Flush() : cache->Sync();
938 view->AddCoin(outp,
Coin(coin),
false);
940 cache_usage = view->DynamicMemoryUsage();
941 cache_size = view->map().size();
969 if (do_erasing_flush) {
975 BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage);
977 BOOST_TEST(view->map().size() < cache_size);
985 view->AccessCoin(outp);
994 view->AddCoin(outp,
Coin(coin),
false),
1027 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1028 all_caches[0]->Sync();
1031 BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));
1052 all_caches[0]->AddCoin(outp, std::move(coin),
false);
1063 all_caches[0]->Sync();
1069 BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
1076 CCoinsViewDB base{{.path =
"test", .cache_bytes = 1 << 23, .memory_only =
true}, {}};
1077 std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches;
1078 caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
1079 caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get()));
1081 for (
const auto& view : caches) {
1093 CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
1102 for (
size_t i = 0; i < 1000; ++i) {
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.
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.
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:
CCoinsViewCacheTest cache
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
constexpr bool IsNull() const
void assign(size_type n, const T &val)
static transaction_identifier FromUint256(const uint256 &id)
const Coin & AccessByTxid(const CCoinsViewCache &view, const Txid &txid)
Utility function to find any unspent output with a given txid.
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher, std::equal_to< COutPoint >, PoolAllocator< std::pair< const COutPoint, CCoinsCacheEntry >, sizeof(std::pair< const COutPoint, CCoinsCacheEntry >)+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
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.
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
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.
std::vector< CTxOut > vout
Txid GetHash() const
Compute the hash of this CMutableTransaction.
@ ZEROS
Seed with a compile time constant of zeros.
static CAmount InsecureRandMoneyAmount()
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()
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.