20 #include <boost/test/unit_test.hpp>
40 std::map<COutPoint, Coin> map_;
44 std::map<uint256, SaplingMerkleTree> mapSaplingAnchors_;
45 std::map<uint256, bool> mapSaplingNullifiers_;
54 std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
55 if (it == map_.end()) {
59 if (coin.
IsSpent() && InsecureRandBool() == 0) {
66 bool HaveCoin(
const COutPoint& outpoint)
const
69 return GetCoin(outpoint, coin);
72 uint256 GetBestBlock()
const {
return hashBestBlock_; }
83 std::map<uint256, SaplingMerkleTree>::const_iterator it = mapSaplingAnchors_.find(rt);
84 if (it == mapSaplingAnchors_.end()) {
92 bool GetNullifier(
const uint256 &nf)
const
94 const std::map<uint256, bool>* mapToUse = &mapSaplingNullifiers_;
95 std::map<uint256, bool>::const_iterator it = mapToUse->find(nf);
96 if (it == mapToUse->end()) {
105 uint256 GetBestAnchor()
const {
106 return hashBestSaplingAnchor_;
111 for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); ) {
114 if (it->second.entered) {
115 cacheNullifiers[it->first] =
true;
117 cacheNullifiers.erase(it->first);
120 mapNullifiers.erase(it++);
124 template<
typename Tree,
typename Map,
typename MapEntry>
127 for (
auto it = mapAnchors.begin(); it != mapAnchors.end(); ) {
128 if (it->second.flags & MapEntry::DIRTY) {
130 if (it->second.entered) {
131 if (it->first != Tree::empty_root()) {
132 auto ret = cacheAnchors.insert(std::make_pair(it->first, Tree())).first;
133 ret->second = it->second.tree;
136 cacheAnchors.erase(it->first);
139 mapAnchors.erase(it++);
145 const uint256& hashSaplingAnchor,
149 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
152 map_[it->first] = it->second.coin;
153 if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
155 map_.erase(it->first);
158 mapCoins.erase(it++);
161 BatchWriteAnchors<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingCacheEntry>(mapSaplingAnchors, mapSaplingAnchors_);
165 hashBestBlock_ = hashBlock;
166 if (!hashSaplingAnchor.
IsNull())
167 hashBestSaplingAnchor_ = hashSaplingAnchor;
172 class TxWithNullifiers
185 mutableTx.
sapData->vShieldedSpend.push_back(sd);
196 void SelfTest()
const
199 size_t ret = memusage::DynamicUsage(cacheCoins) +
200 memusage::DynamicUsage(cacheSaplingAnchors) +
201 memusage::DynamicUsage(cacheSaplingNullifiers);
203 for (
const auto& entry : cacheCoins) {
204 ret += memusage::DynamicUsage(entry.second.coin);
224 bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier);
225 BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
235 TxWithNullifiers txWithNullifiers;
254 TxWithNullifiers txWithNullifiers;
275 TxWithNullifiers txWithNullifiers;
299 TxWithNullifiers txWithNullifiers;
391 anchorPopRegressionTestImpl<SaplingMerkleTree>();
478 anchorRegressionTestImpl<SaplingMerkleTree>();
486 TxWithNullifiers txWithNullifiers;
528 uint256 check_rt = tree.root();
536 anchorsFlushImpl<SaplingMerkleTree>();
562 Tree save_tree_for_later;
563 save_tree_for_later = tree;
581 newrt2 = tree.root();
604 assert(obtain_tree.root() == newrt);
611 anchorsTestImpl<SaplingMerkleTree>();
614 static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
628 bool removed_all_caches =
false;
629 bool reached_4_caches =
false;
630 bool added_an_entry =
false;
631 bool added_an_unspendable_entry =
false;
632 bool removed_an_entry =
false;
633 bool updated_an_entry =
false;
634 bool found_an_entry =
false;
635 bool missed_an_entry =
false;
636 bool uncached_an_entry =
false;
639 std::map<COutPoint, Coin> result;
643 std::vector<CCoinsViewCacheTest*> stack;
644 stack.push_back(
new CCoinsViewCacheTest(&base));
647 std::vector<uint256> txids;
648 txids.resize(NUM_SIMULATION_ITERATIONS / 8);
649 for (
unsigned int i = 0; i < txids.size(); i++) {
650 txids[i] = InsecureRand256();
653 for (
unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
656 uint256 txid = txids[InsecureRandRange(txids.size())];
658 const Coin& entry = (InsecureRandRange(500) == 0) ?
662 if (InsecureRandRange(5) == 0 || coin.
IsSpent()) {
666 if (InsecureRandRange(16) == 0 && coin.
IsSpent()) {
669 added_an_unspendable_entry =
true;
672 (coin.
IsSpent() ? added_an_entry : updated_an_entry) =
true;
675 stack.back()->AddCoin(
COutPoint(txid, 0), std::move(newcoin), !coin.
IsSpent() || InsecureRandBool());
677 removed_an_entry =
true;
679 stack.back()->SpendCoin(
COutPoint(txid, 0));
684 if (InsecureRandRange(10) == 0) {
685 COutPoint out(txids[InsecureRandRange(txids.size())], 0);
686 int cacheid = InsecureRandRange(stack.size());
687 stack[cacheid]->Uncache(out);
688 uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
692 if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
693 for (
const auto& entry : result) {
694 bool have = stack.back()->HaveCoin(entry.first);
695 const Coin& coin = stack.back()->AccessCoin(entry.first);
699 missed_an_entry =
true;
701 BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
702 found_an_entry =
true;
705 for (
const CCoinsViewCacheTest *test : stack) {
710 if (InsecureRandRange(100) == 0) {
712 if (stack.size() > 1 && InsecureRandBool() == 0) {
713 unsigned int flushIndex = InsecureRandRange(stack.size() - 1) == 0;
714 stack[flushIndex]->Flush();
718 if (InsecureRandRange(100) == 0) {
720 if (stack.size() > 0 && InsecureRandBool() == 0) {
722 stack.back()->Flush();
726 if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
729 if (stack.size() > 0) {
732 removed_all_caches =
true;
734 stack.push_back(
new CCoinsViewCacheTest(tip));
735 if (stack.size() == 4) {
736 reached_4_caches =
true;
743 while (stack.size() > 0) {
761 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>>
UtxoData;
765 assert(utxoSet.size());
767 if (utxoSetIt == utxoSet.end()) {
768 utxoSetIt = utxoSet.begin();
770 auto utxoDataIt =
utxoData.find(*utxoSetIt);
771 assert(utxoDataIt !=
utxoData.end());
781 std::map<COutPoint, Coin> result;
785 std::vector<CCoinsViewCacheTest*> stack;
786 stack.push_back(
new CCoinsViewCacheTest(&base));
789 std::set<COutPoint> coinbase_coins;
790 std::set<COutPoint> disconnected_coins;
791 std::set<COutPoint> utxoset;
793 for (
unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
794 uint32_t randiter = InsecureRand32();
797 if (randiter % 20 < 19) {
801 tx.
vout[0].nValue = i;
802 unsigned int height = InsecureRand32();
806 if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
817 if (randiter % 20 == 2 && disconnected_coins.size()) {
819 tx = std::get<0>(utxod->second);
820 prevout = tx.
vin[0].prevout;
823 disconnected_coins.erase(utxod->first);
830 prevout = utxod->first;
833 tx.
vin[0].prevout = prevout;
837 old_coin = result[prevout];
840 result[prevout].
Clear();
842 utxoset.erase(prevout);
846 assert(tx.
vout.size() == 1);
855 utxoset.insert(outpoint);
858 utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
860 }
else if (utxoset.size()) {
865 CTxUndo& undo = std::get<1>(utxod->second);
866 Coin& orig_coin = std::get<2>(utxod->second);
870 result[utxod->first].Clear();
873 result[tx.
vin[0].prevout] = orig_coin;
879 stack.back()->SpendCoin(utxod->first);
888 disconnected_coins.insert(utxod->first);
891 utxoset.erase(utxod->first);
893 utxoset.insert(tx.
vin[0].prevout);
897 if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
898 for (
const auto& entry : result) {
899 bool have = stack.back()->HaveCoin(entry.first);
900 const Coin& coin = stack.back()->AccessCoin(entry.first);
907 if (utxoset.size() > 1 && InsecureRandRange(20) == 0) {
908 stack[InsecureRandRange(stack.size())]->Uncache(
FindRandomFrom(utxoset)->first);
910 if (disconnected_coins.size() > 1 && InsecureRandRange(20) == 0) {
911 stack[InsecureRandRange(stack.size())]->Uncache(
FindRandomFrom(disconnected_coins)->first);
914 if (InsecureRandRange(100) == 0) {
916 if (stack.size() > 0 && InsecureRandBool() == 0) {
917 stack.back()->Flush();
921 if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
923 if (stack.size() > 0) {
926 stack.push_back(
new CCoinsViewCacheTest(tip));
932 while (stack.size() > 0) {
980 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
981 }
catch (
const std::ios_base::failure& e) {
986 uint64_t x = 3000000000ULL;
993 BOOST_CHECK_MESSAGE(
false,
"We should have thrown");
994 }
catch (
const std::ios_base::failure& e) {
999 const static CAmount PRUNED = -1;
1000 const static CAmount ABSENT = -2;
1001 const static CAmount FAIL = -3;
1002 const static CAmount VALUE1 = 100;
1003 const static CAmount VALUE2 = 200;
1004 const static CAmount VALUE3 = 300;
1007 const static char NO_ENTRY = -1;
1009 const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
1010 const static auto CLEAN_FLAGS = {char(0), FRESH};
1011 const static auto ABSENT_FLAGS = {NO_ENTRY};
1015 assert(value != ABSENT);
1018 if (value != PRUNED) {
1027 if (value == ABSENT) {
1028 assert(
flags == NO_ENTRY);
1031 assert(
flags != NO_ENTRY);
1035 auto inserted = map.emplace(OUTPOINT, std::move(entry));
1036 assert(inserted.second);
1037 return inserted.first->second.coin.DynamicMemoryUsage();
1042 auto it = map.find(OUTPOINT);
1043 if (it == map.end()) {
1047 if (it->second.coin.IsSpent()) {
1050 value = it->second.coin.out.nValue;
1052 flags = it->second.flags;
1053 assert(
flags != NO_ENTRY);
1063 view.
BatchWrite(map, {}, {}, mapSaplingAnchors, mapSaplingNullifiers);
1083 test.
cache.AccessCoin(OUTPOINT);
1084 test.
cache.SelfTest();
1134 test.
cache.SpendCoin(OUTPOINT);
1135 test.
cache.SelfTest();
1190 output.
nValue = modify_value;
1191 test.
cache.AddCoin(OUTPOINT,
Coin(std::move(output), 1,
false,
false),
false);
1192 test.
cache.SelfTest();
1194 }
catch (std::logic_error& e) {
1195 result_value = FAIL;
1196 result_flags = NO_ENTRY;
1208 template <
typename... Args>
1211 for (
CAmount base_value : {ABSENT, PRUNED, VALUE1})
1227 CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH );
1228 CheckAddCoin(PRUNED, VALUE3, VALUE3, 0 , DIRTY|FRESH );
1229 CheckAddCoin(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH );
1231 CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH );
1235 CheckAddCoin(VALUE2, VALUE3, FAIL, DIRTY|FRESH, NO_ENTRY );
1246 test.
cache.SelfTest();
1248 }
catch (std::logic_error& e) {
1249 result_value = FAIL;
1250 result_flags = NO_ENTRY;
1266 CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY );
1268 CheckWriteCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY );
1270 CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH);
1274 CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
1278 CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
1281 CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
1282 CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
1286 CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
1289 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
1290 CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
1294 CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
1298 CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
1300 CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
1301 CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
1302 CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
1306 CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
1308 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
1309 CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
1310 CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
1316 for (
CAmount parent_value : {ABSENT, PRUNED, VALUE1})
1317 for (
CAmount child_value : {ABSENT, PRUNED, VALUE2})
1318 for (
char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
1319 for (
char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
1320 CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
int64_t CAmount
Amount in PIV (Can be negative)
bool operator==(const CBigNum &a, const CBigNum &b)
CCoinsView that adds a memory cache for transactions to another CCoinsView.
void SetNullifiers(const CTransaction &tx, bool spent)
void PushAnchor(const Tree &tree)
void PopAnchor(const uint256 &rt)
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const override
Retrieve the tree (Sapling) at a particular anchored root in the chain.
uint256 GetBestAnchor() const override
Get the current "tip" or the latest anchored tree root in the chain.
bool Flush()
Push the modifications applied to this cache to its base.
Abstract view on the open txout dataset.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSaplingAnchor, CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSaplingNullifiers)
Do a bulk modification (multiple Coin changes + BestBlock change).
A reference to a CKey: the Hash160 of its serialized public key.
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.
An output of a transaction.
Undo information for a CTransaction.
std::vector< Coin > vprevout
bool fCoinStake
whether the containing transaction was a coinstake
CTxOut out
unspent transaction output
bool fCoinBase
whether the containing transaction was a coinbase
uint32_t nHeight
at which height the containing transaction was included in the active block chain
CCoinsViewCacheTest cache
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
A shielded input to a transaction.
uint256 nullifier
The nullifier of the input note.
static libzcash::PedersenHash empty_root()
void assign(size_type n, const T &val)
void BatchWriteAnchors(Map &mapAnchors, Map &cacheAnchors, size_t &cachedCoinsUsage)
const Coin & AccessByTxid(const CCoinsViewCache &view, const uint256 &txid)
Utility function to find any unspent output with a given txid.
void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers)
std::unordered_map< uint256, CAnchorsSaplingCacheEntry, SaltedIdHasher > CAnchorsSaplingMap
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher > CCoinsMap
std::unordered_map< uint256, CNullifiersCacheEntry, SaltedIdHasher > CNullifiersMap
void checkNullifierCache(const CCoinsViewCache &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache)
size_t InsertCoinsMapEntry(CCoinsMap &map, CAmount value, char flags)
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, bool fSkipInvalid=false)
void anchorPopRegressionTestImpl()
void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
void WriteCoinsViewEntry(CCoinsView &view, CAmount value, char flags)
void CheckAddCoin(Args &&... args)
void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
int ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoint &out)
Restore the UTXO in a Coin at a given COutPoint.
std::map< COutPoint, std::tuple< CTransaction, CTxUndo, Coin > > UtxoData
void SetCoinsValue(CAmount value, Coin &coin)
BOOST_AUTO_TEST_CASE(nullifier_regression_test)
void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags)
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
void GetCoinsMapEntry(const CCoinsMap &map, CAmount &value, char &flags)
bool GetAnchorAt(const CCoinsViewCache &cache, const uint256 &rt, Tree &tree)
UtxoData::iterator FindRandomFrom(const std::set< COutPoint > &utxoSet)
void anchorRegressionTestImpl()
BOOST_AUTO_TEST_SUITE_END()
void test_tree(UniValue commitment_tests, UniValue root_tests, UniValue ser_tests, UniValue witness_ser_tests, UniValue path_tests)
#define BOOST_FIXTURE_TEST_SUITE(a, b)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
uint256 GetRandHash() noexcept
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a PIVX scriptPubKey for the given CTxDestination.
A mutable version of CTransaction.
Optional< SaplingTxData > sapData
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
std::vector< CTxOut > vout
std::shared_ptr< const CTransaction > CTransactionRef
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)