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;
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));
441 Coin coin = undo.vprevout[0];
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) {
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
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()
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
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 ...
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::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...
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.
Double ended buffer combining vector and stream-like interfaces.
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.
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
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase=true)
Do a bulk modification (multiple Coin changes + BestBlock change).
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
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 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()
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. ...
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.
Seed with a compile time constant of zeros.
CCoinsViewCacheTest cache
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
#define BOOST_CHECK(expr)
static CAmount InsecureRandMoneyAmount()