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<uint256> 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) {
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
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...
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.
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...
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 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()