PIVX Core  5.6.99
P2P Digital Currency
coins_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014 The Bitcoin Core developers
2 // Copyright (c) 2019-2021 The PIVX Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "test/test_pivx.h"
7 
8 #include "coins.h"
9 #include "script/standard.h"
10 #include "uint256.h"
11 #include "undo.h"
12 #include "utilstrencodings.h"
13 #include "random.h"
14 
16 
17 #include <vector>
18 #include <map>
19 
20 #include <boost/test/unit_test.hpp>
21 
22 int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out);
23 void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight, bool fSkipInvalid = false);
24 
25 namespace
26 {
28 bool operator==(const Coin &a, const Coin &b) {
29  // Empty Coin objects are always equal.
30  if (a.IsSpent() && b.IsSpent()) return true;
31  return a.fCoinBase == b.fCoinBase &&
32  a.fCoinStake == b.fCoinStake &&
33  a.nHeight == b.nHeight &&
34  a.out == b.out;
35 }
36 
37 class CCoinsViewTest : public CCoinsView
38 {
39  uint256 hashBestBlock_;
40  std::map<COutPoint, Coin> map_;
41 
42  // Sapling
43  uint256 hashBestSaplingAnchor_;
44  std::map<uint256, SaplingMerkleTree> mapSaplingAnchors_;
45  std::map<uint256, bool> mapSaplingNullifiers_;
46 
47 public:
48  CCoinsViewTest() {
49  hashBestSaplingAnchor_ = SaplingMerkleTree::empty_root();
50  }
51 
52  bool GetCoin(const COutPoint& outpoint, Coin& coin) const
53  {
54  std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
55  if (it == map_.end()) {
56  return false;
57  }
58  coin = it->second;
59  if (coin.IsSpent() && InsecureRandBool() == 0) {
60  // Randomly return false in case of an empty entry.
61  return false;
62  }
63  return true;
64  }
65 
66  bool HaveCoin(const COutPoint& outpoint) const
67  {
68  Coin coin;
69  return GetCoin(outpoint, coin);
70  }
71 
72  uint256 GetBestBlock() const { return hashBestBlock_; }
73 
74  // Sapling
75 
76  bool GetSaplingAnchorAt(const uint256& rt, SaplingMerkleTree &tree) const {
77  if (rt == SaplingMerkleTree::empty_root()) {
78  SaplingMerkleTree new_tree;
79  tree = new_tree;
80  return true;
81  }
82 
83  std::map<uint256, SaplingMerkleTree>::const_iterator it = mapSaplingAnchors_.find(rt);
84  if (it == mapSaplingAnchors_.end()) {
85  return false;
86  } else {
87  tree = it->second;
88  return true;
89  }
90  }
91 
92  bool GetNullifier(const uint256 &nf) const
93  {
94  const std::map<uint256, bool>* mapToUse = &mapSaplingNullifiers_;
95  std::map<uint256, bool>::const_iterator it = mapToUse->find(nf);
96  if (it == mapToUse->end()) {
97  return false;
98  } else {
99  // The map shouldn't contain any false entries.
100  assert(it->second);
101  return true;
102  }
103  }
104 
105  uint256 GetBestAnchor() const {
106  return hashBestSaplingAnchor_;
107  }
108 
109  void BatchWriteNullifiers(CNullifiersMap& mapNullifiers, std::map<uint256, bool>& cacheNullifiers)
110  {
111  for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); ) {
112  if (it->second.flags & CNullifiersCacheEntry::DIRTY) {
113  // Same optimization used in CCoinsViewDB is to only write dirty entries.
114  if (it->second.entered) {
115  cacheNullifiers[it->first] = true;
116  } else {
117  cacheNullifiers.erase(it->first);
118  }
119  }
120  mapNullifiers.erase(it++);
121  }
122  }
123 
124  template<typename Tree, typename Map, typename MapEntry>
125  void BatchWriteAnchors(Map& mapAnchors, std::map<uint256, Tree>& cacheAnchors)
126  {
127  for (auto it = mapAnchors.begin(); it != mapAnchors.end(); ) {
128  if (it->second.flags & MapEntry::DIRTY) {
129  // Same optimization used in CCoinsViewDB is to only write dirty entries.
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;
134  }
135  } else {
136  cacheAnchors.erase(it->first);
137  }
138  }
139  mapAnchors.erase(it++);
140  }
141  }
142 
143  bool BatchWrite(CCoinsMap& mapCoins,
144  const uint256& hashBlock,
145  const uint256& hashSaplingAnchor,
146  CAnchorsSaplingMap& mapSaplingAnchors,
147  CNullifiersMap& mapSaplingNullifiers)
148  {
149  for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
150  if (it->second.flags & CCoinsCacheEntry::DIRTY) {
151  // Same optimization used in CCoinsViewDB is to only write dirty entries.
152  map_[it->first] = it->second.coin;
153  if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
154  // Randomly delete empty entries on write.
155  map_.erase(it->first);
156  }
157  }
158  mapCoins.erase(it++);
159  }
160 
161  BatchWriteAnchors<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingCacheEntry>(mapSaplingAnchors, mapSaplingAnchors_);
162  BatchWriteNullifiers(mapSaplingNullifiers, mapSaplingNullifiers_);
163 
164  if (!hashBlock.IsNull())
165  hashBestBlock_ = hashBlock;
166  if (!hashSaplingAnchor.IsNull())
167  hashBestSaplingAnchor_ = hashSaplingAnchor;
168  return true;
169  }
170 };
171 
172 class TxWithNullifiers
173 {
174 public:
175  CTransactionRef tx;
176  uint256 saplingNullifier;
177 
178  TxWithNullifiers()
179  {
180  CMutableTransaction mutableTx;
181 
182  saplingNullifier = GetRandHash();
183  SpendDescription sd;
184  sd.nullifier = saplingNullifier;
185  mutableTx.sapData->vShieldedSpend.push_back(sd);
186  tx = MakeTransactionRef(CTransaction(mutableTx));
187  }
188 };
189 
190 
191 class CCoinsViewCacheTest : public CCoinsViewCache
192 {
193 public:
194  explicit CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}
195 
196  void SelfTest() const
197  {
198  // Manually recompute the dynamic usage of the whole data, and compare it.
199  size_t ret = memusage::DynamicUsage(cacheCoins) +
200  memusage::DynamicUsage(cacheSaplingAnchors) +
201  memusage::DynamicUsage(cacheSaplingNullifiers);
202  size_t count = 0;
203  for (const auto& entry : cacheCoins) {
204  ret += memusage::DynamicUsage(entry.second.coin);
205  ++count;
206  }
208  BOOST_CHECK_EQUAL(memusage::DynamicUsage(*this), ret);
209  }
210 
211  CCoinsMap& map() { return cacheCoins; }
212  size_t& usage() { return cachedCoinsUsage; }
213 };
214 
215 }
216 
217 template<typename Tree> bool GetAnchorAt(const CCoinsViewCache &cache, const uint256 &rt, Tree &tree);
218 template<> bool GetAnchorAt(const CCoinsViewCache &cache, const uint256 &rt, SaplingMerkleTree &tree) { return cache.GetSaplingAnchorAt(rt, tree); }
219 
221 
222 void checkNullifierCache(const CCoinsViewCache &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) {
223  // Check if the nullifiers either are or are not in the cache
224  bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier);
225  BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
226 }
227 
228 BOOST_AUTO_TEST_CASE(nullifier_regression_test)
229 {
230  // Correct behavior:
231  {
232  CCoinsViewTest base;
233  CCoinsViewCache cache1(&base);
234 
235  TxWithNullifiers txWithNullifiers;
236 
237  // Insert a nullifier into the base.
238  cache1.SetNullifiers(*txWithNullifiers.tx, true);
239  checkNullifierCache(cache1, txWithNullifiers, true);
240  cache1.Flush(); // Flush to base.
241 
242  // Remove the nullifier from cache
243  cache1.SetNullifiers(*txWithNullifiers.tx, false);
244 
245  // The nullifier now should be `false`.
246  checkNullifierCache(cache1, txWithNullifiers, false);
247  }
248 
249  // Also correct behavior:
250  {
251  CCoinsViewTest base;
252  CCoinsViewCache cache1(&base);
253 
254  TxWithNullifiers txWithNullifiers;
255 
256  // Insert a nullifier into the base.
257  cache1.SetNullifiers(*txWithNullifiers.tx, true);
258  checkNullifierCache(cache1, txWithNullifiers, true);
259  cache1.Flush(); // Flush to base.
260 
261  // Remove the nullifier from cache
262  cache1.SetNullifiers(*txWithNullifiers.tx, false);
263  cache1.Flush(); // Flush to base.
264 
265  // The nullifier now should be `false`.
266  checkNullifierCache(cache1, txWithNullifiers, false);
267  }
268 
269  // Works because we bring it from the parent cache:
270  {
271  CCoinsViewTest base;
272  CCoinsViewCache cache1(&base);
273 
274  // Insert a nullifier into the base.
275  TxWithNullifiers txWithNullifiers;
276  cache1.SetNullifiers(*txWithNullifiers.tx, true);
277  checkNullifierCache(cache1, txWithNullifiers, true);
278  cache1.Flush(); // Empties cache.
279 
280  // Create cache on top.
281  {
282  // Remove the nullifier.
283  CCoinsViewCache cache2(&cache1);
284  checkNullifierCache(cache2, txWithNullifiers, true);
285  cache1.SetNullifiers(*txWithNullifiers.tx, false);
286  cache2.Flush(); // Empties cache, flushes to cache1.
287  }
288 
289  // The nullifier now should be `false`.
290  checkNullifierCache(cache1, txWithNullifiers, false);
291  }
292 
293  // Was broken:
294  {
295  CCoinsViewTest base;
296  CCoinsViewCache cache1(&base);
297 
298  // Insert a nullifier into the base.
299  TxWithNullifiers txWithNullifiers;
300  cache1.SetNullifiers(*txWithNullifiers.tx, true);
301  cache1.Flush(); // Empties cache.
302 
303  // Create cache on top.
304  {
305  // Remove the nullifier.
306  CCoinsViewCache cache2(&cache1);
307  cache2.SetNullifiers(*txWithNullifiers.tx, false);
308  cache2.Flush(); // Empties cache, flushes to cache1.
309  }
310 
311  // The nullifier now should be `false`.
312  checkNullifierCache(cache1, txWithNullifiers, false);
313  }
314 }
315 
316 template<typename Tree> void anchorPopRegressionTestImpl()
317 {
318  // Correct behavior:
319  {
320  CCoinsViewTest base;
321  CCoinsViewCache cache1(&base);
322 
323  // Create dummy anchor/commitment
324  Tree tree;
325  tree.append(GetRandHash());
326 
327  // Add the anchor
328  cache1.PushAnchor(tree);
329  cache1.Flush();
330 
331  // Remove the anchor
332  cache1.PopAnchor(Tree::empty_root());
333  cache1.Flush();
334 
335  // Add the anchor back
336  cache1.PushAnchor(tree);
337  cache1.Flush();
338 
339  // The base contains the anchor, of course!
340  {
341  Tree checkTree;
342  BOOST_CHECK(GetAnchorAt(cache1, tree.root(), checkTree));
343  BOOST_CHECK(checkTree.root() == tree.root());
344  }
345  }
346 
347  // Previously incorrect behavior
348  {
349  CCoinsViewTest base;
350  CCoinsViewCache cache1(&base);
351 
352  // Create dummy anchor/commitment
353  Tree tree;
354  tree.append(GetRandHash());
355 
356  // Add the anchor and flush to disk
357  cache1.PushAnchor(tree);
358  cache1.Flush();
359 
360  // Remove the anchor, but don't flush yet!
361  cache1.PopAnchor(Tree::empty_root());
362 
363  {
364  CCoinsViewCache cache2(&cache1); // Build cache on top
365  cache2.PushAnchor(tree); // Put the same anchor back!
366  cache2.Flush(); // Flush to cache1
367  }
368 
369  // cache2's flush kinda worked, i.e. cache1 thinks the
370  // tree is there, but it didn't bring down the correct
371  // treestate...
372  {
373  Tree checktree;
374  BOOST_CHECK(GetAnchorAt(cache1, tree.root(), checktree));
375  BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks.
376  }
377 
378  // Flushing cache won't help either, just makes the inconsistency
379  // permanent.
380  cache1.Flush();
381  {
382  Tree checktree;
383  BOOST_CHECK(GetAnchorAt(cache1, tree.root(), checktree));
384  BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks.
385  }
386  }
387 }
388 
389 BOOST_AUTO_TEST_CASE(anchor_pop_regression_test)
390 {
391  anchorPopRegressionTestImpl<SaplingMerkleTree>();
392 }
393 
394 template<typename Tree> void anchorRegressionTestImpl()
395 {
396  // Correct behavior:
397  {
398  CCoinsViewTest base;
399  CCoinsViewCache cache1(&base);
400 
401  // Insert anchor into base.
402  Tree tree;
403  tree.append(GetRandHash());
404 
405  cache1.PushAnchor(tree);
406  cache1.Flush();
407 
408  cache1.PopAnchor(Tree::empty_root());
409  BOOST_CHECK(cache1.GetBestAnchor() == Tree::empty_root());
410  BOOST_CHECK(!GetAnchorAt(cache1, tree.root(), tree));
411  }
412 
413  // Also correct behavior:
414  {
415  CCoinsViewTest base;
416  CCoinsViewCache cache1(&base);
417 
418  // Insert anchor into base.
419  Tree tree;
420  tree.append(GetRandHash());
421  cache1.PushAnchor(tree);
422  cache1.Flush();
423 
424  cache1.PopAnchor(Tree::empty_root());
425  cache1.Flush();
426  BOOST_CHECK(cache1.GetBestAnchor() == Tree::empty_root());
427  BOOST_CHECK(!GetAnchorAt(cache1, tree.root(), tree));
428  }
429 
430  // Works because we bring the anchor in from parent cache.
431  {
432  CCoinsViewTest base;
433  CCoinsViewCache cache1(&base);
434 
435  // Insert anchor into base.
436  Tree tree;
437  tree.append(GetRandHash());
438  cache1.PushAnchor(tree);
439  cache1.Flush();
440 
441  {
442  // Pop anchor.
443  CCoinsViewCache cache2(&cache1);
444  BOOST_CHECK(GetAnchorAt(cache2, tree.root(), tree));
445  cache2.PopAnchor(Tree::empty_root());
446  cache2.Flush();
447  }
448 
449  BOOST_CHECK(cache1.GetBestAnchor() == Tree::empty_root());
450  BOOST_CHECK(!GetAnchorAt(cache1, tree.root(), tree));
451  }
452 
453  // Was broken:
454  {
455  CCoinsViewTest base;
456  CCoinsViewCache cache1(&base);
457 
458  // Insert anchor into base.
459  Tree tree;
460  tree.append(GetRandHash());
461  cache1.PushAnchor(tree);
462  cache1.Flush();
463 
464  {
465  // Pop anchor.
466  CCoinsViewCache cache2(&cache1);
467  cache2.PopAnchor(Tree::empty_root());
468  cache2.Flush();
469  }
470 
471  BOOST_CHECK(cache1.GetBestAnchor() == Tree::empty_root());
472  BOOST_CHECK(!GetAnchorAt(cache1, tree.root(), tree));
473  }
474 }
475 
476 BOOST_AUTO_TEST_CASE(anchor_regression_test)
477 {
478  anchorRegressionTestImpl<SaplingMerkleTree>();
479 }
480 
481 BOOST_AUTO_TEST_CASE(nullifiers_test)
482 {
483  CCoinsViewTest base;
484  CCoinsViewCache cache(&base);
485 
486  TxWithNullifiers txWithNullifiers;
487  checkNullifierCache(cache, txWithNullifiers, false);
488  cache.SetNullifiers(*txWithNullifiers.tx, true);
489  checkNullifierCache(cache, txWithNullifiers, true);
490  cache.Flush();
491 
492  CCoinsViewCache cache2(&base);
493 
494  checkNullifierCache(cache2, txWithNullifiers, true);
495  cache2.SetNullifiers(*txWithNullifiers.tx, false);
496  checkNullifierCache(cache2, txWithNullifiers, false);
497  cache2.Flush();
498 
499  CCoinsViewCache cache3(&base);
500 
501  checkNullifierCache(cache3, txWithNullifiers, false);
502 }
503 
504 template<typename Tree> void anchorsFlushImpl()
505 {
506  CCoinsViewTest base;
507  uint256 newrt;
508  {
509  CCoinsViewCache cache(&base);
510  Tree tree;
511  BOOST_CHECK(GetAnchorAt(cache, cache.GetBestAnchor(), tree));
512  tree.append(GetRandHash());
513 
514  newrt = tree.root();
515 
516  cache.PushAnchor(tree);
517  cache.Flush();
518  }
519 
520  {
521  CCoinsViewCache cache(&base);
522  Tree tree;
523  BOOST_CHECK(GetAnchorAt(cache, cache.GetBestAnchor(), tree));
524 
525  // Get the cached entry.
526  BOOST_CHECK(GetAnchorAt(cache, cache.GetBestAnchor(), tree));
527 
528  uint256 check_rt = tree.root();
529 
530  BOOST_CHECK(check_rt == newrt);
531  }
532 }
533 
534 BOOST_AUTO_TEST_CASE(anchors_flush_test)
535 {
536  anchorsFlushImpl<SaplingMerkleTree>();
537 }
538 
539 template<typename Tree> void anchorsTestImpl()
540 {
541  // TODO: These tests should be more methodical.
542  // Or, integrate with Bitcoin's tests later.
543 
544  CCoinsViewTest base;
545  CCoinsViewCache cache(&base);
546 
547  BOOST_CHECK(cache.GetBestAnchor() == Tree::empty_root());
548 
549  {
550  Tree tree;
551 
552  BOOST_CHECK(GetAnchorAt(cache, cache.GetBestAnchor(), tree));
553  BOOST_CHECK(cache.GetBestAnchor() == tree.root());
554  tree.append(GetRandHash());
555  tree.append(GetRandHash());
556  tree.append(GetRandHash());
557  tree.append(GetRandHash());
558  tree.append(GetRandHash());
559  tree.append(GetRandHash());
560  tree.append(GetRandHash());
561 
562  Tree save_tree_for_later;
563  save_tree_for_later = tree;
564 
565  uint256 newrt = tree.root();
566  uint256 newrt2;
567 
568  cache.PushAnchor(tree);
569  BOOST_CHECK(cache.GetBestAnchor() == newrt);
570 
571  {
572  Tree confirm_same;
573  BOOST_CHECK(GetAnchorAt(cache, cache.GetBestAnchor(), confirm_same));
574 
575  BOOST_CHECK(confirm_same.root() == newrt);
576  }
577 
578  tree.append(GetRandHash());
579  tree.append(GetRandHash());
580 
581  newrt2 = tree.root();
582 
583  cache.PushAnchor(tree);
584  BOOST_CHECK(cache.GetBestAnchor() == newrt2);
585 
586  Tree test_tree;
588 
589  BOOST_CHECK(tree.root() == test_tree.root());
590 
591  {
592  Tree test_tree2;
593  GetAnchorAt(cache, newrt, test_tree2);
594 
595  BOOST_CHECK(test_tree2.root() == newrt);
596  }
597 
598  {
599  cache.PopAnchor(newrt);
600  Tree obtain_tree;
601  assert(!GetAnchorAt(cache, newrt2, obtain_tree)); // should have been popped off
602  assert(GetAnchorAt(cache, newrt, obtain_tree));
603 
604  assert(obtain_tree.root() == newrt);
605  }
606  }
607 }
608 
609 BOOST_AUTO_TEST_CASE(anchors_test)
610 {
611  anchorsTestImpl<SaplingMerkleTree>();
612 }
613 
614 static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
615 
616 // This is a large randomized insert/remove simulation test on a variable-size
617 // stack of caches on top of CCoinsViewTest.
618 //
619 // It will randomly create/update/delete Coin entries to a tip of caches, with
620 // txids picked from a limited list of random 256-bit hashes. Occasionally, a
621 // new tip is added to the stack of caches, or the tip is flushed and removed.
622 //
623 // During the process, booleans are kept to make sure that the randomized
624 // operation hits all branches.
625 BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
626 {
627  // Various coverage trackers.
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;
637 
638  // A simple map to track what we expect the cache stack to represent.
639  std::map<COutPoint, Coin> result;
640 
641  // The cache stack.
642  CCoinsViewTest base; // A CCoinsViewTest at the bottom.
643  std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
644  stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
645 
646  // Use a limited set of random transaction ids, so we do test overwriting entries.
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();
651  }
652 
653  for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
654  // Do a random modification.
655  {
656  uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration.
657  Coin& coin = result[COutPoint(txid, 0)];
658  const Coin& entry = (InsecureRandRange(500) == 0) ?
659  AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0)
660  );
661  BOOST_CHECK(coin == entry);
662  if (InsecureRandRange(5) == 0 || coin.IsSpent()) {
663  Coin newcoin;
664  newcoin.out.nValue = InsecureRand32();
665  newcoin.nHeight = 1;
666  if (InsecureRandRange(16) == 0 && coin.IsSpent()) {
667  newcoin.out.scriptPubKey.assign(1 + (InsecureRand32() & 0x3F), OP_RETURN);
669  added_an_unspendable_entry = true;
670  } else {
671  newcoin.out.scriptPubKey.assign(InsecureRand32() & 0x3F, 0); // Random sizes so we can test memory usage accounting
672  (coin.IsSpent() ? added_an_entry : updated_an_entry) = true;
673  coin = newcoin;
674  }
675  stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), !coin.IsSpent() || InsecureRandBool());
676  } else {
677  removed_an_entry = true;
678  coin.Clear();
679  stack.back()->SpendCoin(COutPoint(txid, 0));
680  }
681  }
682 
683  // One every 10 iterations, remove a random entry from the cache
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);
689  }
690 
691  // Once every 1000 iterations and at the end, verify the full cache.
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);
696  BOOST_CHECK(have == !coin.IsSpent());
697  BOOST_CHECK(coin == entry.second);
698  if (coin.IsSpent()) {
699  missed_an_entry = true;
700  } else {
701  BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
702  found_an_entry = true;
703  }
704  }
705  for (const CCoinsViewCacheTest *test : stack) {
706  test->SelfTest();
707  }
708  }
709 
710  if (InsecureRandRange(100) == 0) {
711  // Every 100 iterations, flush an intermediate cache
712  if (stack.size() > 1 && InsecureRandBool() == 0) {
713  unsigned int flushIndex = InsecureRandRange(stack.size() - 1) == 0;
714  stack[flushIndex]->Flush();
715  }
716  }
717 
718  if (InsecureRandRange(100) == 0) {
719  // Every 100 iterations, change the cache stack.
720  if (stack.size() > 0 && InsecureRandBool() == 0) {
721  //Remove the top cache
722  stack.back()->Flush();
723  delete stack.back();
724  stack.pop_back();
725  }
726  if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
727  //Add a new cache
728  CCoinsView* tip = &base;
729  if (stack.size() > 0) {
730  tip = stack.back();
731  } else {
732  removed_all_caches = true;
733  }
734  stack.push_back(new CCoinsViewCacheTest(tip));
735  if (stack.size() == 4) {
736  reached_4_caches = true;
737  }
738  }
739  }
740  }
741 
742  // Clean up the stack.
743  while (stack.size() > 0) {
744  delete stack.back();
745  stack.pop_back();
746  }
747 
748  // Verify coverage.
749  BOOST_CHECK(removed_all_caches);
750  BOOST_CHECK(reached_4_caches);
751  BOOST_CHECK(added_an_entry);
752  BOOST_CHECK(added_an_unspendable_entry);
753  BOOST_CHECK(removed_an_entry);
754  BOOST_CHECK(updated_an_entry);
755  BOOST_CHECK(found_an_entry);
756  BOOST_CHECK(missed_an_entry);
757  BOOST_CHECK(uncached_an_entry);
758 }
759 
760 // Store of all necessary tx and undo data for next test
761 typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData;
763 
764 UtxoData::iterator FindRandomFrom(const std::set<COutPoint>& utxoSet) {
765  assert(utxoSet.size());
766  auto utxoSetIt = utxoSet.lower_bound(COutPoint(GetRandHash(), 0));
767  if (utxoSetIt == utxoSet.end()) {
768  utxoSetIt = utxoSet.begin();
769  }
770  auto utxoDataIt = utxoData.find(*utxoSetIt);
771  assert(utxoDataIt != utxoData.end());
772  return utxoDataIt;
773 }
774 
775 // This test is similar to the previous test
776 // except the emphasis is on testing the functionality of UpdateCoins
777 // random txs are created and UpdateCoins is used to update the cache stack
778 BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
779 {
780  // A simple map to track what we expect the cache stack to represent.
781  std::map<COutPoint, Coin> result;
782 
783  // The cache stack.
784  CCoinsViewTest base; // A CCoinsViewTest at the bottom.
785  std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
786  stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
787 
788  // Track the txids we've used in various sets
789  std::set<COutPoint> coinbase_coins;
790  std::set<COutPoint> disconnected_coins;
791  std::set<COutPoint> utxoset;
792 
793  for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
794  uint32_t randiter = InsecureRand32();
795 
796  // 19/20 txs add a new transaction
797  if (randiter % 20 < 19) {
799  tx.vin.resize(1);
800  tx.vout.resize(1);
801  tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
802  unsigned int height = InsecureRand32();
803  Coin old_coin;
804 
805  // 2/20 times create a new coinbase
806  if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
807  // PIVX: don't test for duplicate coinbases as those are not possible due to
808  // BIP34 enforced since the beginning.
809  assert(CTransaction(tx).IsCoinBase());
810  coinbase_coins.insert(COutPoint(tx.GetHash(), 0));
811  }
812 
813  // 17/20 times reconnect previous or add a regular tx
814  else {
815  COutPoint prevout;
816  // 1/20 times reconnect a previously disconnected tx
817  if (randiter % 20 == 2 && disconnected_coins.size()) {
818  auto utxod = FindRandomFrom(coinbase_coins);
819  tx = std::get<0>(utxod->second);
820  prevout = tx.vin[0].prevout;
821  // PIVX: no duplicates
822  BOOST_CHECK(!utxoset.count(prevout));
823  disconnected_coins.erase(utxod->first);
824  continue;
825  }
826 
827  // 16/20 times create a regular tx
828  else {
829  auto utxod = FindRandomFrom(utxoset);
830  prevout = utxod->first;
831 
832  // Construct the tx to spend the coins of prevouthash
833  tx.vin[0].prevout = prevout;
834  assert(!CTransaction(tx).IsCoinBase());
835  }
836  // In this simple test coins only have two states, spent or unspent, save the unspent state to restore
837  old_coin = result[prevout];
838 
839  // Update the expected result of prevouthash to know these coins are spent
840  result[prevout].Clear();
841 
842  utxoset.erase(prevout);
843 
844  }
845  // Update the expected result to know about the new output coins
846  assert(tx.vout.size() == 1);
847  const COutPoint outpoint(tx.GetHash(), 0);
848  result[outpoint] = Coin(tx.vout[0], height, CTransaction(tx).IsCoinBase(), CTransaction(tx).IsCoinStake());
849 
850  // Call UpdateCoins on the top cache
851  CTxUndo undo;
852  UpdateCoins(tx, *(stack.back()), undo, height);
853 
854  // Update the utxo set for future spends
855  utxoset.insert(outpoint);
856 
857  // Track this tx and undo info to use later
858  utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
859 
860  } else if (utxoset.size()) {
861  //1/20 times undo a previous transaction
862  auto utxod = FindRandomFrom(utxoset);
863 
864  CTransaction& tx = std::get<0>(utxod->second);
865  CTxUndo& undo = std::get<1>(utxod->second);
866  Coin& orig_coin = std::get<2>(utxod->second);
867 
868  // Update the expected result
869  // Remove new outputs
870  result[utxod->first].Clear();
871  // If not coinbase restore prevout
872  if (!tx.IsCoinBase()) {
873  result[tx.vin[0].prevout] = orig_coin;
874  }
875 
876  // Disconnect the tx from the current UTXO
877  // See code in DisconnectBlock
878  // remove outputs
879  stack.back()->SpendCoin(utxod->first);
880 
881  // restore inputs
882  if (!tx.IsCoinBase()) {
883  const COutPoint &out = tx.vin[0].prevout;
884  Coin coin = undo.vprevout[0];
885  ApplyTxInUndo(std::move(coin), *(stack.back()), out);
886  }
887  // Store as a candidate for reconnection
888  disconnected_coins.insert(utxod->first);
889 
890  // Update the utxoset
891  utxoset.erase(utxod->first);
892  if (!tx.IsCoinBase())
893  utxoset.insert(tx.vin[0].prevout);
894  }
895 
896  // Once every 1000 iterations and at the end, verify the full cache.
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);
901  BOOST_CHECK(have == !coin.IsSpent());
902  BOOST_CHECK(coin == entry.second);
903  }
904  }
905 
906  // One every 10 iterations, remove a random entry from the cache
907  if (utxoset.size() > 1 && InsecureRandRange(20) == 0) {
908  stack[InsecureRandRange(stack.size())]->Uncache(FindRandomFrom(utxoset)->first);
909  }
910  if (disconnected_coins.size() > 1 && InsecureRandRange(20) == 0) {
911  stack[InsecureRandRange(stack.size())]->Uncache(FindRandomFrom(disconnected_coins)->first);
912  }
913 
914  if (InsecureRandRange(100) == 0) {
915  // Every 100 iterations, change the cache stack.
916  if (stack.size() > 0 && InsecureRandBool() == 0) {
917  stack.back()->Flush();
918  delete stack.back();
919  stack.pop_back();
920  }
921  if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
922  CCoinsView* tip = &base;
923  if (stack.size() > 0) {
924  tip = stack.back();
925  }
926  stack.push_back(new CCoinsViewCacheTest(tip));
927  }
928  }
929  }
930 
931  // Clean up the stack.
932  while (stack.size() > 0) {
933  delete stack.back();
934  stack.pop_back();
935  }
936 }
937 
938 BOOST_AUTO_TEST_CASE(ccoins_serialization)
939 {
940  // Good example
941  CDataStream ss1(ParseHex("00835800816115944e077fe7c803cfa57f29b36bf87c1d35"), SER_DISK, CLIENT_VERSION);
942  Coin cc1;
943  ss1 >> cc1;
944  BOOST_CHECK_EQUAL(cc1.out.nValue, 60000000000ULL);
945  BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
946 
947  // Good example
948  CDataStream ss2(ParseHex("8dcb7ebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
949  Coin cc2;
950  ss2 >> cc2;
951  BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
952  BOOST_CHECK_EQUAL(cc2.nHeight, 59807);
953  BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
954  BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
955 
956  // PIVX: Example with fCoinStake
957  CDataStream ss2b(ParseHex("97b401808b63008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
958  Coin cc2b;
959  ss2b >> cc2b;
960  BOOST_CHECK_EQUAL(cc2b.fCoinBase, false);
961  BOOST_CHECK_EQUAL(cc2b.fCoinStake, true);
962  BOOST_CHECK_EQUAL(cc2b.nHeight, 100000);
963  BOOST_CHECK_EQUAL(cc2b.out.nValue, 2002 * COIN);
964  BOOST_CHECK_EQUAL(HexStr(cc2b.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
965 
966  // Smallest possible example
967  CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
968  Coin cc3;
969  ss3 >> cc3;
970  BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
971  BOOST_CHECK_EQUAL(cc3.nHeight, 0);
972  BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
974 
975  // scriptPubKey that ends beyond the end of the stream
976  CDataStream ss4(ParseHex("000007"), SER_DISK, CLIENT_VERSION);
977  try {
978  Coin cc4;
979  ss4 >> cc4;
980  BOOST_CHECK_MESSAGE(false, "We should have thrown");
981  } catch (const std::ios_base::failure& e) {
982  }
983 
984  // Very large scriptPubKey (3*10^9 bytes) past the end of the stream
985  CDataStream tmp(SER_DISK, CLIENT_VERSION);
986  uint64_t x = 3000000000ULL;
987  tmp << VARINT(x);
988  BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
989  CDataStream ss5(ParseHex("00008a95c0bb00"), SER_DISK, CLIENT_VERSION);
990  try {
991  Coin cc5;
992  ss5 >> cc5;
993  BOOST_CHECK_MESSAGE(false, "We should have thrown");
994  } catch (const std::ios_base::failure& e) {
995  }
996 }
997 
998 const static COutPoint OUTPOINT;
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;
1005 const static char DIRTY = CCoinsCacheEntry::DIRTY;
1006 const static char FRESH = CCoinsCacheEntry::FRESH;
1007 const static char NO_ENTRY = -1;
1008 
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};
1012 
1013 void SetCoinsValue(CAmount value, Coin& coin)
1014 {
1015  assert(value != ABSENT);
1016  coin.Clear();
1017  assert(coin.IsSpent());
1018  if (value != PRUNED) {
1019  coin.out.nValue = value;
1020  coin.nHeight = 1;
1021  assert(!coin.IsSpent());
1022  }
1023 }
1024 
1025 size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
1026 {
1027  if (value == ABSENT) {
1028  assert(flags == NO_ENTRY);
1029  return 0;
1030  }
1031  assert(flags != NO_ENTRY);
1032  CCoinsCacheEntry entry;
1033  entry.flags = flags;
1034  SetCoinsValue(value, entry.coin);
1035  auto inserted = map.emplace(OUTPOINT, std::move(entry));
1036  assert(inserted.second);
1037  return inserted.first->second.coin.DynamicMemoryUsage();
1038 }
1039 
1040 void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
1041 {
1042  auto it = map.find(OUTPOINT);
1043  if (it == map.end()) {
1044  value = ABSENT;
1045  flags = NO_ENTRY;
1046  } else {
1047  if (it->second.coin.IsSpent()) {
1048  value = PRUNED;
1049  } else {
1050  value = it->second.coin.out.nValue;
1051  }
1052  flags = it->second.flags;
1053  assert(flags != NO_ENTRY);
1054  }
1055 }
1056 
1058 {
1059  CCoinsMap map;
1060  InsertCoinsMapEntry(map, value, flags);
1061  CAnchorsSaplingMap mapSaplingAnchors;
1062  CNullifiersMap mapSaplingNullifiers;
1063  view.BatchWrite(map, {}, {}, mapSaplingAnchors, mapSaplingNullifiers);
1064 }
1065 
1067 {
1068 public:
1069  SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
1070  {
1071  WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
1072  cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
1073  }
1074 
1076  CCoinsViewCacheTest base{&root};
1077  CCoinsViewCacheTest cache{&base};
1078 };
1079 
1080 void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
1081 {
1082  SingleEntryCacheTest test(base_value, cache_value, cache_flags);
1083  test.cache.AccessCoin(OUTPOINT);
1084  test.cache.SelfTest();
1085 
1086  CAmount result_value;
1087  char result_flags;
1088  GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
1089  BOOST_CHECK_EQUAL(result_value, expected_value);
1090  BOOST_CHECK_EQUAL(result_flags, expected_flags);
1091 }
1092 
1093 BOOST_AUTO_TEST_CASE(ccoins_access)
1094 {
1095  /* Check AccessCoin behavior, requesting a coin from a cache view layered on
1096  * top of a base view, and checking the resulting entry in the cache after
1097  * the access.
1098  *
1099  * Base Cache Result Cache Result
1100  * Value Value Value Flags Flags
1101  */
1102  CheckAccessCoin(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
1103  CheckAccessCoin(ABSENT, PRUNED, PRUNED, 0 , 0 );
1104  CheckAccessCoin(ABSENT, PRUNED, PRUNED, FRESH , FRESH );
1105  CheckAccessCoin(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
1106  CheckAccessCoin(ABSENT, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
1107  CheckAccessCoin(ABSENT, VALUE2, VALUE2, 0 , 0 );
1108  CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH , FRESH );
1109  CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY );
1110  CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
1111  CheckAccessCoin(PRUNED, ABSENT, PRUNED, NO_ENTRY , FRESH );
1112  CheckAccessCoin(PRUNED, PRUNED, PRUNED, 0 , 0 );
1113  CheckAccessCoin(PRUNED, PRUNED, PRUNED, FRESH , FRESH );
1114  CheckAccessCoin(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
1115  CheckAccessCoin(PRUNED, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
1116  CheckAccessCoin(PRUNED, VALUE2, VALUE2, 0 , 0 );
1117  CheckAccessCoin(PRUNED, VALUE2, VALUE2, FRESH , FRESH );
1118  CheckAccessCoin(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY );
1119  CheckAccessCoin(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
1120  CheckAccessCoin(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 );
1121  CheckAccessCoin(VALUE1, PRUNED, PRUNED, 0 , 0 );
1122  CheckAccessCoin(VALUE1, PRUNED, PRUNED, FRESH , FRESH );
1123  CheckAccessCoin(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
1124  CheckAccessCoin(VALUE1, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
1125  CheckAccessCoin(VALUE1, VALUE2, VALUE2, 0 , 0 );
1126  CheckAccessCoin(VALUE1, VALUE2, VALUE2, FRESH , FRESH );
1127  CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY );
1128  CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
1129 }
1130 
1131 void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
1132 {
1133  SingleEntryCacheTest test(base_value, cache_value, cache_flags);
1134  test.cache.SpendCoin(OUTPOINT);
1135  test.cache.SelfTest();
1136 
1137  CAmount result_value;
1138  char result_flags;
1139  GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
1140  BOOST_CHECK_EQUAL(result_value, expected_value);
1141  BOOST_CHECK_EQUAL(result_flags, expected_flags);
1142 };
1143 
1145 {
1146  /* Check SpendCoin behavior, requesting a coin from a cache view layered on
1147  * top of a base view, spending, and then checking
1148  * the resulting entry in the cache after the modification.
1149  *
1150  * Base Cache Result Cache Result
1151  * Value Value Value Flags Flags
1152  */
1153  CheckSpendCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
1154  CheckSpendCoins(ABSENT, PRUNED, PRUNED, 0 , DIRTY );
1155  CheckSpendCoins(ABSENT, PRUNED, ABSENT, FRESH , NO_ENTRY );
1156  CheckSpendCoins(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
1157  CheckSpendCoins(ABSENT, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
1158  CheckSpendCoins(ABSENT, VALUE2, PRUNED, 0 , DIRTY );
1159  CheckSpendCoins(ABSENT, VALUE2, ABSENT, FRESH , NO_ENTRY );
1160  CheckSpendCoins(ABSENT, VALUE2, PRUNED, DIRTY , DIRTY );
1161  CheckSpendCoins(ABSENT, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
1162  CheckSpendCoins(PRUNED, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
1163  CheckSpendCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY );
1164  CheckSpendCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
1165  CheckSpendCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
1166  CheckSpendCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
1167  CheckSpendCoins(PRUNED, VALUE2, PRUNED, 0 , DIRTY );
1168  CheckSpendCoins(PRUNED, VALUE2, ABSENT, FRESH , NO_ENTRY );
1169  CheckSpendCoins(PRUNED, VALUE2, PRUNED, DIRTY , DIRTY );
1170  CheckSpendCoins(PRUNED, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
1171  CheckSpendCoins(VALUE1, ABSENT, PRUNED, NO_ENTRY , DIRTY );
1172  CheckSpendCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY );
1173  CheckSpendCoins(VALUE1, PRUNED, ABSENT, FRESH , NO_ENTRY );
1174  CheckSpendCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
1175  CheckSpendCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
1176  CheckSpendCoins(VALUE1, VALUE2, PRUNED, 0 , DIRTY );
1177  CheckSpendCoins(VALUE1, VALUE2, ABSENT, FRESH , NO_ENTRY );
1178  CheckSpendCoins(VALUE1, VALUE2, PRUNED, DIRTY , DIRTY );
1179  CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
1180 }
1181 
1182 void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags)
1183 {
1184  SingleEntryCacheTest test(base_value, cache_value, cache_flags);
1185 
1186  CAmount result_value;
1187  char result_flags;
1188  try {
1189  CTxOut output;
1190  output.nValue = modify_value;
1191  test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, false, false), false);
1192  test.cache.SelfTest();
1193  GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
1194  } catch (std::logic_error& e) {
1195  result_value = FAIL;
1196  result_flags = NO_ENTRY;
1197  }
1198 
1199  BOOST_CHECK_EQUAL(result_value, expected_value);
1200  BOOST_CHECK_EQUAL(result_flags, expected_flags);
1201 }
1202 
1203 // Simple wrapper for CheckAddCoinBase function above that loops through
1204 // different possible base_values, making sure each one gives the same results.
1205 // This wrapper lets the coins_add test below be shorter and less repetitive,
1206 // while still verifying that the CoinsViewCache::ModifyNewCoins implementation
1207 // ignores base values.
1208 template <typename... Args>
1209 void CheckAddCoin(Args&&... args)
1210 {
1211  for (CAmount base_value : {ABSENT, PRUNED, VALUE1})
1212  CheckAddCoinBase(base_value, std::forward<Args>(args)...);
1213 }
1214 
1216 {
1217  /* Check ModifyNewCoin behavior, requesting a new coin from a cache view,
1218  * writing a modification to the coin, and then checking the resulting
1219  * entry in the cache after the modification. Verify behavior with the
1220  * with the ModifyNewCoin coinbase argument set to false, and to true.
1221  *
1222  * PIVX: Remove Coinbase argument (ref: https://github.com/PIVX-Project/PIVX/pull/1775)
1223  *
1224  * Cache Write Result Cache Result
1225  * Value Value Value Flags Flags
1226  */
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 );
1230  CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
1231  CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH );
1232  CheckAddCoin(VALUE2, VALUE3, FAIL, 0 , NO_ENTRY );
1233  CheckAddCoin(VALUE2, VALUE3, FAIL, FRESH , NO_ENTRY );
1234  CheckAddCoin(VALUE2, VALUE3, FAIL, DIRTY , NO_ENTRY );
1235  CheckAddCoin(VALUE2, VALUE3, FAIL, DIRTY|FRESH, NO_ENTRY );
1236 }
1237 
1238 void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
1239 {
1240  SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
1241 
1242  CAmount result_value;
1243  char result_flags;
1244  try {
1245  WriteCoinsViewEntry(test.cache, child_value, child_flags);
1246  test.cache.SelfTest();
1247  GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
1248  } catch (std::logic_error& e) {
1249  result_value = FAIL;
1250  result_flags = NO_ENTRY;
1251  }
1252 
1253  BOOST_CHECK_EQUAL(result_value, expected_value);
1254  BOOST_CHECK_EQUAL(result_flags, expected_flags);
1255 }
1256 
1258 {
1259  /* Check BatchWrite behavior, flushing one entry from a child cache to a
1260  * parent cache, and checking the resulting entry in the parent cache
1261  * after the write.
1262  *
1263  * Parent Child Result Parent Child Result
1264  * Value Value Value Flags Flags Flags
1265  */
1266  CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY );
1267  CheckWriteCoins(ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY , DIRTY );
1268  CheckWriteCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY );
1269  CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY , DIRTY );
1270  CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH);
1271  CheckWriteCoins(PRUNED, ABSENT, PRUNED, 0 , NO_ENTRY , 0 );
1272  CheckWriteCoins(PRUNED, ABSENT, PRUNED, FRESH , NO_ENTRY , FRESH );
1273  CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY , NO_ENTRY , DIRTY );
1274  CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
1275  CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
1276  CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
1277  CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
1278  CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
1279  CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
1280  CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
1281  CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
1282  CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
1283  CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
1284  CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
1285  CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
1286  CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
1287  CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
1288  CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
1289  CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
1290  CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
1291  CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0 , NO_ENTRY , 0 );
1292  CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH , NO_ENTRY , FRESH );
1293  CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
1294  CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
1295  CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
1296  CheckWriteCoins(VALUE1, PRUNED, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
1297  CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
1298  CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
1299  CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
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 );
1303  CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
1304  CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
1305  CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
1306  CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
1307  CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
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 );
1311 
1312  // The checks above omit cases where the child flags are not DIRTY, since
1313  // they would be too repetitive (the parent cache is never updated in these
1314  // cases). The loop below covers these cases and makes sure the parent cache
1315  // is always left unchanged.
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);
1321 }
1322 
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
bool operator==(const CBigNum &a, const CBigNum &b)
Definition: bignum.h:220
#define ss4(x)
Definition: bmw.c:146
#define ss1(x)
Definition: bmw.c:140
#define ss5(x)
Definition: bmw.c:147
#define ss2(x)
Definition: bmw.c:142
#define ss3(x)
Definition: bmw.c:144
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:283
void SetNullifiers(const CTransaction &tx, bool spent)
Definition: coins.cpp:550
void PushAnchor(const Tree &tree)
void PopAnchor(const uint256 &rt)
Definition: coins.cpp:542
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
Definition: coins.cpp:332
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const override
Retrieve the tree (Sapling) at a particular anchored root in the chain.
Definition: coins.cpp:422
size_t cachedCoinsUsage
Definition: coins.h:298
uint256 GetBestAnchor() const override
Get the current "tip" or the latest anchored tree root in the chain.
Definition: coins.cpp:561
bool Flush()
Push the modifications applied to this cache to its base.
Definition: coins.cpp:309
CCoinsMap cacheCoins
Definition: coins.h:290
Abstract view on the open txout dataset.
Definition: coins.h:201
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSaplingAnchor, CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSaplingNullifiers)
Do a bulk modification (multiple Coin changes + BestBlock change).
Definition: coins.cpp:22
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:21
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
bool IsUnspendable() const
Returns whether the script is guaranteed to fail at execution, regardless of the initial stack.
Definition: script.h:654
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:244
std::vector< CTxIn > vin
Definition: transaction.h:270
bool IsCoinBase() const
Definition: transaction.h:376
An output of a transaction.
Definition: transaction.h:137
CScript scriptPubKey
Definition: transaction.h:140
CAmount nValue
Definition: transaction.h:139
Undo information for a CTransaction.
Definition: undo.h:56
std::vector< Coin > vprevout
Definition: undo.h:59
A UTXO entry.
Definition: coins.h:32
void Clear()
Definition: coins.h:50
bool fCoinStake
whether the containing transaction was a coinstake
Definition: coins.h:38
CTxOut out
unspent transaction output
Definition: coins.h:41
bool IsSpent() const
Definition: coins.h:86
bool fCoinBase
whether the containing transaction was a coinbase
Definition: coins.h:35
uint32_t nHeight
at which height the containing transaction was included in the active block chain
Definition: coins.h:44
CCoinsViewCacheTest cache
CCoinsViewCacheTest base
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
CCoinsView root
A shielded input to a transaction.
uint256 nullifier
The nullifier of the input note.
bool IsNull() const
Definition: uint256.h:36
size_type size() const
Definition: prevector.h:277
void assign(size_type n, const T &val)
Definition: prevector.h:213
160-bit opaque blob.
Definition: uint256.h:127
256-bit opaque blob.
Definition: uint256.h:138
void BatchWriteAnchors(Map &mapAnchors, Map &cacheAnchors, size_t &cachedCoinsUsage)
Definition: coins.cpp:186
const Coin & AccessByTxid(const CCoinsViewCache &view, const uint256 &txid)
Utility function to find any unspent output with a given txid.
Definition: coins.cpp:409
void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers)
Definition: coins.cpp:218
std::unordered_map< uint256, CAnchorsSaplingCacheEntry, SaltedIdHasher > CAnchorsSaplingMap
Definition: coins.h:174
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher > CCoinsMap
Definition: coins.h:177
std::unordered_map< uint256, CNullifiersCacheEntry, SaltedIdHasher > CNullifiersMap
Definition: coins.h:175
void checkNullifierCache(const CCoinsViewCache &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache)
size_t InsertCoinsMapEntry(CCoinsMap &map, CAmount value, char flags)
void anchorsFlushImpl()
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)
UtxoData utxoData
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)
void anchorsTestImpl()
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)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
int flags
Definition: pivx-tx.cpp:400
uint256 GetRandHash() noexcept
Definition: random.cpp:596
@ OP_RETURN
Definition: script.h:87
@ SER_DISK
Definition: serialize.h:175
#define VARINT(obj)
Definition: serialize.h:513
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a PIVX scriptPubKey for the given CTxDestination.
Definition: standard.cpp:278
Basic testing setup.
Definition: test_pivx.h:51
Definition: coins.h:134
unsigned char flags
Definition: coins.h:136
Coin coin
Definition: coins.h:135
@ FRESH
Definition: coins.h:140
@ DIRTY
Definition: coins.h:139
A mutable version of CTransaction.
Definition: transaction.h:409
Optional< SaplingTxData > sapData
Definition: transaction.h:415
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:96
std::vector< CTxOut > vout
Definition: transaction.h:411
std::vector< CTxIn > vin
Definition: transaction.h:410
@ DIRTY
Definition: coins.h:168
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:456
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)