LCOV - code coverage report
Current view: top level - src/test/librust - sapling_wallet_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 791 792 99.9 %
Date: 2025-04-02 01:23:23 Functions: 34 34 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2016-2020 The ZCash developers
       2             : // Copyright (c) 2020-2021 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "wallet/test/wallet_test_fixture.h"
       7             : 
       8             : #include <sodium.h>
       9             : 
      10             : #include "chainparams.h"
      11             : #include "key_io.h"
      12             : #include "validation.h"
      13             : #include "optional.h"
      14             : #include "primitives/block.h"
      15             : #include "random.h"
      16             : #include "sapling/transaction_builder.h"
      17             : #include "test/librust/utiltest.h"
      18             : #include "wallet/wallet.h"
      19             : #include "consensus/merkle.h"
      20             : #include "sapling/note.h"
      21             : #include "sapling/noteencryption.h"
      22             : 
      23             : #include <boost/filesystem.hpp>
      24             : 
      25             : #include <boost/test/unit_test.hpp>
      26             : 
      27          13 : void setupWallet(CWallet& wallet)
      28             : {
      29          13 :     wallet.SetMinVersion(FEATURE_SAPLING);
      30          13 :     wallet.SetupSPKM(false);
      31          13 : }
      32             : 
      33         118 : std::vector<SaplingOutPoint> SetSaplingNoteData(CWalletTx& wtx) {
      34         118 :     mapSaplingNoteData_t saplingNoteData;
      35         118 :     SaplingOutPoint saplingOutPoint = {wtx.GetHash(), 0};
      36         236 :     SaplingNoteData saplingNd;
      37             :     // set this as internal note (with non-nullopt ivk)
      38         236 :     saplingNd.ivk = libzcash::SaplingIncomingViewingKey();
      39         118 :     saplingNoteData[saplingOutPoint] = saplingNd;
      40         118 :     wtx.SetSaplingNoteData(saplingNoteData);
      41         118 :     std::vector<SaplingOutPoint> saplingNotes {saplingOutPoint};
      42         236 :     return saplingNotes;
      43             : }
      44             : 
      45         114 : SaplingOutPoint CreateValidBlock(CWallet& wallet,
      46             :                                 libzcash::SaplingExtendedSpendingKey& sk,
      47             :                                 const CBlockIndex& index,
      48             :                                 CBlock& block,
      49             :                                 SaplingMerkleTree& saplingTree)
      50             : {
      51         114 :     CWalletTx wtx = GetValidSaplingReceive(Params().GetConsensus(),
      52         114 :                                            wallet, sk, 0, true);
      53         228 :     auto saplingNotes = SetSaplingNoteData(wtx);
      54         114 :     wallet.LoadToWallet(wtx);
      55             : 
      56         114 :     block.vtx.emplace_back(wtx.tx);
      57         114 :     wallet.IncrementNoteWitnesses(&index, &block, saplingTree);
      58             : 
      59         228 :     return saplingNotes[0];
      60             : }
      61             : 
      62         241 : uint256 GetWitnessesAndAnchors(CWallet& wallet,
      63             :                                const std::vector<SaplingOutPoint>& saplingNotes,
      64             :                                std::vector<Optional<SaplingWitness>>& saplingWitnesses)
      65             : {
      66         241 :     saplingWitnesses.clear();
      67         241 :     uint256 saplingAnchor;
      68         241 :     wallet.GetSaplingScriptPubKeyMan()->GetSaplingNoteWitnesses(saplingNotes, saplingWitnesses, saplingAnchor);
      69         241 :     return saplingAnchor;
      70             : }
      71             : 
      72             : BOOST_FIXTURE_TEST_SUITE(sapling_wallet_tests, WalletRegTestingSetup)
      73             : 
      74           2 : BOOST_AUTO_TEST_CASE(SetSaplingNoteAddrsInCWalletTx) {
      75           1 :     auto consensusParams = Params().GetConsensus();
      76             : 
      77           1 :     CWallet& wallet = m_wallet;
      78           2 :     LOCK(wallet.cs_wallet);
      79           1 :     setupWallet(wallet);
      80             : 
      81           1 :     auto sk = GetTestMasterSaplingSpendingKey();
      82           1 :     auto expsk = sk.expsk;
      83           1 :     auto fvk = expsk.full_viewing_key();
      84           1 :     auto ivk = fvk.in_viewing_key();
      85           1 :     auto pk = sk.DefaultAddress();
      86             : 
      87           2 :     libzcash::SaplingNote note(pk, 50000000);
      88           2 :     auto cm = note.cmu().get();
      89           2 :     SaplingMerkleTree tree;
      90           1 :     tree.append(cm);
      91           1 :     auto anchor = tree.root();
      92           2 :     auto witness = tree.witness();
      93             : 
      94           2 :     Optional<uint256> nf = note.nullifier(fvk, witness.position());
      95           2 :     BOOST_CHECK(nf != boost::none);
      96           1 :     uint256 nullifier = nf.get();
      97             : 
      98           2 :     auto builder = TransactionBuilder(consensusParams);
      99           1 :     builder.AddSaplingSpend(expsk, note, anchor, witness);
     100           1 :     builder.AddSaplingOutput(fvk.ovk, pk, 40000000, {});
     101           1 :     builder.SetFee(10000000);
     102           2 :     auto tx = builder.Build().GetTxOrThrow();
     103             : 
     104           1 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     105             : 
     106           1 :     BOOST_CHECK_EQUAL(0, wtx.mapSaplingNoteData.size());
     107           2 :     mapSaplingNoteData_t noteData;
     108             : 
     109           1 :     SaplingOutPoint op {wtx.GetHash(), 0};
     110           2 :     SaplingNoteData nd;
     111           1 :     nd.nullifier = nullifier;
     112           1 :     nd.ivk = ivk;
     113           1 :     nd.witnesses.push_front(witness);
     114           1 :     nd.witnessHeight = 123;
     115           2 :     noteData.insert(std::make_pair(op, nd));
     116             : 
     117           1 :     wtx.SetSaplingNoteData(noteData);
     118           2 :     BOOST_CHECK(noteData == wtx.mapSaplingNoteData);
     119             : 
     120             :     // Test individual fields in case equality operator is defined/changed.
     121           2 :     BOOST_CHECK(wtx.mapSaplingNoteData[op].IsMyNote());
     122           2 :     BOOST_CHECK(ivk == *(wtx.mapSaplingNoteData[op].ivk));
     123           2 :     BOOST_CHECK(nullifier == wtx.mapSaplingNoteData[op].nullifier);
     124           2 :     BOOST_CHECK(nd.witnessHeight == wtx.mapSaplingNoteData[op].witnessHeight);
     125           2 :     BOOST_CHECK(witness == wtx.mapSaplingNoteData[op].witnesses.front());
     126           1 : }
     127             : 
     128             : // Cannot add note data for an index which does not exist in tx.vShieldedOutput
     129           2 : BOOST_AUTO_TEST_CASE(SetInvalidSaplingNoteDataInCWalletTx) {
     130           1 :     CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
     131           1 :     BOOST_CHECK_EQUAL(0, wtx.mapSaplingNoteData.size());
     132             : 
     133           2 :     mapSaplingNoteData_t noteData;
     134           2 :     SaplingOutPoint op {uint256(), 1};
     135           2 :     SaplingNoteData nd;
     136           2 :     noteData.insert(std::make_pair(op, nd));
     137             : 
     138           2 :     BOOST_CHECK_THROW(wtx.SetSaplingNoteData(noteData), std::logic_error);
     139           1 : }
     140             : 
     141           2 : BOOST_AUTO_TEST_CASE(FindMySaplingNotes)
     142             : {
     143           1 :     auto consensusParams = Params().GetConsensus();
     144             : 
     145           1 :     CWallet& wallet = m_wallet;
     146           2 :     LOCK(wallet.cs_wallet);
     147           1 :     wallet.SetupSPKM(false);
     148             : 
     149             :     // Generate dummy Sapling address
     150           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     151           1 :     auto expsk = sk.expsk;
     152           1 :     auto extfvk = sk.ToXFVK();
     153           1 :     auto pa = sk.DefaultAddress();
     154             : 
     155           2 :     auto testNote = GetTestSaplingNote(pa, 50000000);
     156             : 
     157             :     // Generate transaction
     158           2 :     auto builder = TransactionBuilder(consensusParams);
     159           1 :     builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
     160           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 25000000, {});
     161           1 :     builder.SetFee(10000000);
     162           2 :     auto tx = builder.Build().GetTxOrThrow();
     163             : 
     164             :     // No Sapling notes can be found in tx which does not belong to the wallet
     165           1 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     166           2 :     BOOST_CHECK(!wallet.HaveSaplingSpendingKey(extfvk));
     167           3 :     auto noteMap = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     168           1 :     BOOST_CHECK_EQUAL(0, noteMap.size());
     169             : 
     170             :     // Add spending key to wallet, so Sapling notes can be found
     171           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     172           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     173           2 :     noteMap = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     174           1 :     BOOST_CHECK_EQUAL(2, noteMap.size());
     175           1 : }
     176             : 
     177             : // Generate note A and spend to create note B, from which we spend to create two conflicting transactions
     178           2 : BOOST_AUTO_TEST_CASE(GetConflictedSaplingNotes)
     179             : {
     180           1 :     auto consensusParams = Params().GetConsensus();
     181             : 
     182           1 :     CWallet& wallet = m_wallet;
     183           3 :     LOCK2(cs_main, wallet.cs_wallet);
     184           1 :     setupWallet(wallet);
     185             : 
     186             :     // Generate Sapling address
     187           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     188           1 :     auto expsk = sk.expsk;
     189           1 :     auto extfvk = sk.ToXFVK();
     190           1 :     auto ivk = extfvk.fvk.in_viewing_key();
     191           1 :     auto pk = sk.DefaultAddress();
     192             : 
     193           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     194           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     195             : 
     196             :     // Generate note A
     197           2 :     libzcash::SaplingNote note(pk, 50000000);
     198           2 :     auto cm = note.cmu().get();
     199           2 :     SaplingMerkleTree saplingTree;
     200           1 :     saplingTree.append(cm);
     201           1 :     auto anchor = saplingTree.root();
     202           2 :     auto witness = saplingTree.witness();
     203             : 
     204             :     // Generate tx to create output note B
     205           2 :     auto builder = TransactionBuilder(consensusParams);
     206           1 :     builder.AddSaplingSpend(expsk, note, anchor, witness);
     207           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 35000000, {});
     208           1 :     builder.SetFee(10000000);
     209           2 :     auto tx = builder.Build().GetTxOrThrow();
     210           2 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     211             : 
     212             :     // Fake-mine the transaction
     213           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     214           1 :     CBlock block;
     215           3 :     block.hashPrevBlock = chainActive[0]->GetBlockHash();
     216           1 :     block.vtx.emplace_back(wtx.tx);
     217           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     218           1 :     const auto& blockHash = block.GetHash();
     219           2 :     CBlockIndex fakeIndex {block};
     220           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
     221           1 :     fakeIndex.phashBlock = &((*mi).first);
     222           1 :     chainActive.SetTip(&fakeIndex);
     223           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
     224           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     225             : 
     226             :     // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
     227           3 :     auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     228           2 :     BOOST_CHECK(saplingNoteData.size() > 0);
     229           1 :     wtx.SetSaplingNoteData(saplingNoteData);
     230           1 :     wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
     231           2 :     BOOST_CHECK(wallet.LoadToWallet(wtx));
     232             : 
     233             :     // Simulate receiving new block and ChainTip signal
     234           1 :     wallet.IncrementNoteWitnesses(&fakeIndex, &block, saplingTree);
     235           1 :     wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
     236             : 
     237             :     // Retrieve the updated wtx from wallet
     238           1 :     const uint256& hash = wtx.GetHash();
     239           1 :     wtx = wallet.mapWallet.at(hash);
     240             : 
     241             :     // Decrypt output note B
     242           1 :     auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
     243           1 :             wtx.tx->sapData->vShieldedOutput[0].encCiphertext,
     244             :             ivk,
     245           1 :             wtx.tx->sapData->vShieldedOutput[0].ephemeralKey,
     246           2 :             wtx.tx->sapData->vShieldedOutput[0].cmu);
     247           2 :     BOOST_CHECK(static_cast<bool>(maybe_pt) == true);
     248           2 :     auto maybe_note = maybe_pt.get().note(ivk);
     249           2 :     BOOST_CHECK(static_cast<bool>(maybe_note) == true);
     250           2 :     auto note2 = maybe_note.get();
     251             : 
     252           1 :     SaplingOutPoint sop0(wtx.GetHash(), 0);
     253           2 :     auto spend_note_witness =  wtx.mapSaplingNoteData[sop0].witnesses.front();
     254           2 :     auto maybe_nf = note2.nullifier(extfvk.fvk, spend_note_witness.position());
     255           2 :     BOOST_CHECK(static_cast<bool>(maybe_nf) == true);
     256             : 
     257           1 :     anchor = saplingTree.root();
     258             : 
     259             :     // Create transaction to spend note B
     260           2 :     auto builder2 = TransactionBuilder(consensusParams);
     261           1 :     builder2.SetFee(10000000);
     262           1 :     builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
     263           1 :     builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 20000000, {});
     264           2 :     auto tx2 = builder2.Build().GetTxOrThrow();
     265             : 
     266             :     // Create conflicting transaction which also spends note B
     267           2 :     auto builder3 = TransactionBuilder(consensusParams);
     268           1 :     builder3.SetFee(10000000);
     269           1 :     builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
     270           1 :     builder3.AddSaplingOutput(extfvk.fvk.ovk, pk, 19999000, {});
     271           2 :     auto tx3 = builder3.Build().GetTxOrThrow();
     272             : 
     273           2 :     CWalletTx wtx2 {&wallet, MakeTransactionRef(tx2)};
     274           1 :     CWalletTx wtx3 {&wallet, MakeTransactionRef(tx3)};
     275             : 
     276           1 :     const auto& hash2 = wtx2.GetHash();
     277           1 :     const auto& hash3 = wtx3.GetHash();
     278             : 
     279             :     // No conflicts for no spends (wtx is currently the only transaction in the wallet)
     280           1 :     BOOST_CHECK_EQUAL(0, wallet.GetConflicts(hash2).size());
     281           1 :     BOOST_CHECK_EQUAL(0, wallet.GetConflicts(hash3).size());
     282             : 
     283             :     // No conflicts for one spend
     284           2 :     BOOST_CHECK(wallet.LoadToWallet(wtx2));
     285           1 :     BOOST_CHECK_EQUAL(0, wallet.GetConflicts(hash2).size());
     286             : 
     287             :     // Conflicts for two spends
     288           2 :     BOOST_CHECK(wallet.LoadToWallet(wtx3));
     289           2 :     auto c3 = wallet.GetConflicts(hash2);
     290           1 :     BOOST_CHECK_EQUAL(2, c3.size());
     291           3 :     BOOST_CHECK(std::set<uint256>({hash2, hash3}) == c3);
     292             : 
     293             :     // Tear down
     294           1 :     chainActive.SetTip(nullptr);
     295           1 :     mapBlockIndex.erase(blockHash);
     296           1 : }
     297             : 
     298           2 : BOOST_AUTO_TEST_CASE(SaplingNullifierIsSpent)
     299             : {
     300           1 :     auto consensusParams = Params().GetConsensus();
     301             : 
     302           1 :     CWallet& wallet = m_wallet;
     303           3 :     LOCK2(cs_main, wallet.cs_wallet);
     304           1 :     setupWallet(wallet);
     305             : 
     306             :     // Generate dummy Sapling address
     307           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     308           1 :     auto expsk = sk.expsk;
     309           1 :     auto extfvk = sk.ToXFVK();
     310           1 :     auto pa = sk.DefaultAddress();
     311             : 
     312           2 :     auto testNote = GetTestSaplingNote(pa, 50000000);
     313             : 
     314             :     // Generate transaction
     315           2 :     auto builder = TransactionBuilder(consensusParams);
     316           1 :     builder.AddSaplingSpend(expsk,  testNote.note, testNote.tree.root(), testNote.tree.witness());
     317           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 2500000, {});
     318           1 :     builder.SetFee(10000000);
     319           2 :     auto tx = builder.Build().GetTxOrThrow();
     320             : 
     321           1 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     322           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     323           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     324             : 
     325             :     // Manually compute the nullifier based on the known position
     326           3 :     auto nf = testNote.note.nullifier(extfvk.fvk, testNote.tree.witness().position());
     327           2 :     BOOST_CHECK(nf);
     328           1 :     uint256 nullifier = nf.get();
     329             : 
     330             :     // Verify note has not been spent
     331           2 :     BOOST_CHECK(!wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier));
     332             : 
     333             :     // Fake-mine the transaction
     334           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     335           2 :     CBlock block;
     336           2 :     block.hashPrevBlock = chainActive[0]->GetBlockHash();
     337           1 :     block.vtx.emplace_back(wtx.tx);
     338           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     339           1 :     const auto& blockHash = block.GetHash();
     340           2 :     CBlockIndex fakeIndex {block};
     341           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
     342           1 :     fakeIndex.phashBlock = &((*mi).first);
     343           1 :     chainActive.SetTip(&fakeIndex);
     344           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
     345           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     346             : 
     347           2 :     wallet.BlockConnected(std::make_shared<CBlock>(block), mi->second);
     348             : 
     349             :     // Verify note has been spent
     350           2 :     BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier));
     351             : 
     352             :     // Tear down
     353           1 :     chainActive.SetTip(nullptr);
     354           1 :     mapBlockIndex.erase(blockHash);
     355           1 : }
     356             : 
     357           2 : BOOST_AUTO_TEST_CASE(NavigateFromSaplingNullifierToNote)
     358             : {
     359           1 :     auto consensusParams = Params().GetConsensus();
     360             : 
     361           1 :     CWallet& wallet = m_wallet;
     362           3 :     LOCK2(cs_main, wallet.cs_wallet);
     363           1 :     setupWallet(wallet);
     364             : 
     365             :     // Generate dummy Sapling address
     366           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     367           1 :     auto expsk = sk.expsk;
     368           1 :     auto extfvk = sk.ToXFVK();
     369           1 :     auto pa = sk.DefaultAddress();
     370             : 
     371           2 :     auto testNote = GetTestSaplingNote(pa, 50000000);
     372             : 
     373             :     // Generate transaction
     374           2 :     auto builder = TransactionBuilder(consensusParams);
     375           1 :     builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
     376           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 25000000, {});
     377           1 :     builder.SetFee(10000000);
     378           2 :     auto tx = builder.Build().GetTxOrThrow();
     379             : 
     380           1 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     381           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     382           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     383             : 
     384             :     // Manually compute the nullifier based on the expected position
     385           3 :     auto nf = testNote.note.nullifier(extfvk.fvk, testNote.tree.witness().position());
     386           2 :     BOOST_CHECK(nf);
     387           1 :     uint256 nullifier = nf.get();
     388             : 
     389             :     // Verify dummy note is unspent
     390           2 :     BOOST_CHECK(!wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier));
     391             : 
     392             :     // Fake-mine the transaction
     393           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     394           2 :     CBlock block;
     395           2 :     block.hashPrevBlock = chainActive[0]->GetBlockHash();
     396           1 :     block.vtx.emplace_back(wtx.tx);
     397           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     398           1 :     const auto& blockHash = block.GetHash();
     399           2 :     CBlockIndex fakeIndex {block};
     400           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
     401           1 :     fakeIndex.phashBlock = &((*mi).first);
     402           1 :     chainActive.SetTip(&fakeIndex);
     403           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
     404           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     405             : 
     406             :     // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
     407           1 :     wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
     408           3 :     auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     409           2 :     BOOST_CHECK(saplingNoteData.size() > 0);
     410           1 :     wtx.SetSaplingNoteData(saplingNoteData);
     411           1 :     wallet.LoadToWallet(wtx);
     412             : 
     413             :     // Verify dummy note is now spent, as AddToWallet invokes AddToSpends()
     414           1 :     wallet.SetLastBlockProcessed(mi->second);
     415           2 :     BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier));
     416             : 
     417             :     // Test invariant: no witnesses means no nullifier.
     418           1 :     BOOST_CHECK_EQUAL(0, wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.size());
     419           3 :     for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
     420           4 :         SaplingNoteData nd = item.second;
     421           4 :         BOOST_CHECK(nd.witnesses.empty());
     422           4 :         BOOST_CHECK(!nd.nullifier);
     423             :     }
     424             : 
     425             :     // Simulate receiving new block and ChainTip signal
     426           1 :     wallet.IncrementNoteWitnesses(&fakeIndex, &block, testNote.tree);
     427           1 :     wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
     428             : 
     429             :     // Retrieve the updated wtx from wallet
     430           1 :     const uint256& hash = wtx.GetHash();
     431           1 :     wtx = wallet.mapWallet.at(hash);
     432             : 
     433             :     // Verify Sapling nullifiers map to SaplingOutPoints
     434           1 :     BOOST_CHECK_EQUAL(2, wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.size());
     435           3 :     for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
     436           2 :         SaplingOutPoint op = item.first;
     437           4 :         SaplingNoteData nd = item.second;
     438           4 :         BOOST_CHECK(hash == op.hash);
     439           2 :         BOOST_CHECK_EQUAL(1, nd.witnesses.size());
     440           4 :         BOOST_CHECK(nd.nullifier);
     441           2 :         auto nf = nd.nullifier.get();
     442           4 :         BOOST_CHECK_EQUAL(1, wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.count(nf));
     443           4 :         BOOST_CHECK(op.hash == wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes[nf].hash);
     444           2 :         BOOST_CHECK_EQUAL(op.n, wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes[nf].n);
     445             :     }
     446             : 
     447             :     // Tear down
     448           1 :     chainActive.SetTip(nullptr);
     449           1 :     mapBlockIndex.erase(blockHash);
     450           1 : }
     451             : 
     452             : // Create note A, spend A to create note B, spend and verify note B is from me.
     453           2 : BOOST_AUTO_TEST_CASE(SpentSaplingNoteIsFromMe)
     454             : {
     455           1 :     auto consensusParams = Params().GetConsensus();
     456             : 
     457           1 :     CWallet& wallet = m_wallet;
     458           3 :     LOCK2(cs_main, wallet.cs_wallet);
     459           1 :     setupWallet(wallet);
     460             : 
     461             :     // Generate Sapling address
     462           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     463           1 :     auto expsk = sk.expsk;
     464           1 :     auto extfvk = sk.ToXFVK();
     465           1 :     auto ivk = extfvk.fvk.in_viewing_key();
     466           1 :     auto pk = sk.DefaultAddress();
     467             : 
     468             :     // Generate Sapling note A
     469           2 :     libzcash::SaplingNote note(pk, 50000000);
     470           2 :     auto cm = note.cmu().get();
     471           2 :     SaplingMerkleTree saplingTree;
     472           1 :     saplingTree.append(cm);
     473           1 :     auto anchor = saplingTree.root();
     474           2 :     auto witness = saplingTree.witness();
     475             : 
     476             :     // Generate transaction, which sends funds to note B
     477           2 :     auto builder = TransactionBuilder(consensusParams);
     478           1 :     builder.AddSaplingSpend(expsk, note, anchor, witness);
     479           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 25000000, {});
     480           1 :     builder.SetFee(10000000);
     481           2 :     auto tx = builder.Build().GetTxOrThrow();
     482             : 
     483           2 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     484           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     485           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     486             : 
     487             :     // Fake-mine the transaction
     488           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     489           1 :     CBlock block;
     490           2 :     block.hashPrevBlock = chainActive[0]->GetBlockHash();
     491           1 :     block.vtx.emplace_back(wtx.tx);
     492           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     493           1 :     const auto& blockHash = block.GetHash();
     494           2 :     CBlockIndex fakeIndex {block};
     495           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
     496           1 :     fakeIndex.phashBlock = &((*mi).first);
     497           1 :     chainActive.SetTip(&fakeIndex);
     498           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
     499           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     500             : 
     501           3 :     auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     502           2 :     BOOST_CHECK(saplingNoteData.size() > 0);
     503           1 :     wtx.SetSaplingNoteData(saplingNoteData);
     504           1 :     wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
     505           1 :     wallet.LoadToWallet(wtx);
     506             : 
     507             :     // Simulate receiving new block and ChainTip signal.
     508             :     // This triggers calculation of nullifiers for notes belonging to this wallet
     509             :     // in the output descriptions of wtx.
     510           1 :     wallet.IncrementNoteWitnesses(&fakeIndex, &block, saplingTree);
     511           1 :     wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
     512           1 :     wallet.SetLastBlockProcessed(mi->second);
     513             : 
     514             :     // Retrieve the updated wtx from wallet
     515           1 :     wtx = wallet.mapWallet.at(wtx.GetHash());
     516             : 
     517             :     // The test wallet never received the fake note which is being spent, so there
     518             :     // is no mapping from nullifier to notedata stored in mapSaplingNullifiersToNotes.
     519             :     // Therefore the wallet does not know the tx belongs to the wallet.
     520           2 :     BOOST_CHECK(!wallet.IsFromMe(wtx.tx));
     521             : 
     522             :     // Manually compute the nullifier and check map entry does not exist
     523           1 :     auto nf = note.nullifier(extfvk.fvk, witness.position());
     524           2 :     BOOST_CHECK(nf);
     525           3 :     BOOST_CHECK(!wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.count(nf.get()));
     526             : 
     527             :     // Decrypt note B
     528           1 :     auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
     529           1 :             wtx.tx->sapData->vShieldedOutput[0].encCiphertext,
     530             :             ivk,
     531           1 :             wtx.tx->sapData->vShieldedOutput[0].ephemeralKey,
     532           2 :             wtx.tx->sapData->vShieldedOutput[0].cmu);
     533           1 :     BOOST_CHECK_EQUAL(static_cast<bool>(maybe_pt), true);
     534           2 :     auto maybe_note = maybe_pt.get().note(ivk);
     535           1 :     BOOST_CHECK_EQUAL(static_cast<bool>(maybe_note), true);
     536           2 :     auto note2 = maybe_note.get();
     537             : 
     538             :     // Get witness to retrieve position of note B we want to spend
     539           1 :     SaplingOutPoint sop0(wtx.GetHash(), 0);
     540           2 :     auto spend_note_witness =  wtx.mapSaplingNoteData[sop0].witnesses.front();
     541           2 :     auto maybe_nf = note2.nullifier(extfvk.fvk, spend_note_witness.position());
     542           1 :     BOOST_CHECK_EQUAL(static_cast<bool>(maybe_nf), true);
     543           1 :     auto nullifier2 = maybe_nf.get();
     544             : 
     545             :     // NOTE: Not updating the anchor results in a core dump.  Shouldn't builder just return error?
     546             :     // *** Error in `./zcash-gtest': double free or corruption (out): 0x00007ffd8755d990 ***
     547           1 :     anchor = saplingTree.root();
     548             : 
     549             :     // Create transaction to spend note B
     550           2 :     auto builder2 = TransactionBuilder(consensusParams);
     551           1 :     builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
     552           1 :     builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 12500000, {});
     553           1 :     builder2.SetFee(10000000);
     554           2 :     auto tx2 = builder2.Build().GetTxOrThrow();
     555           1 :     BOOST_CHECK_EQUAL(tx2.vin.size(), 0);
     556           1 :     BOOST_CHECK_EQUAL(tx2.vout.size(), 0);
     557           1 :     BOOST_CHECK_EQUAL(tx2.sapData->vShieldedSpend.size(), 1);
     558           1 :     BOOST_CHECK_EQUAL(tx2.sapData->vShieldedOutput.size(), 2);
     559           1 :     BOOST_CHECK_EQUAL(tx2.sapData->valueBalance, 10000000);
     560             : 
     561           2 :     CWalletTx wtx2 {&wallet, MakeTransactionRef(tx2)};
     562             : 
     563             :     // Fake-mine this tx into the next block
     564           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     565           1 :     CBlock block2;
     566           1 :     block2.vtx.emplace_back(wtx2.tx);
     567           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     568           1 :     block2.hashPrevBlock = blockHash;
     569           1 :     auto blockHash2 = block2.GetHash();
     570           2 :     CBlockIndex fakeIndex2 {block2};
     571           1 :     BlockMap::iterator mi2 = mapBlockIndex.emplace(blockHash2, &fakeIndex2).first;
     572           1 :     fakeIndex2.phashBlock = &((*mi2).first);
     573           1 :     fakeIndex2.nHeight = 1;
     574           1 :     chainActive.SetTip(&fakeIndex2);
     575           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex2));
     576           1 :     BOOST_CHECK_EQUAL(1, chainActive.Height());
     577             : 
     578           3 :     auto saplingNoteData2 = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx2.tx).first;
     579           2 :     BOOST_CHECK(saplingNoteData2.size() > 0);
     580           1 :     wtx2.SetSaplingNoteData(saplingNoteData2);
     581           1 :     wtx2.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex2.nHeight, block2.GetHash(), 0);
     582           1 :     wallet.LoadToWallet(wtx2);
     583           1 :     wallet.SetLastBlockProcessed(mi2->second);
     584             : 
     585             :     // Verify note B is spent. AddToWallet invokes AddToSpends which updates mapTxSaplingNullifiers
     586           2 :     BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier2));
     587             : 
     588             :     // Verify note B belongs to wallet.
     589           2 :     BOOST_CHECK(wallet.IsFromMe(wtx2.tx));
     590           3 :     BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.count(nullifier2));
     591             : 
     592             :     // Tear down
     593           1 :     chainActive.SetTip(nullptr);
     594           1 :     mapBlockIndex.erase(blockHash);
     595           1 :     mapBlockIndex.erase(blockHash2);
     596           1 : }
     597             : 
     598           2 : BOOST_AUTO_TEST_CASE(CachedWitnessesEmptyChain)
     599             : {
     600           2 :     auto consensusParams = Params().GetConsensus();
     601             : 
     602           1 :     CWallet& wallet = m_wallet;
     603           1 :     {
     604           1 :         LOCK(wallet.cs_wallet);
     605           1 :         setupWallet(wallet);
     606             :     }
     607             : 
     608           1 :     auto sk = GetTestMasterSaplingSpendingKey();
     609           1 :     CWalletTx wtx = GetValidSaplingReceive(Params().GetConsensus(), wallet, sk, 10, true);
     610             : 
     611           2 :     std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
     612           2 :     std::vector<Optional<SaplingWitness>> saplingWitnesses;
     613             : 
     614           1 :     ::GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     615             : 
     616           2 :     BOOST_CHECK(!(bool) saplingWitnesses[0]);
     617             : 
     618           1 :     wallet.LoadToWallet(wtx);
     619             : 
     620           1 :     ::GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     621             : 
     622           2 :     BOOST_CHECK(!(bool) saplingWitnesses[0]);
     623             : 
     624           1 :     CBlock block;
     625           1 :     block.vtx.emplace_back(wtx.tx);
     626           2 :     CBlockIndex index(block);
     627           2 :     SaplingMerkleTree saplingTree;
     628           1 :     wallet.IncrementNoteWitnesses(&index, &block, saplingTree);
     629             : 
     630           1 :     ::GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     631             : 
     632           2 :     BOOST_CHECK((bool) saplingWitnesses[0]);
     633             : 
     634             :     // Until zcash#1302 is implemented, this should trigger an assertion
     635           2 :     BOOST_CHECK_THROW(wallet.DecrementNoteWitnesses(&index), std::runtime_error);
     636           1 : }
     637             : 
     638           2 : BOOST_AUTO_TEST_CASE(CachedWitnessesChainTip)
     639             : {
     640           2 :     auto consensusParams = Params().GetConsensus();
     641             : 
     642           1 :     libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey();
     643           1 :     CWallet& wallet = m_wallet;
     644           1 :     {
     645           1 :         LOCK(wallet.cs_wallet);
     646           1 :         setupWallet(wallet);
     647           2 :         BOOST_CHECK(wallet.AddSaplingZKey(sk));
     648           2 :         BOOST_CHECK(wallet.HaveSaplingSpendingKey(sk.ToXFVK()));
     649             :     }
     650             : 
     651           1 :     uint256 anchors1;
     652           1 :     CBlock block1;
     653           2 :     SaplingMerkleTree saplingTree;
     654             : 
     655           1 :     {
     656             :         // First block (case tested in _empty_chain)
     657           1 :         CBlockIndex index1(block1);
     658           1 :         index1.nHeight = 1;
     659           1 :         auto output = CreateValidBlock(wallet, sk, index1, block1, saplingTree);
     660             : 
     661             :         // Called to fetch anchor
     662           2 :         std::vector<SaplingOutPoint> saplingNotes {output};
     663           1 :         std::vector<Optional<SaplingWitness>> saplingWitnesses;
     664             : 
     665           1 :         anchors1 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     666             :     }
     667             : 
     668           1 :     {
     669             :         // Second transaction
     670           1 :         CWalletTx wtx = GetValidSaplingReceive(Params().GetConsensus(),
     671           1 :                 wallet, sk, 50, true);
     672             : 
     673           2 :         std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
     674           1 :         wallet.LoadToWallet(wtx);
     675             : 
     676           2 :         std::vector<Optional<SaplingWitness>> saplingWitnesses;
     677           1 :         GetWitnessesAndAnchors(wallet , saplingNotes, saplingWitnesses);
     678             : 
     679           2 :         BOOST_CHECK(!(bool) saplingWitnesses[0]);
     680             : 
     681             :         // Second block
     682           1 :         CBlock block2;
     683           1 :         block2.hashPrevBlock = block1.GetHash();
     684           1 :         block2.vtx.emplace_back(wtx.tx);
     685           2 :         CBlockIndex index2(block2);
     686           1 :         index2.nHeight = 2;
     687           2 :         SaplingMerkleTree saplingTree2 {saplingTree};
     688           1 :         wallet.IncrementNoteWitnesses(&index2, &block2, saplingTree2);
     689             : 
     690           1 :         auto anchors2 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     691           2 :         BOOST_CHECK((bool) saplingWitnesses[0]);
     692           2 :         BOOST_CHECK(anchors1 != anchors2);
     693             : 
     694             :         // Decrementing should give us the previous anchor
     695           1 :         wallet.DecrementNoteWitnesses(&index2);
     696           1 :         auto anchors3 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     697             : 
     698           2 :         BOOST_CHECK(!(bool) saplingWitnesses[0]);
     699             :         // Should not equal first anchor because none of these notes had witnesses
     700           2 :         BOOST_CHECK(anchors1 != anchors3);
     701             : 
     702             :         // Re-incrementing with the same block should give the same result
     703           1 :         wallet.IncrementNoteWitnesses(&index2, &block2, saplingTree);
     704           1 :         auto anchors4 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     705           2 :         BOOST_CHECK((bool) saplingWitnesses[0]);
     706           2 :         BOOST_CHECK(anchors2 == anchors4);
     707             : 
     708             :         // Incrementing with the same block again should not change the cache
     709           1 :         wallet.IncrementNoteWitnesses(&index2, &block2, saplingTree);
     710           2 :         std::vector<Optional<SaplingWitness>> saplingWitnesses5;
     711             : 
     712           1 :         auto anchors5 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses5);
     713           2 :         BOOST_CHECK(saplingWitnesses == saplingWitnesses5);
     714           2 :         BOOST_CHECK(anchors4 == anchors5);
     715             :     }
     716           1 : }
     717             : 
     718           2 : BOOST_AUTO_TEST_CASE(CachedWitnessesDecrementFirst)
     719             : {
     720           1 :     auto consensusParams = Params().GetConsensus();
     721           1 :     libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey();
     722           1 :     CWallet& wallet = m_wallet;
     723           1 :     {
     724           1 :         LOCK(wallet.cs_wallet);
     725           1 :         setupWallet(wallet);
     726           2 :         BOOST_CHECK(wallet.AddSaplingZKey(sk));
     727             :     }
     728             : 
     729           2 :     SaplingMerkleTree saplingTree;
     730           1 :     {
     731             :         // First block (case tested in _empty_chain)
     732           1 :         CBlock block1;
     733           2 :         CBlockIndex index1(block1);
     734           1 :         index1.nHeight = 1;
     735           1 :         CreateValidBlock(wallet, sk, index1, block1, saplingTree);
     736             :     }
     737             : 
     738           1 :     uint256 anchors2;
     739           2 :     CBlock block2;
     740           2 :     CBlockIndex index2(block2);
     741             : 
     742           1 :     {
     743             :         // Second block (case tested in _chain_tip)
     744           1 :         index2.nHeight = 2;
     745           1 :         auto output = CreateValidBlock(wallet, sk, index2, block2, saplingTree);
     746             : 
     747             :         // Called to fetch anchor
     748           1 :         std::vector<SaplingOutPoint> saplingNotes {output};
     749           1 :         std::vector<Optional<SaplingWitness>> saplingWitnesses;
     750           1 :         anchors2 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     751             :     }
     752             : 
     753           1 :     {
     754             :         // Third transaction - never mined
     755           1 :         CWalletTx wtx = GetValidSaplingReceive(Params().GetConsensus(),
     756           1 :                                                wallet, sk, 20, true);
     757           2 :         std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
     758           1 :         wallet.LoadToWallet(wtx);
     759             : 
     760           2 :         std::vector<Optional<SaplingWitness>> saplingWitnesses;
     761             : 
     762           1 :         auto anchors3 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     763             : 
     764           2 :         BOOST_CHECK(!(bool) saplingWitnesses[0]);
     765             : 
     766             :         // Decrementing (before the transaction has ever seen an increment)
     767             :         // should give us the previous anchor
     768           1 :         wallet.DecrementNoteWitnesses(&index2);
     769             : 
     770           1 :         auto anchors4 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     771             : 
     772           2 :         BOOST_CHECK(!(bool) saplingWitnesses[0]);
     773             :         // Should not equal second anchor because none of these notes had witnesses
     774           2 :         BOOST_CHECK(anchors2 != anchors4);
     775             : 
     776             :         // Re-incrementing with the same block should give the same result
     777           1 :         wallet.IncrementNoteWitnesses(&index2, &block2, saplingTree);
     778             : 
     779           1 :         auto anchors5 = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     780             : 
     781           2 :         BOOST_CHECK(!(bool) saplingWitnesses[0]);
     782           2 :         BOOST_CHECK(anchors3 == anchors5);
     783             :     }
     784           1 : }
     785             : 
     786           2 : BOOST_AUTO_TEST_CASE(CachedWitnessesCleanIndex)
     787             : {
     788           2 :     auto consensusParams = Params().GetConsensus();
     789             : 
     790           1 :     libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey();
     791           1 :     CWallet& wallet = m_wallet;
     792           1 :     {
     793           1 :         LOCK(wallet.cs_wallet);
     794           1 :         setupWallet(wallet);
     795           2 :         BOOST_CHECK(wallet.AddSaplingZKey(sk));
     796             :     }
     797             : 
     798           2 :     std::vector<CBlock> blocks;
     799           0 :     std::vector<CBlockIndex> indices;
     800           1 :     std::vector<SaplingOutPoint> saplingNotes;
     801           1 :     std::vector<uint256> saplingAnchors;
     802           2 :     SaplingMerkleTree saplingTree;
     803           2 :     SaplingMerkleTree saplingRiTree = saplingTree;
     804           2 :     std::vector<Optional<SaplingWitness>> saplingWitnesses;
     805             : 
     806             :     // Generate a chain
     807           1 :     size_t numBlocks = WITNESS_CACHE_SIZE + 10;
     808           1 :     blocks.resize(numBlocks);
     809           1 :     indices.resize(numBlocks);
     810         112 :     for (size_t i = 0; i < numBlocks; i++) {
     811         111 :         indices[i].nHeight = i;
     812         111 :         auto oldSaplingRoot = saplingTree.root();
     813         111 :         auto outpts = CreateValidBlock(wallet, sk, indices[i], blocks[i], saplingTree);
     814         222 :         BOOST_CHECK(oldSaplingRoot != saplingTree.root());
     815         111 :         saplingNotes.push_back(outpts);
     816             : 
     817         111 :         auto anchor = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     818        6327 :         for (size_t j = 0; j <= i; j++) {
     819       12432 :             BOOST_CHECK((bool) saplingWitnesses[j]);
     820             :         }
     821         111 :         saplingAnchors.push_back(anchor);
     822             :     }
     823             : 
     824             :     // Now pretend we are reindexing: the chain is cleared, and each block is
     825             :     // used to increment witnesses again.
     826         112 :     for (size_t i = 0; i < numBlocks; i++) {
     827         222 :         SaplingMerkleTree saplingRiPrevTree {saplingRiTree};
     828         111 :         wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), saplingRiTree);
     829             : 
     830         111 :         auto anchors = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     831       12432 :         for (size_t j = 0; j < numBlocks; j++) {
     832       24642 :             BOOST_CHECK((bool) saplingWitnesses[j]);
     833             :         }
     834             :         // Should equal final anchor because witness cache unaffected
     835         222 :         BOOST_CHECK(saplingAnchors.back() == anchors);
     836             : 
     837         111 :         if ((i == 5) || (i == 50)) {
     838             :             // Pretend a reorg happened that was recorded in the block files
     839           2 :             {
     840           2 :                 wallet.DecrementNoteWitnesses(&(indices[i]));
     841             : 
     842           2 :                 auto anchors = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     843         224 :                 for (size_t j = 0; j < numBlocks; j++) {
     844         444 :                     BOOST_CHECK((bool) saplingWitnesses[j]);
     845             :                 }
     846             :                 // Should equal final anchor because witness cache unaffected
     847           4 :                 BOOST_CHECK(saplingAnchors.back() == anchors);
     848             :             }
     849             : 
     850           2 :             {
     851           2 :                 wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), saplingRiPrevTree);
     852           2 :                 auto anchors = GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     853         224 :                 for (size_t j = 0; j < numBlocks; j++) {
     854         444 :                     BOOST_CHECK((bool) saplingWitnesses[j]);
     855             :                 }
     856             :                 // Should equal final anchor because witness cache unaffected
     857           4 :                 BOOST_CHECK(saplingAnchors.back() == anchors);
     858             :             }
     859             :         }
     860             :     }
     861           1 : }
     862             : 
     863           2 : BOOST_AUTO_TEST_CASE(ClearNoteWitnessCache)
     864             : {
     865           2 :     auto consensusParams = Params().GetConsensus();
     866             : 
     867           1 :     libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey();
     868           1 :     CWallet& wallet = m_wallet;
     869           1 :     {
     870           1 :         LOCK(wallet.cs_wallet);
     871           1 :         setupWallet(wallet);
     872           2 :         BOOST_CHECK(wallet.AddSaplingZKey(sk));
     873             :     }
     874             : 
     875           1 :     CWalletTx wtx = GetValidSaplingReceive(Params().GetConsensus(),
     876           1 :                                            wallet, sk, 10, true);
     877           1 :     auto hash = wtx.GetHash();
     878           2 :     auto saplingNotes = SetSaplingNoteData(wtx);
     879             : 
     880             :     // Pretend we mined the tx by adding a fake witness
     881           2 :     SaplingMerkleTree saplingTree;
     882           2 :     wtx.mapSaplingNoteData[saplingNotes[0]].witnesses.push_front(saplingTree.witness());
     883           1 :     wtx.mapSaplingNoteData[saplingNotes[0]].witnessHeight = 1;
     884           1 :     wallet.GetSaplingScriptPubKeyMan()->nWitnessCacheSize = 1;
     885             : 
     886           1 :     wallet.LoadToWallet(wtx);
     887             : 
     888             :     // SetSaplingNoteData() only created a single Sapling output
     889             :     // which is in the wallet, so we add a second SaplingOutPoint here to
     890             :     // exercise the "note not in wallet" case.
     891           1 :     saplingNotes.emplace_back(wtx.GetHash(), 1);
     892           1 :     BOOST_CHECK_EQUAL(saplingNotes.size(), 2);
     893             : 
     894           2 :     std::vector<Optional<SaplingWitness>> saplingWitnesses;
     895             : 
     896             :     // Before clearing, we should have a witness for one note
     897           1 :     GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     898           2 :     BOOST_CHECK((bool) saplingWitnesses[0]);
     899           2 :     BOOST_CHECK(!(bool) saplingWitnesses[1]);
     900           1 :     BOOST_CHECK_EQUAL(1, wallet.mapWallet.at(hash).mapSaplingNoteData[saplingNotes[0]].witnessHeight);
     901           1 :     BOOST_CHECK_EQUAL(1, wallet.GetSaplingScriptPubKeyMan()->nWitnessCacheSize);
     902             : 
     903             :     // After clearing, we should not have a witness for either note
     904           1 :     wallet.GetSaplingScriptPubKeyMan()->ClearNoteWitnessCache();
     905           1 :     GetWitnessesAndAnchors(wallet, saplingNotes, saplingWitnesses);
     906           2 :     BOOST_CHECK(!(bool) saplingWitnesses[0]);
     907           2 :     BOOST_CHECK(!(bool) saplingWitnesses[1]);
     908           1 :     BOOST_CHECK_EQUAL(-1, wallet.mapWallet.at(hash).mapSaplingNoteData[saplingNotes[0]].witnessHeight);
     909           1 :     BOOST_CHECK_EQUAL(0, wallet.GetSaplingScriptPubKeyMan()->nWitnessCacheSize);
     910           1 : }
     911             : 
     912           2 : BOOST_AUTO_TEST_CASE(UpdatedSaplingNoteData)
     913             : {
     914           1 :     auto consensusParams = Params().GetConsensus();
     915             : 
     916           1 :     CWallet& wallet = m_wallet;
     917             :     // Need to lock cs_main for now due the lock ordering. future: revamp all of this function to only lock where is needed.
     918           3 :     LOCK2(cs_main, wallet.cs_wallet);
     919           1 :     setupWallet(wallet);
     920             : 
     921           1 :     auto m = GetTestMasterSaplingSpendingKey();
     922             : 
     923             :     // Generate dummy Sapling address
     924           1 :     auto sk = m.Derive(0);
     925           1 :     auto expsk = sk.expsk;
     926           1 :     auto extfvk = sk.ToXFVK();
     927           1 :     auto pa = sk.DefaultAddress();
     928             : 
     929             :     // Generate dummy recipient Sapling address
     930           1 :     auto sk2 = m.Derive(1);
     931           1 :     auto extfvk2 = sk2.ToXFVK();
     932           1 :     auto pa2 = sk2.DefaultAddress();
     933             : 
     934           2 :     auto testNote = GetTestSaplingNote(pa, 50000000);
     935             : 
     936             :     // Generate transaction
     937           2 :     auto builder = TransactionBuilder(consensusParams);
     938           1 :     builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
     939           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pa2, 25000000, {});
     940           1 :     builder.SetFee(10000000);
     941           2 :     auto tx = builder.Build().GetTxOrThrow();
     942             : 
     943             :     // Wallet contains extfvk1 but not extfvk2
     944           2 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx)};
     945           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
     946           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
     947           2 :     BOOST_CHECK(!wallet.HaveSaplingSpendingKey(extfvk2));
     948             : 
     949             :     // Fake-mine the transaction
     950           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     951           1 :     CBlock block;
     952           1 :     block.vtx.emplace_back(wtx.tx);
     953           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     954           1 :     const auto& blockHash = block.GetHash();
     955           2 :     CBlockIndex fakeIndex {block};
     956           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
     957           1 :     fakeIndex.phashBlock = &((*mi).first);
     958           1 :     chainActive.SetTip(&fakeIndex);
     959           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
     960           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
     961             : 
     962             :     // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
     963           3 :     auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
     964           2 :     BOOST_CHECK(saplingNoteData.size() == 1); // wallet only has key for change output
     965           1 :     wtx.SetSaplingNoteData(saplingNoteData);
     966           1 :     wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
     967           1 :     wallet.LoadToWallet(wtx);
     968             : 
     969             :     // Simulate receiving new block and ChainTip signal
     970           1 :     wallet.IncrementNoteWitnesses(&fakeIndex, &block, testNote.tree);
     971           1 :     wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
     972             : 
     973             :     // Retrieve the updated wtx from wallet
     974           1 :     const uint256& hash = wtx.GetHash();
     975           1 :     wtx = wallet.mapWallet.at(hash);
     976             : 
     977             :     // Now lets add key extfvk2 so wallet can find the payment note sent to pa2
     978           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk2));
     979           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk2));
     980           2 :     CWalletTx wtx2 = wtx;
     981           3 :     auto saplingNoteData2 = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx2.tx).first;
     982           2 :     BOOST_CHECK(saplingNoteData2.size() == 2);
     983           1 :     wtx2.SetSaplingNoteData(saplingNoteData2);
     984             : 
     985             :     // The payment note has not been witnessed yet, so let's fake the witness.
     986           1 :     SaplingOutPoint sop0(wtx2.GetHash(), 0);
     987           1 :     SaplingOutPoint sop1(wtx2.GetHash(), 1);
     988           2 :     wtx2.mapSaplingNoteData[sop0].witnesses.push_front(testNote.tree.witness());
     989           1 :     wtx2.mapSaplingNoteData[sop0].witnessHeight = 0;
     990             : 
     991             :     // The txs are different as wtx is aware of just the change output,
     992             :     // whereas wtx2 is aware of both payment and change outputs.
     993           2 :     BOOST_CHECK(wtx.mapSaplingNoteData != wtx2.mapSaplingNoteData);
     994           1 :     BOOST_CHECK_EQUAL(1, wtx.mapSaplingNoteData.size());
     995           1 :     BOOST_CHECK_EQUAL(1, wtx.mapSaplingNoteData[sop1].witnesses.size());    // wtx has witness for change
     996             : 
     997           1 :     BOOST_CHECK_EQUAL(2, wtx2.mapSaplingNoteData.size());
     998           1 :     BOOST_CHECK_EQUAL(1, wtx2.mapSaplingNoteData[sop0].witnesses.size());    // wtx2 has fake witness for payment output
     999           1 :     BOOST_CHECK_EQUAL(0, wtx2.mapSaplingNoteData[sop1].witnesses.size());    // wtx2 never had incrementnotewitness called
    1000             : 
    1001             :     // After updating, they should be the same
    1002           2 :     BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->UpdatedNoteData(wtx2, wtx));
    1003             : 
    1004             :     // We can't do this:
    1005             :     // BOOST_CHECK_EQUAL(wtx.mapSaplingNoteData, wtx2.mapSaplingNoteData);
    1006             :     // because nullifiers (if part of == comparator) have not all been computed
    1007             :     // Also note that mapwallet[hash] is not updated with the updated wtx.
    1008             :     // wtx = wallet.mapWallet[hash];
    1009             : 
    1010           1 :     BOOST_CHECK_EQUAL(2, wtx.mapSaplingNoteData.size());
    1011           1 :     BOOST_CHECK_EQUAL(2, wtx2.mapSaplingNoteData.size());
    1012             :     // wtx copied over the fake witness from wtx2 for the payment output
    1013           2 :     BOOST_CHECK(wtx.mapSaplingNoteData[sop0].witnesses.front() == wtx2.mapSaplingNoteData[sop0].witnesses.front());
    1014             :     // wtx2 never had its change output witnessed even though it has been in wtx
    1015           1 :     BOOST_CHECK_EQUAL(0, wtx2.mapSaplingNoteData[sop1].witnesses.size());
    1016           2 :     BOOST_CHECK(wtx.mapSaplingNoteData[sop1].witnesses.front() == testNote.tree.witness());
    1017             : 
    1018             :     // Tear down
    1019           1 :     chainActive.SetTip(nullptr);
    1020           1 :     mapBlockIndex.erase(blockHash);
    1021           1 : }
    1022             : 
    1023           2 : BOOST_AUTO_TEST_CASE(MarkAffectedSaplingTransactionsDirty)
    1024             : {
    1025           1 :     auto consensusParams = Params().GetConsensus();
    1026             : 
    1027           1 :     CWallet& wallet = m_wallet;
    1028           3 :     LOCK2(cs_main, wallet.cs_wallet);
    1029           1 :     setupWallet(wallet);
    1030             : 
    1031             :     // Generate Sapling address
    1032           1 :     auto sk = GetTestMasterSaplingSpendingKey();
    1033           1 :     auto expsk = sk.expsk;
    1034           1 :     auto extfvk = sk.ToXFVK();
    1035           1 :     auto ivk = extfvk.fvk.in_viewing_key();
    1036           1 :     auto pk = sk.DefaultAddress();
    1037             : 
    1038           2 :     BOOST_CHECK(wallet.AddSaplingZKey(sk));
    1039           2 :     BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
    1040             : 
    1041             :     // Set up transparent address
    1042           2 :     CBasicKeyStore keystore;
    1043           2 :     CKey tsk = AddTestCKeyToKeyStore(keystore);
    1044           2 :     auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
    1045             : 
    1046             :     // Generate shielding tx from transparent to Sapling
    1047             :     // 0.5 t-PIV in, 0.4 z-PIV out, 0.1 t-PIV fee
    1048           2 :     auto builder = TransactionBuilder(consensusParams, &keystore);
    1049           1 :     builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000000);
    1050           1 :     builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 40000000, {});
    1051           1 :     builder.SetFee(10000000);
    1052           2 :     auto tx1 = builder.Build().GetTxOrThrow();
    1053             : 
    1054           1 :     BOOST_CHECK_EQUAL(tx1.vin.size(), 1);
    1055           1 :     BOOST_CHECK_EQUAL(tx1.vout.size(), 0);
    1056           1 :     BOOST_CHECK_EQUAL(tx1.sapData->vShieldedSpend.size(), 0);
    1057           1 :     BOOST_CHECK_EQUAL(tx1.sapData->vShieldedOutput.size(), 1);
    1058           1 :     BOOST_CHECK_EQUAL(tx1.sapData->valueBalance, -40000000);
    1059             : 
    1060           1 :     CWalletTx wtx {&wallet, MakeTransactionRef(tx1)};
    1061             : 
    1062             :     // Fake-mine the transaction
    1063           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
    1064           2 :     SaplingMerkleTree saplingTree;
    1065           2 :     CBlock block;
    1066           1 :     block.vtx.emplace_back(wtx.tx);
    1067           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
    1068           1 :     const auto& blockHash = block.GetHash();
    1069           2 :     CBlockIndex fakeIndex {block};
    1070           1 :     BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
    1071           1 :     fakeIndex.phashBlock = &((*mi).first);
    1072           1 :     chainActive.SetTip(&fakeIndex);
    1073           3 :     BOOST_CHECK(chainActive.Contains(&fakeIndex));
    1074           1 :     BOOST_CHECK_EQUAL(0, chainActive.Height());
    1075             : 
    1076             :     // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
    1077           3 :     auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
    1078           2 :     BOOST_CHECK(saplingNoteData.size() > 0);
    1079           1 :     wtx.SetSaplingNoteData(saplingNoteData);
    1080           1 :     wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
    1081           1 :     wallet.LoadToWallet(wtx);
    1082             : 
    1083             :     // Simulate receiving new block and ChainTip signal
    1084           1 :     wallet.IncrementNoteWitnesses(&fakeIndex, &block, saplingTree);
    1085           1 :     wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
    1086             : 
    1087             :     // Retrieve the updated wtx from wallet
    1088           1 :     const uint256& hash = wtx.GetHash();
    1089           1 :     wtx = wallet.mapWallet.at(hash);
    1090             : 
    1091             :     // Prepare to spend the note that was just created
    1092           1 :     auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
    1093           2 :             tx1.sapData->vShieldedOutput[0].encCiphertext, ivk, tx1.sapData->vShieldedOutput[0].ephemeralKey, tx1.sapData->vShieldedOutput[0].cmu);
    1094           2 :     BOOST_CHECK(static_cast<bool>(maybe_pt));
    1095           2 :     auto maybe_note = maybe_pt.get().note(ivk);
    1096           2 :     BOOST_CHECK(static_cast<bool>(maybe_note));
    1097           2 :     auto note = maybe_note.get();
    1098           1 :     auto anchor = saplingTree.root();
    1099           2 :     auto witness = saplingTree.witness();
    1100             : 
    1101             :     // Create a Sapling-only transaction
    1102             :     // 0.4 z-PIV in, 0.25 z-PIV out, 0.1 t-PIV fee, 0.05 z-PIV change
    1103           2 :     auto builder2 = TransactionBuilder(consensusParams);
    1104           1 :     builder2.AddSaplingSpend(expsk, note, anchor, witness);
    1105           1 :     builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 25000000, {});
    1106           1 :     builder2.SetFee(10000000);
    1107           2 :     auto tx2 = builder2.Build().GetTxOrThrow();
    1108             : 
    1109           1 :     BOOST_CHECK_EQUAL(tx2.vin.size(), 0);
    1110           1 :     BOOST_CHECK_EQUAL(tx2.vout.size(), 0);
    1111           1 :     BOOST_CHECK_EQUAL(tx2.sapData->vShieldedSpend.size(), 1);
    1112           1 :     BOOST_CHECK_EQUAL(tx2.sapData->vShieldedOutput.size(), 2);
    1113           1 :     BOOST_CHECK_EQUAL(tx2.sapData->valueBalance, 10000000);
    1114             : 
    1115           1 :     CWalletTx wtx2 {&wallet, MakeTransactionRef(tx2)};
    1116             : 
    1117           1 :     wallet.MarkAffectedTransactionsDirty(*wtx.tx);
    1118             : 
    1119             :     // After getting a cached value, the first tx should be clean
    1120           1 :     wallet.mapWallet.at(hash).GetDebit(ISMINE_SPENDABLE);
    1121           2 :     BOOST_CHECK(wallet.mapWallet.at(hash).IsAmountCached(CWalletTx::AmountType::DEBIT, ISMINE_SPENDABLE));
    1122             : 
    1123             :     // After adding the note spend, the first tx should be dirty
    1124           1 :     wallet.LoadToWallet(wtx2);
    1125           1 :     wallet.MarkAffectedTransactionsDirty(*wtx2.tx);
    1126           2 :     BOOST_CHECK(!wallet.mapWallet.at(hash).IsAmountCached(CWalletTx::AmountType::DEBIT, ISMINE_SPENDABLE));
    1127             : 
    1128             :     // Tear down
    1129           1 :     chainActive.SetTip(nullptr);
    1130           1 :     mapBlockIndex.erase(blockHash);
    1131           1 : }
    1132             : 
    1133           2 : BOOST_AUTO_TEST_CASE(GetNotes)
    1134             : {
    1135           1 :     auto consensusParams = Params().GetConsensus();
    1136             : 
    1137           1 :     CWallet& wallet = m_wallet;
    1138           1 :     libzcash::SaplingPaymentAddress pk;
    1139           1 :     uint256 blockHash;
    1140           2 :     std::vector<SaplingOutPoint> saplingOutpoints;
    1141           1 :     {
    1142           2 :         LOCK2(cs_main, wallet.cs_wallet);
    1143           1 :         setupWallet(wallet);
    1144             : 
    1145             :         // Generate Sapling address
    1146           1 :         auto sk = GetTestMasterSaplingSpendingKey();
    1147           1 :         auto extfvk = sk.ToXFVK();
    1148           1 :         pk = sk.DefaultAddress();
    1149             : 
    1150           2 :         BOOST_CHECK(wallet.AddSaplingZKey(sk));
    1151           2 :         BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk));
    1152             : 
    1153             :         // Set up transparent address
    1154           2 :         CBasicKeyStore keystore;
    1155           2 :         CKey tsk = AddTestCKeyToKeyStore(keystore);
    1156           2 :         auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
    1157             : 
    1158             :         // Generate shielding tx from transparent to Sapling (five 1 PIV notes)
    1159           2 :         auto builder = TransactionBuilder(consensusParams, &keystore);
    1160           1 :         builder.AddTransparentInput(COutPoint(), scriptPubKey, 510000000);
    1161           6 :         for (int i=0; i<5; i++) builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 100000000, {});
    1162           1 :         builder.SetFee(10000000);
    1163           2 :         auto tx1 = builder.Build().GetTxOrThrow();
    1164             : 
    1165           1 :         CWalletTx wtx {&wallet, MakeTransactionRef(tx1)};
    1166             : 
    1167             :         // Fake-mine the transaction
    1168           1 :         BOOST_CHECK_EQUAL(0, chainActive.Height());
    1169           2 :         SaplingMerkleTree saplingTree;
    1170           2 :         CBlock block;
    1171           1 :         block.vtx.emplace_back(wtx.tx);
    1172           1 :         block.hashMerkleRoot = BlockMerkleRoot(block);
    1173           1 :         blockHash = block.GetHash();
    1174           2 :         CBlockIndex fakeIndex {block};
    1175           1 :         BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first;
    1176           1 :         fakeIndex.phashBlock = &((*mi).first);
    1177           1 :         chainActive.SetTip(&fakeIndex);
    1178           3 :         BOOST_CHECK(chainActive.Contains(&fakeIndex));
    1179           1 :         BOOST_CHECK_EQUAL(0, chainActive.Height());
    1180             : 
    1181             :         // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
    1182           3 :         auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first;
    1183           2 :         BOOST_CHECK(saplingNoteData.size() > 0);
    1184           1 :         wtx.SetSaplingNoteData(saplingNoteData);
    1185           1 :         wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0);
    1186           1 :         wallet.LoadToWallet(wtx);
    1187             : 
    1188             :         // Simulate receiving new block and ChainTip signal
    1189           1 :         wallet.IncrementNoteWitnesses(&fakeIndex, &block, saplingTree);
    1190           1 :         wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block);
    1191           1 :         wallet.SetLastBlockProcessed(mi->second);
    1192             : 
    1193           1 :         const uint256& txid = wtx.GetHash();
    1194           6 :         for (int i=0; i<5; i++) saplingOutpoints.emplace_back(txid, i);
    1195             :     }
    1196             : 
    1197             :     // Check GetFilteredNotes
    1198           2 :     std::vector<SaplingNoteEntry> entries;
    1199           2 :     Optional<libzcash::SaplingPaymentAddress> address = pk;
    1200           1 :     wallet.GetSaplingScriptPubKeyMan()->GetFilteredNotes(entries, address, 0, true, false);
    1201           6 :     for (int i=0; i<5; i++) {
    1202          15 :         BOOST_CHECK(entries[i].op == saplingOutpoints[i]);
    1203          10 :         BOOST_CHECK(entries[i].address == pk);
    1204           5 :         BOOST_CHECK_EQUAL(entries[i].confirmations, 1);
    1205             :     }
    1206             : 
    1207             :     // Check GetNotes
    1208           2 :     std::vector<SaplingNoteEntry> entries2;
    1209           1 :     wallet.GetSaplingScriptPubKeyMan()->GetNotes(saplingOutpoints, entries2);
    1210           6 :     for (int i=0; i<5; i++) {
    1211          15 :         BOOST_CHECK(entries2[i].op == entries[i].op);
    1212          10 :         BOOST_CHECK(entries2[i].address == entries[i].address);
    1213          15 :         BOOST_CHECK(entries2[i].note.d == entries[i].note.d);
    1214           5 :         BOOST_CHECK_EQUAL(entries2[i].note.pk_d, entries[i].note.pk_d);
    1215           5 :         BOOST_CHECK_EQUAL(entries2[i].note.r, entries[i].note.r);
    1216          15 :         BOOST_CHECK(entries2[i].memo == entries[i].memo);
    1217           5 :         BOOST_CHECK_EQUAL(entries2[i].confirmations, entries[i].confirmations);
    1218             :     }
    1219             : 
    1220             :     // Tear down
    1221           2 :     LOCK(cs_main);
    1222           1 :     chainActive.SetTip(nullptr);
    1223           1 :     mapBlockIndex.erase(blockHash);
    1224           1 : }
    1225             : 
    1226             : // TODO: Back port WriteWitnessCache & SetBestChainIgnoresTxsWithoutShieldedData test cases.
    1227             : 
    1228             : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.14