1 #include <gmock/gmock.h>
2 #include <gtest/gtest.h>
6 #include "chainparams.h"
9 #include "primitives/block.h"
11 #include "transaction_builder.h"
13 #include "wallet/wallet.h"
14 #include "zcash/JoinSplit.hpp"
15 #include "zcash/Note.hpp"
16 #include "zcash/NoteEncryption.hpp"
18 #include <boost/filesystem.hpp>
20 using ::testing::Return;
22 extern ZCJoinSplit* params;
24 ACTION(ThrowLogicError) {
25 throw std::logic_error("Boom");
28 static const std::string tSecretRegtest = "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN";
32 MOCK_METHOD0(TxnBegin, bool());
33 MOCK_METHOD0(TxnCommit, bool());
34 MOCK_METHOD0(TxnAbort, bool());
36 MOCK_METHOD2(WriteTx, bool(uint256 hash, const CWalletTx& wtx));
37 MOCK_METHOD1(WriteWitnessCacheSize, bool(int64_t nWitnessCacheSize));
38 MOCK_METHOD1(WriteBestBlock, bool(const CBlockLocator& loc));
41 template void CWallet::SetBestChainINTERNAL<MockWalletDB>(
42 MockWalletDB& walletdb, const CBlockLocator& loc);
44 class TestWallet : public CWallet {
46 TestWallet() : CWallet() { }
48 bool EncryptKeys(CKeyingMaterial& vMasterKeyIn) {
49 return CCryptoKeyStore::EncryptKeys(vMasterKeyIn);
52 bool Unlock(const CKeyingMaterial& vMasterKeyIn) {
53 return CCryptoKeyStore::Unlock(vMasterKeyIn);
56 void IncrementNoteWitnesses(const CBlockIndex* pindex,
58 SproutMerkleTree& sproutTree,
59 SaplingMerkleTree& saplingTree) {
60 CWallet::IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree);
62 void DecrementNoteWitnesses(const CBlockIndex* pindex) {
63 CWallet::DecrementNoteWitnesses(pindex);
65 void SetBestChain(MockWalletDB& walletdb, const CBlockLocator& loc) {
66 CWallet::SetBestChainINTERNAL(walletdb, loc);
68 bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) {
69 return CWallet::UpdatedNoteData(wtxIn, wtx);
71 void MarkAffectedTransactionsDirty(const CTransaction& tx) {
72 CWallet::MarkAffectedTransactionsDirty(tx);
76 CWalletTx GetValidReceive(const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, int32_t version = 2) {
77 return GetValidReceive(*params, sk, value, randomInputs, version);
80 libzcash::SproutNote GetNote(const libzcash::SproutSpendingKey& sk,
81 const CTransaction& tx, size_t js, size_t n) {
82 return GetNote(*params, sk, tx, js, n);
85 CWalletTx GetValidSpend(const libzcash::SproutSpendingKey& sk,
86 const libzcash::SproutNote& note, CAmount value) {
87 return GetValidSpend(*params, sk, note, value);
90 std::vector<SaplingOutPoint> SetSaplingNoteData(CWalletTx& wtx) {
91 mapSaplingNoteData_t saplingNoteData;
92 SaplingOutPoint saplingOutPoint = {wtx.GetHash(), 0};
93 SaplingNoteData saplingNd;
94 saplingNoteData[saplingOutPoint] = saplingNd;
95 wtx.SetSaplingNoteData(saplingNoteData);
96 std::vector<SaplingOutPoint> saplingNotes {saplingOutPoint};
100 std::pair<JSOutPoint, SaplingOutPoint> CreateValidBlock(TestWallet& wallet,
101 const libzcash::SproutSpendingKey& sk,
102 const CBlockIndex& index,
104 SproutMerkleTree& sproutTree,
105 SaplingMerkleTree& saplingTree) {
106 auto wtx = GetValidReceive(sk, 50, true, 4);
107 auto note = GetNote(sk, wtx, 0, 1);
108 auto nullifier = note.nullifier(sk);
110 mapSproutNoteData_t noteData;
111 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
112 SproutNoteData nd {sk.address(), nullifier};
113 noteData[jsoutpt] = nd;
114 wtx.SetSproutNoteData(noteData);
115 auto saplingNotes = SetSaplingNoteData(wtx);
116 wallet.AddToWallet(wtx, true, NULL);
118 block.vtx.push_back(wtx);
119 wallet.IncrementNoteWitnesses(&index, &block, sproutTree, saplingTree);
121 return std::make_pair(jsoutpt, saplingNotes[0]);
124 std::pair<uint256, uint256> GetWitnessesAndAnchors(TestWallet& wallet,
125 std::vector<JSOutPoint>& sproutNotes,
126 std::vector<SaplingOutPoint>& saplingNotes,
127 std::vector<boost::optional<SproutWitness>>& sproutWitnesses,
128 std::vector<boost::optional<SaplingWitness>>& saplingWitnesses) {
129 sproutWitnesses.clear();
130 saplingWitnesses.clear();
131 uint256 sproutAnchor;
132 uint256 saplingAnchor;
133 wallet.GetSproutNoteWitnesses(sproutNotes, sproutWitnesses, sproutAnchor);
134 wallet.GetSaplingNoteWitnesses(saplingNotes, saplingWitnesses, saplingAnchor);
135 return std::make_pair(sproutAnchor, saplingAnchor);
138 TEST(WalletTests, SetupDatadirLocationRunAsFirstTest) {
139 // Get temporary and unique path for file.
140 boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
141 boost::filesystem::create_directories(pathTemp);
142 mapArgs["-datadir"] = pathTemp.string();
145 TEST(WalletTests, SproutNoteDataSerialisation) {
146 auto sk = libzcash::SproutSpendingKey::random();
147 auto wtx = GetValidReceive(sk, 10, true);
148 auto note = GetNote(sk, wtx, 0, 1);
149 auto nullifier = note.nullifier(sk);
151 mapSproutNoteData_t noteData;
152 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
153 SproutNoteData nd {sk.address(), nullifier};
154 SproutMerkleTree tree;
155 nd.witnesses.push_front(tree.witness());
156 noteData[jsoutpt] = nd;
158 CDataStream ss(SER_DISK, CLIENT_VERSION);
161 mapSproutNoteData_t noteData2;
164 EXPECT_EQ(noteData, noteData2);
165 EXPECT_EQ(noteData[jsoutpt].witnesses, noteData2[jsoutpt].witnesses);
169 TEST(WalletTests, FindUnspentSproutNotes) {
170 SelectParams(CBaseChainParams::TESTNET);
172 auto sk = libzcash::SproutSpendingKey::random();
173 wallet.AddSproutSpendingKey(sk);
175 auto wtx = GetValidReceive(sk, 10, true);
176 auto note = GetNote(sk, wtx, 0, 1);
177 auto nullifier = note.nullifier(sk);
179 mapSproutNoteData_t noteData;
180 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
181 SproutNoteData nd {sk.address(), nullifier};
182 noteData[jsoutpt] = nd;
184 wtx.SetSproutNoteData(noteData);
185 wallet.AddToWallet(wtx, true, NULL);
186 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
188 // We currently have an unspent and unconfirmed note in the wallet (depth of -1)
189 std::vector<CSproutNotePlaintextEntry> entries;
190 wallet.GetFilteredNotes(entries, "", 0);
191 EXPECT_EQ(0, entries.size());
193 wallet.GetFilteredNotes(entries, "", -1);
194 EXPECT_EQ(1, entries.size());
197 // Fake-mine the transaction
198 EXPECT_EQ(-1, chainActive.Height());
200 block.vtx.push_back(wtx);
201 block.hashMerkleRoot = block.BuildMerkleTree();
202 auto blockHash = block.GetHash();
203 CBlockIndex fakeIndex {block};
204 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
205 chainActive.SetTip(&fakeIndex);
206 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
207 EXPECT_EQ(0, chainActive.Height());
209 wtx.SetMerkleBranch(block);
210 wallet.AddToWallet(wtx, true, NULL);
211 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
214 // We now have an unspent and confirmed note in the wallet (depth of 1)
215 wallet.GetFilteredNotes(entries, "", 0);
216 EXPECT_EQ(1, entries.size());
218 wallet.GetFilteredNotes(entries, "", 1);
219 EXPECT_EQ(1, entries.size());
221 wallet.GetFilteredNotes(entries, "", 2);
222 EXPECT_EQ(0, entries.size());
226 // Let's spend the note.
227 auto wtx2 = GetValidSpend(sk, note, 5);
228 wallet.AddToWallet(wtx2, true, NULL);
229 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
231 // Fake-mine a spend transaction
232 EXPECT_EQ(0, chainActive.Height());
234 block2.vtx.push_back(wtx2);
235 block2.hashMerkleRoot = block2.BuildMerkleTree();
236 block2.hashPrevBlock = blockHash;
237 auto blockHash2 = block2.GetHash();
238 CBlockIndex fakeIndex2 {block2};
239 mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2));
240 fakeIndex2.nHeight = 1;
241 chainActive.SetTip(&fakeIndex2);
242 EXPECT_TRUE(chainActive.Contains(&fakeIndex2));
243 EXPECT_EQ(1, chainActive.Height());
245 wtx2.SetMerkleBranch(block2);
246 wallet.AddToWallet(wtx2, true, NULL);
247 EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
249 // The note has been spent. By default, GetFilteredNotes() ignores spent notes.
250 wallet.GetFilteredNotes(entries, "", 0);
251 EXPECT_EQ(0, entries.size());
253 // Let's include spent notes to retrieve it.
254 wallet.GetFilteredNotes(entries, "", 0, false);
255 EXPECT_EQ(1, entries.size());
257 // The spent note has two confirmations.
258 wallet.GetFilteredNotes(entries, "", 2, false);
259 EXPECT_EQ(1, entries.size());
261 // It does not have 3 confirmations.
262 wallet.GetFilteredNotes(entries, "", 3, false);
263 EXPECT_EQ(0, entries.size());
267 // Let's receive a new note
270 auto wtx = GetValidReceive(sk, 20, true);
271 auto note = GetNote(sk, wtx, 0, 1);
272 auto nullifier = note.nullifier(sk);
274 mapSproutNoteData_t noteData;
275 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
276 SproutNoteData nd {sk.address(), nullifier};
277 noteData[jsoutpt] = nd;
279 wtx.SetSproutNoteData(noteData);
280 wallet.AddToWallet(wtx, true, NULL);
281 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
286 // Fake-mine the new transaction
287 EXPECT_EQ(1, chainActive.Height());
289 block3.vtx.push_back(wtx3);
290 block3.hashMerkleRoot = block3.BuildMerkleTree();
291 block3.hashPrevBlock = blockHash2;
292 auto blockHash3 = block3.GetHash();
293 CBlockIndex fakeIndex3 {block3};
294 mapBlockIndex.insert(std::make_pair(blockHash3, &fakeIndex3));
295 fakeIndex3.nHeight = 2;
296 chainActive.SetTip(&fakeIndex3);
297 EXPECT_TRUE(chainActive.Contains(&fakeIndex3));
298 EXPECT_EQ(2, chainActive.Height());
300 wtx3.SetMerkleBranch(block3);
301 wallet.AddToWallet(wtx3, true, NULL);
303 // We now have an unspent note which has one confirmation, in addition to our spent note.
304 wallet.GetFilteredNotes(entries, "", 1);
305 EXPECT_EQ(1, entries.size());
307 // Let's return the spent note too.
308 wallet.GetFilteredNotes(entries, "", 1, false);
309 EXPECT_EQ(2, entries.size());
311 // Increasing number of confirmations will exclude our new unspent note.
312 wallet.GetFilteredNotes(entries, "", 2, false);
313 EXPECT_EQ(1, entries.size());
315 // If we also ignore spent notes at this depth, we won't find any notes.
316 wallet.GetFilteredNotes(entries, "", 2, true);
317 EXPECT_EQ(0, entries.size());
321 chainActive.SetTip(NULL);
322 mapBlockIndex.erase(blockHash);
323 mapBlockIndex.erase(blockHash2);
324 mapBlockIndex.erase(blockHash3);
328 TEST(WalletTests, SetSproutNoteAddrsInCWalletTx) {
329 auto sk = libzcash::SproutSpendingKey::random();
330 auto wtx = GetValidReceive(sk, 10, true);
331 auto note = GetNote(sk, wtx, 0, 1);
332 auto nullifier = note.nullifier(sk);
333 EXPECT_EQ(0, wtx.mapSproutNoteData.size());
335 mapSproutNoteData_t noteData;
336 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
337 SproutNoteData nd {sk.address(), nullifier};
338 noteData[jsoutpt] = nd;
340 wtx.SetSproutNoteData(noteData);
341 EXPECT_EQ(noteData, wtx.mapSproutNoteData);
344 TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
345 SelectParams(CBaseChainParams::REGTEST);
346 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
347 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
348 auto consensusParams = Params().GetConsensus();
352 auto sk = libzcash::SaplingSpendingKey::random();
353 auto expsk = sk.expanded_spending_key();
354 auto fvk = sk.full_viewing_key();
355 auto pk = sk.default_address();
356 auto ivk = fvk.in_viewing_key();
358 libzcash::SaplingNote note(pk, 50000);
359 auto cm = note.cm().get();
360 SaplingMerkleTree tree;
362 auto anchor = tree.root();
363 auto witness = tree.witness();
365 auto nf = note.nullifier(fvk, witness.position());
367 uint256 nullifier = nf.get();
369 auto builder = TransactionBuilder(consensusParams, 1);
370 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
371 builder.AddSaplingOutput(fvk, pk, 50000, {});
373 auto maybe_tx = builder.Build();
374 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
375 auto tx = maybe_tx.get();
377 CWalletTx wtx {&wallet, tx};
379 EXPECT_EQ(0, wtx.mapSaplingNoteData.size());
380 mapSaplingNoteData_t noteData;
382 SaplingOutPoint op {wtx.GetHash(), 0};
384 nd.nullifier = nullifier;
386 nd.witnesses.push_front(witness);
387 nd.witnessHeight = 123;
388 noteData.insert(std::make_pair(op, nd));
390 wtx.SetSaplingNoteData(noteData);
391 EXPECT_EQ(noteData, wtx.mapSaplingNoteData);
393 // Test individual fields in case equality operator is defined/changed.
394 EXPECT_EQ(ivk, wtx.mapSaplingNoteData[op].ivk);
395 EXPECT_EQ(nullifier, wtx.mapSaplingNoteData[op].nullifier);
396 EXPECT_EQ(nd.witnessHeight, wtx.mapSaplingNoteData[op].witnessHeight);
397 EXPECT_TRUE(witness == wtx.mapSaplingNoteData[op].witnesses.front());
400 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
401 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
404 TEST(WalletTests, SetSproutInvalidNoteAddrsInCWalletTx) {
406 EXPECT_EQ(0, wtx.mapSproutNoteData.size());
408 mapSproutNoteData_t noteData;
409 auto sk = libzcash::SproutSpendingKey::random();
410 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
411 SproutNoteData nd {sk.address(), uint256()};
412 noteData[jsoutpt] = nd;
414 EXPECT_THROW(wtx.SetSproutNoteData(noteData), std::logic_error);
417 // The following test is the same as SetInvalidSaplingNoteDataInCWalletTx
418 // TEST(WalletTests, SetSaplingInvalidNoteAddrsInCWalletTx)
420 // Cannot add note data for an index which does not exist in tx.vShieldedOutput
421 TEST(WalletTests, SetInvalidSaplingNoteDataInCWalletTx) {
423 EXPECT_EQ(0, wtx.mapSaplingNoteData.size());
425 mapSaplingNoteData_t noteData;
426 SaplingOutPoint op {uint256(), 1};
428 noteData.insert(std::make_pair(op, nd));
430 EXPECT_THROW(wtx.SetSaplingNoteData(noteData), std::logic_error);
433 TEST(WalletTests, GetSproutNoteNullifier) {
436 auto sk = libzcash::SproutSpendingKey::random();
437 auto address = sk.address();
438 auto dec = ZCNoteDecryption(sk.receiving_key());
440 auto wtx = GetValidReceive(sk, 10, true);
441 auto note = GetNote(sk, wtx, 0, 1);
442 auto nullifier = note.nullifier(sk);
444 auto hSig = wtx.vjoinsplit[0].h_sig(
445 *params, wtx.joinSplitPubKey);
447 auto ret = wallet.GetSproutNoteNullifier(
452 EXPECT_NE(nullifier, ret);
454 wallet.AddSproutSpendingKey(sk);
456 ret = wallet.GetSproutNoteNullifier(
461 EXPECT_EQ(nullifier, ret);
464 TEST(WalletTests, FindMySaplingNotes) {
465 SelectParams(CBaseChainParams::REGTEST);
466 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
467 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
468 auto consensusParams = Params().GetConsensus();
472 // Generate dummy Sapling address
473 auto sk = libzcash::SaplingSpendingKey::random();
474 auto expsk = sk.expanded_spending_key();
475 auto fvk = sk.full_viewing_key();
476 auto pk = sk.default_address();
478 // Generate dummy Sapling note
479 libzcash::SaplingNote note(pk, 50000);
480 auto cm = note.cm().get();
481 SaplingMerkleTree tree;
483 auto anchor = tree.root();
484 auto witness = tree.witness();
486 // Generate transaction
487 auto builder = TransactionBuilder(consensusParams, 1);
488 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
489 builder.AddSaplingOutput(fvk, pk, 25000, {});
490 auto maybe_tx = builder.Build();
491 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
492 auto tx = maybe_tx.get();
494 // No Sapling notes can be found in tx which belong to the wallet
495 CWalletTx wtx {&wallet, tx};
496 ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk));
497 auto noteMap = wallet.FindMySaplingNotes(wtx);
498 EXPECT_EQ(0, noteMap.size());
500 // Add spending key to wallet, so Sapling notes can be found
501 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
502 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
503 noteMap = wallet.FindMySaplingNotes(wtx);
504 EXPECT_EQ(2, noteMap.size());
507 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
508 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
511 TEST(WalletTests, FindMySproutNotes) {
514 auto sk = libzcash::SproutSpendingKey::random();
515 auto sk2 = libzcash::SproutSpendingKey::random();
516 wallet.AddSproutSpendingKey(sk2);
518 auto wtx = GetValidReceive(sk, 10, true);
519 auto note = GetNote(sk, wtx, 0, 1);
520 auto nullifier = note.nullifier(sk);
522 auto noteMap = wallet.FindMySproutNotes(wtx);
523 EXPECT_EQ(0, noteMap.size());
525 wallet.AddSproutSpendingKey(sk);
527 noteMap = wallet.FindMySproutNotes(wtx);
528 EXPECT_EQ(2, noteMap.size());
530 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
531 SproutNoteData nd {sk.address(), nullifier};
532 EXPECT_EQ(1, noteMap.count(jsoutpt));
533 EXPECT_EQ(nd, noteMap[jsoutpt]);
536 TEST(WalletTests, FindMySproutNotesInEncryptedWallet) {
538 uint256 r {GetRandHash()};
539 CKeyingMaterial vMasterKey (r.begin(), r.end());
541 auto sk = libzcash::SproutSpendingKey::random();
542 wallet.AddSproutSpendingKey(sk);
544 ASSERT_TRUE(wallet.EncryptKeys(vMasterKey));
546 auto wtx = GetValidReceive(sk, 10, true);
547 auto note = GetNote(sk, wtx, 0, 1);
548 auto nullifier = note.nullifier(sk);
550 auto noteMap = wallet.FindMySproutNotes(wtx);
551 EXPECT_EQ(2, noteMap.size());
553 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
554 SproutNoteData nd {sk.address(), nullifier};
555 EXPECT_EQ(1, noteMap.count(jsoutpt));
556 EXPECT_NE(nd, noteMap[jsoutpt]);
558 ASSERT_TRUE(wallet.Unlock(vMasterKey));
560 noteMap = wallet.FindMySproutNotes(wtx);
561 EXPECT_EQ(2, noteMap.size());
562 EXPECT_EQ(1, noteMap.count(jsoutpt));
563 EXPECT_EQ(nd, noteMap[jsoutpt]);
566 TEST(WalletTests, GetConflictedSproutNotes) {
569 auto sk = libzcash::SproutSpendingKey::random();
570 wallet.AddSproutSpendingKey(sk);
572 auto wtx = GetValidReceive(sk, 10, true);
573 auto note = GetNote(sk, wtx, 0, 1);
574 auto nullifier = note.nullifier(sk);
576 auto wtx2 = GetValidSpend(sk, note, 5);
577 auto wtx3 = GetValidSpend(sk, note, 10);
578 auto hash2 = wtx2.GetHash();
579 auto hash3 = wtx3.GetHash();
581 // No conflicts for no spends
582 EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
583 wallet.AddToWallet(wtx, true, NULL);
584 EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
586 // No conflicts for one spend
587 wallet.AddToWallet(wtx2, true, NULL);
588 EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
590 // Conflicts for two spends
591 wallet.AddToWallet(wtx3, true, NULL);
592 auto c3 = wallet.GetConflicts(hash2);
593 EXPECT_EQ(2, c3.size());
594 EXPECT_EQ(std::set<uint256>({hash2, hash3}), c3);
597 // Generate note A and spend to create note B, from which we spend to create two conflicting transactions
598 TEST(WalletTests, GetConflictedSaplingNotes) {
599 SelectParams(CBaseChainParams::REGTEST);
600 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
601 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
602 auto consensusParams = Params().GetConsensus();
606 // Generate Sapling address
607 auto sk = libzcash::SaplingSpendingKey::random();
608 auto expsk = sk.expanded_spending_key();
609 auto fvk = sk.full_viewing_key();
610 auto ivk = fvk.in_viewing_key();
611 auto pk = sk.default_address();
613 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
614 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
617 libzcash::SaplingNote note(pk, 50000);
618 auto cm = note.cm().get();
619 SaplingMerkleTree saplingTree;
620 saplingTree.append(cm);
621 auto anchor = saplingTree.root();
622 auto witness = saplingTree.witness();
624 // Generate tx to create output note B
625 auto builder = TransactionBuilder(consensusParams, 1);
626 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
627 builder.AddSaplingOutput(fvk, pk, 35000, {});
628 auto maybe_tx = builder.Build();
629 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
630 auto tx = maybe_tx.get();
631 CWalletTx wtx {&wallet, tx};
633 // Fake-mine the transaction
634 EXPECT_EQ(-1, chainActive.Height());
635 SproutMerkleTree sproutTree;
637 block.vtx.push_back(wtx);
638 block.hashMerkleRoot = block.BuildMerkleTree();
639 auto blockHash = block.GetHash();
640 CBlockIndex fakeIndex {block};
641 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
642 chainActive.SetTip(&fakeIndex);
643 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
644 EXPECT_EQ(0, chainActive.Height());
646 // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
647 auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
648 ASSERT_TRUE(saplingNoteData.size() > 0);
649 wtx.SetSaplingNoteData(saplingNoteData);
650 wtx.SetMerkleBranch(block);
651 wallet.AddToWallet(wtx, true, NULL);
653 // Simulate receiving new block and ChainTip signal
654 wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
655 wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
657 // Retrieve the updated wtx from wallet
658 uint256 hash = wtx.GetHash();
659 wtx = wallet.mapWallet[hash];
661 // Decrypt output note B
662 auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
663 wtx.vShieldedOutput[0].encCiphertext,
665 wtx.vShieldedOutput[0].ephemeralKey,
666 wtx.vShieldedOutput[0].cm);
667 ASSERT_EQ(static_cast<bool>(maybe_pt), true);
668 auto maybe_note = maybe_pt.get().note(ivk);
669 ASSERT_EQ(static_cast<bool>(maybe_note), true);
670 auto note2 = maybe_note.get();
672 SaplingOutPoint sop0(wtx.GetHash(), 0);
673 auto spend_note_witness = wtx.mapSaplingNoteData[sop0].witnesses.front();
674 auto maybe_nf = note2.nullifier(fvk, spend_note_witness.position());
675 ASSERT_EQ(static_cast<bool>(maybe_nf), true);
676 auto nullifier2 = maybe_nf.get();
678 anchor = saplingTree.root();
680 // Create transaction to spend note B
681 auto builder2 = TransactionBuilder(consensusParams, 2);
682 ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
683 builder2.AddSaplingOutput(fvk, pk, 20000, {});
684 auto maybe_tx2 = builder2.Build();
685 ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
686 auto tx2 = maybe_tx2.get();
688 // Create conflicting transaction which also spends note B
689 auto builder3 = TransactionBuilder(consensusParams, 2);
690 ASSERT_TRUE(builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
691 builder3.AddSaplingOutput(fvk, pk, 19999, {});
692 auto maybe_tx3 = builder3.Build();
693 ASSERT_EQ(static_cast<bool>(maybe_tx3), true);
694 auto tx3 = maybe_tx3.get();
696 CWalletTx wtx2 {&wallet, tx2};
697 CWalletTx wtx3 {&wallet, tx3};
699 auto hash2 = wtx2.GetHash();
700 auto hash3 = wtx3.GetHash();
702 // No conflicts for no spends (wtx is currently the only transaction in the wallet)
703 EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
704 EXPECT_EQ(0, wallet.GetConflicts(hash3).size());
706 // No conflicts for one spend
707 wallet.AddToWallet(wtx2, true, NULL);
708 EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
710 // Conflicts for two spends
711 wallet.AddToWallet(wtx3, true, NULL);
712 auto c3 = wallet.GetConflicts(hash2);
713 EXPECT_EQ(2, c3.size());
714 EXPECT_EQ(std::set<uint256>({hash2, hash3}), c3);
717 chainActive.SetTip(NULL);
718 mapBlockIndex.erase(blockHash);
721 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
722 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
725 TEST(WalletTests, SproutNullifierIsSpent) {
728 auto sk = libzcash::SproutSpendingKey::random();
729 wallet.AddSproutSpendingKey(sk);
731 auto wtx = GetValidReceive(sk, 10, true);
732 auto note = GetNote(sk, wtx, 0, 1);
733 auto nullifier = note.nullifier(sk);
735 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
737 wallet.AddToWallet(wtx, true, NULL);
738 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
740 auto wtx2 = GetValidSpend(sk, note, 5);
741 wallet.AddToWallet(wtx2, true, NULL);
742 EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
744 // Fake-mine the transaction
745 EXPECT_EQ(-1, chainActive.Height());
747 block.vtx.push_back(wtx2);
748 block.hashMerkleRoot = block.BuildMerkleTree();
749 auto blockHash = block.GetHash();
750 CBlockIndex fakeIndex {block};
751 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
752 chainActive.SetTip(&fakeIndex);
753 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
754 EXPECT_EQ(0, chainActive.Height());
756 wtx2.SetMerkleBranch(block);
757 wallet.AddToWallet(wtx2, true, NULL);
758 EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
761 chainActive.SetTip(NULL);
762 mapBlockIndex.erase(blockHash);
765 TEST(WalletTests, SaplingNullifierIsSpent) {
766 SelectParams(CBaseChainParams::REGTEST);
767 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
768 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
769 auto consensusParams = Params().GetConsensus();
773 // Generate dummy Sapling address
774 auto sk = libzcash::SaplingSpendingKey::random();
775 auto expsk = sk.expanded_spending_key();
776 auto fvk = sk.full_viewing_key();
777 auto pk = sk.default_address();
779 // Generate dummy Sapling note
780 libzcash::SaplingNote note(pk, 50000);
781 auto cm = note.cm().get();
782 SaplingMerkleTree tree;
784 auto anchor = tree.root();
785 auto witness = tree.witness();
787 // Generate transaction
788 auto builder = TransactionBuilder(consensusParams, 1);
789 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
790 builder.AddSaplingOutput(fvk, pk, 25000, {});
791 auto maybe_tx = builder.Build();
792 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
793 auto tx = maybe_tx.get();
795 CWalletTx wtx {&wallet, tx};
796 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
797 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
799 // Manually compute the nullifier based on the known position
800 auto nf = note.nullifier(fvk, witness.position());
802 uint256 nullifier = nf.get();
804 // Verify note has not been spent
805 EXPECT_FALSE(wallet.IsSaplingSpent(nullifier));
807 // Fake-mine the transaction
808 EXPECT_EQ(-1, chainActive.Height());
810 block.vtx.push_back(wtx);
811 block.hashMerkleRoot = block.BuildMerkleTree();
812 auto blockHash = block.GetHash();
813 CBlockIndex fakeIndex {block};
814 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
815 chainActive.SetTip(&fakeIndex);
816 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
817 EXPECT_EQ(0, chainActive.Height());
819 wtx.SetMerkleBranch(block);
820 wallet.AddToWallet(wtx, true, NULL);
822 // Verify note has been spent
823 EXPECT_TRUE(wallet.IsSaplingSpent(nullifier));
826 chainActive.SetTip(NULL);
827 mapBlockIndex.erase(blockHash);
830 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
831 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
834 TEST(WalletTests, NavigateFromSproutNullifierToNote) {
837 auto sk = libzcash::SproutSpendingKey::random();
838 wallet.AddSproutSpendingKey(sk);
840 auto wtx = GetValidReceive(sk, 10, true);
841 auto note = GetNote(sk, wtx, 0, 1);
842 auto nullifier = note.nullifier(sk);
844 mapSproutNoteData_t noteData;
845 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
846 SproutNoteData nd {sk.address(), nullifier};
847 noteData[jsoutpt] = nd;
849 wtx.SetSproutNoteData(noteData);
851 EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes.count(nullifier));
853 wallet.AddToWallet(wtx, true, NULL);
854 EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes.count(nullifier));
855 EXPECT_EQ(wtx.GetHash(), wallet.mapSproutNullifiersToNotes[nullifier].hash);
856 EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes[nullifier].js);
857 EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes[nullifier].n);
860 TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
861 SelectParams(CBaseChainParams::REGTEST);
862 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
863 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
864 auto consensusParams = Params().GetConsensus();
868 // Generate dummy Sapling address
869 auto sk = libzcash::SaplingSpendingKey::random();
870 auto expsk = sk.expanded_spending_key();
871 auto fvk = sk.full_viewing_key();
872 auto pk = sk.default_address();
874 // Generate dummy Sapling note
875 libzcash::SaplingNote note(pk, 50000);
876 auto cm = note.cm().get();
877 SaplingMerkleTree saplingTree;
878 saplingTree.append(cm);
879 auto anchor = saplingTree.root();
880 auto witness = saplingTree.witness();
882 // Generate transaction
883 auto builder = TransactionBuilder(consensusParams, 1);
884 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
885 builder.AddSaplingOutput(fvk, pk, 25000, {});
886 auto maybe_tx = builder.Build();
887 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
888 auto tx = maybe_tx.get();
890 CWalletTx wtx {&wallet, tx};
891 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
892 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
894 // Manually compute the nullifier based on the expected position
895 auto nf = note.nullifier(fvk, witness.position());
897 uint256 nullifier = nf.get();
899 // Verify dummy note is unspent
900 EXPECT_FALSE(wallet.IsSaplingSpent(nullifier));
902 // Fake-mine the transaction
903 EXPECT_EQ(-1, chainActive.Height());
904 SproutMerkleTree sproutTree;
906 block.vtx.push_back(wtx);
907 block.hashMerkleRoot = block.BuildMerkleTree();
908 auto blockHash = block.GetHash();
909 CBlockIndex fakeIndex {block};
910 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
911 chainActive.SetTip(&fakeIndex);
912 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
913 EXPECT_EQ(0, chainActive.Height());
915 // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
916 wtx.SetMerkleBranch(block);
917 auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
918 ASSERT_TRUE(saplingNoteData.size() > 0);
919 wtx.SetSaplingNoteData(saplingNoteData);
920 wallet.AddToWallet(wtx, true, NULL);
922 // Verify dummy note is now spent, as AddToWallet invokes AddToSpends()
923 EXPECT_TRUE(wallet.IsSaplingSpent(nullifier));
925 // Test invariant: no witnesses means no nullifier.
926 EXPECT_EQ(0, wallet.mapSaplingNullifiersToNotes.size());
927 for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
928 SaplingNoteData nd = item.second;
929 ASSERT_TRUE(nd.witnesses.empty());
930 ASSERT_FALSE(nd.nullifier);
933 // Simulate receiving new block and ChainTip signal
934 wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
935 wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
937 // Retrieve the updated wtx from wallet
938 uint256 hash = wtx.GetHash();
939 wtx = wallet.mapWallet[hash];
941 // Verify Sapling nullifiers map to SaplingOutPoints
942 EXPECT_EQ(2, wallet.mapSaplingNullifiersToNotes.size());
943 for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
944 SaplingOutPoint op = item.first;
945 SaplingNoteData nd = item.second;
946 EXPECT_EQ(hash, op.hash);
947 EXPECT_EQ(1, nd.witnesses.size());
948 ASSERT_TRUE(nd.nullifier);
949 auto nf = nd.nullifier.get();
950 EXPECT_EQ(1, wallet.mapSaplingNullifiersToNotes.count(nf));
951 EXPECT_EQ(op.hash, wallet.mapSaplingNullifiersToNotes[nf].hash);
952 EXPECT_EQ(op.n, wallet.mapSaplingNullifiersToNotes[nf].n);
956 chainActive.SetTip(NULL);
957 mapBlockIndex.erase(blockHash);
960 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
961 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
964 TEST(WalletTests, SpentSproutNoteIsFromMe) {
967 auto sk = libzcash::SproutSpendingKey::random();
968 wallet.AddSproutSpendingKey(sk);
970 auto wtx = GetValidReceive(sk, 10, true);
971 auto note = GetNote(sk, wtx, 0, 1);
972 auto nullifier = note.nullifier(sk);
973 auto wtx2 = GetValidSpend(sk, note, 5);
975 EXPECT_FALSE(wallet.IsFromMe(wtx));
976 EXPECT_FALSE(wallet.IsFromMe(wtx2));
978 mapSproutNoteData_t noteData;
979 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
980 SproutNoteData nd {sk.address(), nullifier};
981 noteData[jsoutpt] = nd;
983 wtx.SetSproutNoteData(noteData);
984 EXPECT_FALSE(wallet.IsFromMe(wtx));
985 EXPECT_FALSE(wallet.IsFromMe(wtx2));
987 wallet.AddToWallet(wtx, true, NULL);
988 EXPECT_FALSE(wallet.IsFromMe(wtx));
989 EXPECT_TRUE(wallet.IsFromMe(wtx2));
992 // Create note A, spend A to create note B, spend and verify note B is from me.
993 TEST(WalletTests, SpentSaplingNoteIsFromMe) {
994 SelectParams(CBaseChainParams::REGTEST);
995 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
996 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
997 auto consensusParams = Params().GetConsensus();
1001 // Generate Sapling address
1002 auto sk = libzcash::SaplingSpendingKey::random();
1003 auto expsk = sk.expanded_spending_key();
1004 auto fvk = sk.full_viewing_key();
1005 auto ivk = fvk.in_viewing_key();
1006 auto pk = sk.default_address();
1008 // Generate Sapling note A
1009 libzcash::SaplingNote note(pk, 50000);
1010 auto cm = note.cm().get();
1011 SaplingMerkleTree saplingTree;
1012 saplingTree.append(cm);
1013 auto anchor = saplingTree.root();
1014 auto witness = saplingTree.witness();
1016 // Generate transaction, which sends funds to note B
1017 auto builder = TransactionBuilder(consensusParams, 1);
1018 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
1019 builder.AddSaplingOutput(fvk, pk, 25000, {});
1020 auto maybe_tx = builder.Build();
1021 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
1022 auto tx = maybe_tx.get();
1024 CWalletTx wtx {&wallet, tx};
1025 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
1026 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
1028 // Fake-mine the transaction
1029 EXPECT_EQ(-1, chainActive.Height());
1030 SproutMerkleTree sproutTree;
1032 block.vtx.push_back(wtx);
1033 block.hashMerkleRoot = block.BuildMerkleTree();
1034 auto blockHash = block.GetHash();
1035 CBlockIndex fakeIndex {block};
1036 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
1037 chainActive.SetTip(&fakeIndex);
1038 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
1039 EXPECT_EQ(0, chainActive.Height());
1041 auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
1042 ASSERT_TRUE(saplingNoteData.size() > 0);
1043 wtx.SetSaplingNoteData(saplingNoteData);
1044 wtx.SetMerkleBranch(block);
1045 wallet.AddToWallet(wtx, true, NULL);
1047 // Simulate receiving new block and ChainTip signal.
1048 // This triggers calculation of nullifiers for notes belonging to this wallet
1049 // in the output descriptions of wtx.
1050 wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
1051 wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
1053 // Retrieve the updated wtx from wallet
1054 wtx = wallet.mapWallet[wtx.GetHash()];
1056 // The test wallet never received the fake note which is being spent, so there
1057 // is no mapping from nullifier to notedata stored in mapSaplingNullifiersToNotes.
1058 // Therefore the wallet does not know the tx belongs to the wallet.
1059 EXPECT_FALSE(wallet.IsFromMe(wtx));
1061 // Manually compute the nullifier and check map entry does not exist
1062 auto nf = note.nullifier(fvk, witness.position());
1064 ASSERT_FALSE(wallet.mapSaplingNullifiersToNotes.count(nf.get()));
1067 auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
1068 wtx.vShieldedOutput[0].encCiphertext,
1070 wtx.vShieldedOutput[0].ephemeralKey,
1071 wtx.vShieldedOutput[0].cm);
1072 ASSERT_EQ(static_cast<bool>(maybe_pt), true);
1073 auto maybe_note = maybe_pt.get().note(ivk);
1074 ASSERT_EQ(static_cast<bool>(maybe_note), true);
1075 auto note2 = maybe_note.get();
1077 // Get witness to retrieve position of note B we want to spend
1078 SaplingOutPoint sop0(wtx.GetHash(), 0);
1079 auto spend_note_witness = wtx.mapSaplingNoteData[sop0].witnesses.front();
1080 auto maybe_nf = note2.nullifier(fvk, spend_note_witness.position());
1081 ASSERT_EQ(static_cast<bool>(maybe_nf), true);
1082 auto nullifier2 = maybe_nf.get();
1084 // NOTE: Not updating the anchor results in a core dump. Shouldn't builder just return error?
1085 // *** Error in `./zcash-gtest': double free or corruption (out): 0x00007ffd8755d990 ***
1086 anchor = saplingTree.root();
1088 // Create transaction to spend note B
1089 auto builder2 = TransactionBuilder(consensusParams, 2);
1090 ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
1091 builder2.AddSaplingOutput(fvk, pk, 12500, {});
1092 auto maybe_tx2 = builder2.Build();
1093 ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
1094 auto tx2 = maybe_tx2.get();
1095 EXPECT_EQ(tx2.vin.size(), 0);
1096 EXPECT_EQ(tx2.vout.size(), 0);
1097 EXPECT_EQ(tx2.vjoinsplit.size(), 0);
1098 EXPECT_EQ(tx2.vShieldedSpend.size(), 1);
1099 EXPECT_EQ(tx2.vShieldedOutput.size(), 2);
1100 EXPECT_EQ(tx2.valueBalance, 10000);
1102 CWalletTx wtx2 {&wallet, tx2};
1104 // Fake-mine this tx into the next block
1105 EXPECT_EQ(0, chainActive.Height());
1107 block2.vtx.push_back(wtx2);
1108 block2.hashMerkleRoot = block2.BuildMerkleTree();
1109 block2.hashPrevBlock = blockHash;
1110 auto blockHash2 = block2.GetHash();
1111 CBlockIndex fakeIndex2 {block2};
1112 mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2));
1113 fakeIndex2.nHeight = 1;
1114 chainActive.SetTip(&fakeIndex2);
1115 EXPECT_TRUE(chainActive.Contains(&fakeIndex2));
1116 EXPECT_EQ(1, chainActive.Height());
1118 auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2);
1119 ASSERT_TRUE(saplingNoteData2.size() > 0);
1120 wtx2.SetSaplingNoteData(saplingNoteData2);
1121 wtx2.SetMerkleBranch(block2);
1122 wallet.AddToWallet(wtx2, true, NULL);
1124 // Verify note B is spent. AddToWallet invokes AddToSpends which updates mapTxSaplingNullifiers
1125 EXPECT_TRUE(wallet.IsSaplingSpent(nullifier2));
1127 // Verify note B belongs to wallet.
1128 EXPECT_TRUE(wallet.IsFromMe(wtx2));
1129 ASSERT_TRUE(wallet.mapSaplingNullifiersToNotes.count(nullifier2));
1132 chainActive.SetTip(NULL);
1133 mapBlockIndex.erase(blockHash);
1134 mapBlockIndex.erase(blockHash2);
1136 // Revert to default
1137 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1138 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1141 TEST(WalletTests, CachedWitnessesEmptyChain) {
1144 auto sk = libzcash::SproutSpendingKey::random();
1145 wallet.AddSproutSpendingKey(sk);
1147 auto wtx = GetValidReceive(sk, 10, true, 4);
1148 auto note = GetNote(sk, wtx, 0, 0);
1149 auto note2 = GetNote(sk, wtx, 0, 1);
1150 auto nullifier = note.nullifier(sk);
1151 auto nullifier2 = note2.nullifier(sk);
1153 mapSproutNoteData_t sproutNoteData;
1154 JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
1155 JSOutPoint jsoutpt2 {wtx.GetHash(), 0, 1};
1156 SproutNoteData nd {sk.address(), nullifier};
1157 SproutNoteData nd2 {sk.address(), nullifier2};
1158 sproutNoteData[jsoutpt] = nd;
1159 sproutNoteData[jsoutpt2] = nd2;
1160 wtx.SetSproutNoteData(sproutNoteData);
1162 std::vector<JSOutPoint> sproutNotes {jsoutpt, jsoutpt2};
1163 std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
1165 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1166 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1168 ::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1170 EXPECT_FALSE((bool) sproutWitnesses[0]);
1171 EXPECT_FALSE((bool) sproutWitnesses[1]);
1172 EXPECT_FALSE((bool) saplingWitnesses[0]);
1174 wallet.AddToWallet(wtx, true, NULL);
1176 ::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1178 EXPECT_FALSE((bool) sproutWitnesses[0]);
1179 EXPECT_FALSE((bool) sproutWitnesses[1]);
1180 EXPECT_FALSE((bool) saplingWitnesses[0]);
1183 block.vtx.push_back(wtx);
1184 CBlockIndex index(block);
1185 SproutMerkleTree sproutTree;
1186 SaplingMerkleTree saplingTree;
1187 wallet.IncrementNoteWitnesses(&index, &block, sproutTree, saplingTree);
1189 ::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1191 EXPECT_TRUE((bool) sproutWitnesses[0]);
1192 EXPECT_TRUE((bool) sproutWitnesses[1]);
1193 EXPECT_TRUE((bool) saplingWitnesses[0]);
1195 // Until #1302 is implemented, this should triggger an assertion
1196 EXPECT_DEATH(wallet.DecrementNoteWitnesses(&index),
1197 ".*nWitnessCacheSize > 0.*");
1200 TEST(WalletTests, CachedWitnessesChainTip) {
1202 std::pair<uint256, uint256> anchors1;
1204 SproutMerkleTree sproutTree;
1205 SaplingMerkleTree saplingTree;
1207 auto sk = libzcash::SproutSpendingKey::random();
1208 wallet.AddSproutSpendingKey(sk);
1211 // First block (case tested in _empty_chain)
1212 CBlockIndex index1(block1);
1214 auto outpts = CreateValidBlock(wallet, sk, index1, block1, sproutTree, saplingTree);
1216 // Called to fetch anchor
1217 std::vector<JSOutPoint> sproutNotes {outpts.first};
1218 std::vector<SaplingOutPoint> saplingNotes {outpts.second};
1219 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1220 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1222 anchors1 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1223 EXPECT_NE(anchors1.first, anchors1.second);
1227 // Second transaction
1228 auto wtx = GetValidReceive(sk, 50, true, 4);
1229 auto note = GetNote(sk, wtx, 0, 1);
1230 auto nullifier = note.nullifier(sk);
1232 mapSproutNoteData_t sproutNoteData;
1233 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
1234 SproutNoteData nd {sk.address(), nullifier};
1235 sproutNoteData[jsoutpt] = nd;
1236 wtx.SetSproutNoteData(sproutNoteData);
1237 std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
1238 wallet.AddToWallet(wtx, true, NULL);
1240 std::vector<JSOutPoint> sproutNotes {jsoutpt};
1241 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1242 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1244 GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1246 EXPECT_FALSE((bool) sproutWitnesses[0]);
1247 EXPECT_FALSE((bool) saplingWitnesses[0]);
1251 block2.hashPrevBlock = block1.GetHash();
1252 block2.vtx.push_back(wtx);
1253 CBlockIndex index2(block2);
1255 SproutMerkleTree sproutTree2 {sproutTree};
1256 SaplingMerkleTree saplingTree2 {saplingTree};
1257 wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree2, saplingTree2);
1259 auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1260 EXPECT_NE(anchors2.first, anchors2.second);
1262 EXPECT_TRUE((bool) sproutWitnesses[0]);
1263 EXPECT_TRUE((bool) saplingWitnesses[0]);
1264 EXPECT_NE(anchors1.first, anchors2.first);
1265 EXPECT_NE(anchors1.second, anchors2.second);
1267 // Decrementing should give us the previous anchor
1268 wallet.DecrementNoteWitnesses(&index2);
1269 auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1271 EXPECT_FALSE((bool) sproutWitnesses[0]);
1272 EXPECT_FALSE((bool) saplingWitnesses[0]);
1273 // Should not equal first anchor because none of these notes had witnesses
1274 EXPECT_NE(anchors1.first, anchors3.first);
1275 EXPECT_NE(anchors1.second, anchors3.second);
1277 // Re-incrementing with the same block should give the same result
1278 wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
1279 auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1280 EXPECT_NE(anchors4.first, anchors4.second);
1282 EXPECT_TRUE((bool) sproutWitnesses[0]);
1283 EXPECT_TRUE((bool) saplingWitnesses[0]);
1284 EXPECT_EQ(anchors2.first, anchors4.first);
1285 EXPECT_EQ(anchors2.second, anchors4.second);
1287 // Incrementing with the same block again should not change the cache
1288 wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
1289 std::vector<boost::optional<SproutWitness>> sproutWitnesses5;
1290 std::vector<boost::optional<SaplingWitness>> saplingWitnesses5;
1292 auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses5, saplingWitnesses5);
1293 EXPECT_NE(anchors5.first, anchors5.second);
1295 EXPECT_EQ(sproutWitnesses, sproutWitnesses5);
1296 EXPECT_EQ(saplingWitnesses, saplingWitnesses5);
1297 EXPECT_EQ(anchors4.first, anchors5.first);
1298 EXPECT_EQ(anchors4.second, anchors5.second);
1302 TEST(WalletTests, CachedWitnessesDecrementFirst) {
1304 SproutMerkleTree sproutTree;
1305 SaplingMerkleTree saplingTree;
1307 auto sk = libzcash::SproutSpendingKey::random();
1308 wallet.AddSproutSpendingKey(sk);
1311 // First block (case tested in _empty_chain)
1313 CBlockIndex index1(block1);
1315 CreateValidBlock(wallet, sk, index1, block1, sproutTree, saplingTree);
1318 std::pair<uint256, uint256> anchors2;
1320 CBlockIndex index2(block2);
1323 // Second block (case tested in _chain_tip)
1325 auto outpts = CreateValidBlock(wallet, sk, index2, block2, sproutTree, saplingTree);
1327 // Called to fetch anchor
1328 std::vector<JSOutPoint> sproutNotes {outpts.first};
1329 std::vector<SaplingOutPoint> saplingNotes {outpts.second};
1330 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1331 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1332 anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1336 // Third transaction - never mined
1337 auto wtx = GetValidReceive(sk, 20, true, 4);
1338 auto note = GetNote(sk, wtx, 0, 1);
1339 auto nullifier = note.nullifier(sk);
1341 mapSproutNoteData_t noteData;
1342 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
1343 SproutNoteData nd {sk.address(), nullifier};
1344 noteData[jsoutpt] = nd;
1345 wtx.SetSproutNoteData(noteData);
1346 std::vector<SaplingOutPoint> saplingNotes = SetSaplingNoteData(wtx);
1347 wallet.AddToWallet(wtx, true, NULL);
1349 std::vector<JSOutPoint> sproutNotes {jsoutpt};
1350 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1351 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1353 auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1355 EXPECT_FALSE((bool) sproutWitnesses[0]);
1356 EXPECT_FALSE((bool) saplingWitnesses[0]);
1358 // Decrementing (before the transaction has ever seen an increment)
1359 // should give us the previous anchor
1360 wallet.DecrementNoteWitnesses(&index2);
1362 auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1364 EXPECT_FALSE((bool) sproutWitnesses[0]);
1365 EXPECT_FALSE((bool) saplingWitnesses[0]);
1366 // Should not equal second anchor because none of these notes had witnesses
1367 EXPECT_NE(anchors2.first, anchors4.first);
1368 EXPECT_NE(anchors2.second, anchors4.second);
1370 // Re-incrementing with the same block should give the same result
1371 wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
1373 auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1375 EXPECT_FALSE((bool) sproutWitnesses[0]);
1376 EXPECT_FALSE((bool) saplingWitnesses[0]);
1377 EXPECT_EQ(anchors3.first, anchors5.first);
1378 EXPECT_EQ(anchors3.second, anchors5.second);
1382 TEST(WalletTests, CachedWitnessesCleanIndex) {
1384 std::vector<CBlock> blocks;
1385 std::vector<CBlockIndex> indices;
1386 std::vector<JSOutPoint> sproutNotes;
1387 std::vector<SaplingOutPoint> saplingNotes;
1388 std::vector<uint256> sproutAnchors;
1389 std::vector<uint256> saplingAnchors;
1390 SproutMerkleTree sproutTree;
1391 SproutMerkleTree sproutRiTree = sproutTree;
1392 SaplingMerkleTree saplingTree;
1393 SaplingMerkleTree saplingRiTree = saplingTree;
1394 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1395 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1397 auto sk = libzcash::SproutSpendingKey::random();
1398 wallet.AddSproutSpendingKey(sk);
1401 size_t numBlocks = WITNESS_CACHE_SIZE + 10;
1402 blocks.resize(numBlocks);
1403 indices.resize(numBlocks);
1404 for (size_t i = 0; i < numBlocks; i++) {
1405 indices[i].nHeight = i;
1406 auto oldSproutRoot = sproutTree.root();
1407 auto oldSaplingRoot = saplingTree.root();
1408 auto outpts = CreateValidBlock(wallet, sk, indices[i], blocks[i], sproutTree, saplingTree);
1409 EXPECT_NE(oldSproutRoot, sproutTree.root());
1410 EXPECT_NE(oldSaplingRoot, saplingTree.root());
1411 sproutNotes.push_back(outpts.first);
1412 saplingNotes.push_back(outpts.second);
1414 auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1415 for (size_t j = 0; j <= i; j++) {
1416 EXPECT_TRUE((bool) sproutWitnesses[j]);
1417 EXPECT_TRUE((bool) saplingWitnesses[j]);
1419 sproutAnchors.push_back(anchors.first);
1420 saplingAnchors.push_back(anchors.second);
1423 // Now pretend we are reindexing: the chain is cleared, and each block is
1424 // used to increment witnesses again.
1425 for (size_t i = 0; i < numBlocks; i++) {
1426 SproutMerkleTree sproutRiPrevTree {sproutRiTree};
1427 SaplingMerkleTree saplingRiPrevTree {saplingRiTree};
1428 wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), sproutRiTree, saplingRiTree);
1430 auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1431 for (size_t j = 0; j < numBlocks; j++) {
1432 EXPECT_TRUE((bool) sproutWitnesses[j]);
1433 EXPECT_TRUE((bool) saplingWitnesses[j]);
1435 // Should equal final anchor because witness cache unaffected
1436 EXPECT_EQ(sproutAnchors.back(), anchors.first);
1437 EXPECT_EQ(saplingAnchors.back(), anchors.second);
1439 if ((i == 5) || (i == 50)) {
1440 // Pretend a reorg happened that was recorded in the block files
1442 wallet.DecrementNoteWitnesses(&(indices[i]));
1444 auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1445 for (size_t j = 0; j < numBlocks; j++) {
1446 EXPECT_TRUE((bool) sproutWitnesses[j]);
1447 EXPECT_TRUE((bool) saplingWitnesses[j]);
1449 // Should equal final anchor because witness cache unaffected
1450 EXPECT_EQ(sproutAnchors.back(), anchors.first);
1451 EXPECT_EQ(saplingAnchors.back(), anchors.second);
1455 wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), sproutRiPrevTree, saplingRiPrevTree);
1456 auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1457 for (size_t j = 0; j < numBlocks; j++) {
1458 EXPECT_TRUE((bool) sproutWitnesses[j]);
1459 EXPECT_TRUE((bool) saplingWitnesses[j]);
1461 // Should equal final anchor because witness cache unaffected
1462 EXPECT_EQ(sproutAnchors.back(), anchors.first);
1463 EXPECT_EQ(saplingAnchors.back(), anchors.second);
1469 TEST(WalletTests, ClearNoteWitnessCache) {
1472 auto sk = libzcash::SproutSpendingKey::random();
1473 wallet.AddSproutSpendingKey(sk);
1475 auto wtx = GetValidReceive(sk, 10, true, 4);
1476 auto hash = wtx.GetHash();
1477 auto note = GetNote(sk, wtx, 0, 0);
1478 auto nullifier = note.nullifier(sk);
1480 mapSproutNoteData_t noteData;
1481 JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
1482 JSOutPoint jsoutpt2 {wtx.GetHash(), 0, 1};
1483 SproutNoteData nd {sk.address(), nullifier};
1484 noteData[jsoutpt] = nd;
1485 wtx.SetSproutNoteData(noteData);
1486 auto saplingNotes = SetSaplingNoteData(wtx);
1488 // Pretend we mined the tx by adding a fake witness
1489 SproutMerkleTree sproutTree;
1490 wtx.mapSproutNoteData[jsoutpt].witnesses.push_front(sproutTree.witness());
1491 wtx.mapSproutNoteData[jsoutpt].witnessHeight = 1;
1492 wallet.nWitnessCacheSize = 1;
1494 SaplingMerkleTree saplingTree;
1495 wtx.mapSaplingNoteData[saplingNotes[0]].witnesses.push_front(saplingTree.witness());
1496 wtx.mapSaplingNoteData[saplingNotes[0]].witnessHeight = 1;
1497 wallet.nWitnessCacheSize = 2;
1499 wallet.AddToWallet(wtx, true, NULL);
1501 std::vector<JSOutPoint> sproutNotes {jsoutpt, jsoutpt2};
1502 std::vector<boost::optional<SproutWitness>> sproutWitnesses;
1503 std::vector<boost::optional<SaplingWitness>> saplingWitnesses;
1505 // Before clearing, we should have a witness for one note
1506 GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1507 EXPECT_TRUE((bool) sproutWitnesses[0]);
1508 EXPECT_FALSE((bool) sproutWitnesses[1]);
1509 EXPECT_TRUE((bool) saplingWitnesses[0]);
1510 EXPECT_FALSE((bool) saplingWitnesses[1]);
1511 EXPECT_EQ(1, wallet.mapWallet[hash].mapSproutNoteData[jsoutpt].witnessHeight);
1512 EXPECT_EQ(1, wallet.mapWallet[hash].mapSaplingNoteData[saplingNotes[0]].witnessHeight);
1513 EXPECT_EQ(2, wallet.nWitnessCacheSize);
1515 // After clearing, we should not have a witness for either note
1516 wallet.ClearNoteWitnessCache();
1517 auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
1518 EXPECT_FALSE((bool) sproutWitnesses[0]);
1519 EXPECT_FALSE((bool) sproutWitnesses[1]);
1520 EXPECT_FALSE((bool) saplingWitnesses[0]);
1521 EXPECT_FALSE((bool) saplingWitnesses[1]);
1522 EXPECT_EQ(-1, wallet.mapWallet[hash].mapSproutNoteData[jsoutpt].witnessHeight);
1523 EXPECT_EQ(-1, wallet.mapWallet[hash].mapSaplingNoteData[saplingNotes[0]].witnessHeight);
1524 EXPECT_EQ(0, wallet.nWitnessCacheSize);
1527 TEST(WalletTests, WriteWitnessCache) {
1529 MockWalletDB walletdb;
1532 auto sk = libzcash::SproutSpendingKey::random();
1533 wallet.AddSproutSpendingKey(sk);
1535 auto wtx = GetValidReceive(sk, 10, true);
1536 wallet.AddToWallet(wtx, true, NULL);
1539 EXPECT_CALL(walletdb, TxnBegin())
1540 .WillOnce(Return(false));
1541 wallet.SetBestChain(walletdb, loc);
1542 EXPECT_CALL(walletdb, TxnBegin())
1543 .WillRepeatedly(Return(true));
1546 EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx))
1547 .WillOnce(Return(false));
1548 EXPECT_CALL(walletdb, TxnAbort())
1550 wallet.SetBestChain(walletdb, loc);
1553 EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx))
1554 .WillOnce(ThrowLogicError());
1555 EXPECT_CALL(walletdb, TxnAbort())
1557 wallet.SetBestChain(walletdb, loc);
1558 EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx))
1559 .WillRepeatedly(Return(true));
1561 // WriteWitnessCacheSize fails
1562 EXPECT_CALL(walletdb, WriteWitnessCacheSize(0))
1563 .WillOnce(Return(false));
1564 EXPECT_CALL(walletdb, TxnAbort())
1566 wallet.SetBestChain(walletdb, loc);
1568 // WriteWitnessCacheSize throws
1569 EXPECT_CALL(walletdb, WriteWitnessCacheSize(0))
1570 .WillOnce(ThrowLogicError());
1571 EXPECT_CALL(walletdb, TxnAbort())
1573 wallet.SetBestChain(walletdb, loc);
1574 EXPECT_CALL(walletdb, WriteWitnessCacheSize(0))
1575 .WillRepeatedly(Return(true));
1577 // WriteBestBlock fails
1578 EXPECT_CALL(walletdb, WriteBestBlock(loc))
1579 .WillOnce(Return(false));
1580 EXPECT_CALL(walletdb, TxnAbort())
1582 wallet.SetBestChain(walletdb, loc);
1584 // WriteBestBlock throws
1585 EXPECT_CALL(walletdb, WriteBestBlock(loc))
1586 .WillOnce(ThrowLogicError());
1587 EXPECT_CALL(walletdb, TxnAbort())
1589 wallet.SetBestChain(walletdb, loc);
1590 EXPECT_CALL(walletdb, WriteBestBlock(loc))
1591 .WillRepeatedly(Return(true));
1594 EXPECT_CALL(walletdb, TxnCommit())
1595 .WillOnce(Return(false));
1596 wallet.SetBestChain(walletdb, loc);
1597 EXPECT_CALL(walletdb, TxnCommit())
1598 .WillRepeatedly(Return(true));
1600 // Everything succeeds
1601 wallet.SetBestChain(walletdb, loc);
1604 TEST(WalletTests, UpdateSproutNullifierNoteMap) {
1606 uint256 r {GetRandHash()};
1607 CKeyingMaterial vMasterKey (r.begin(), r.end());
1609 auto sk = libzcash::SproutSpendingKey::random();
1610 wallet.AddSproutSpendingKey(sk);
1612 ASSERT_TRUE(wallet.EncryptKeys(vMasterKey));
1614 auto wtx = GetValidReceive(sk, 10, true);
1615 auto note = GetNote(sk, wtx, 0, 1);
1616 auto nullifier = note.nullifier(sk);
1618 // Pretend that we called FindMySproutNotes while the wallet was locked
1619 mapSproutNoteData_t noteData;
1620 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
1621 SproutNoteData nd {sk.address()};
1622 noteData[jsoutpt] = nd;
1623 wtx.SetSproutNoteData(noteData);
1625 wallet.AddToWallet(wtx, true, NULL);
1626 EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes.count(nullifier));
1628 EXPECT_FALSE(wallet.UpdateNullifierNoteMap());
1630 ASSERT_TRUE(wallet.Unlock(vMasterKey));
1632 EXPECT_TRUE(wallet.UpdateNullifierNoteMap());
1633 EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes.count(nullifier));
1634 EXPECT_EQ(wtx.GetHash(), wallet.mapSproutNullifiersToNotes[nullifier].hash);
1635 EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes[nullifier].js);
1636 EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes[nullifier].n);
1639 TEST(WalletTests, UpdatedSproutNoteData) {
1642 auto sk = libzcash::SproutSpendingKey::random();
1643 wallet.AddSproutSpendingKey(sk);
1645 auto wtx = GetValidReceive(sk, 10, true);
1646 auto note = GetNote(sk, wtx, 0, 0);
1647 auto note2 = GetNote(sk, wtx, 0, 1);
1648 auto nullifier = note.nullifier(sk);
1649 auto nullifier2 = note2.nullifier(sk);
1652 // First pretend we added the tx to the wallet and
1653 // we don't have the key for the second note
1654 mapSproutNoteData_t noteData;
1655 JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
1656 SproutNoteData nd {sk.address(), nullifier};
1657 noteData[jsoutpt] = nd;
1658 wtx.SetSproutNoteData(noteData);
1660 // Pretend we mined the tx by adding a fake witness
1661 SproutMerkleTree tree;
1662 wtx.mapSproutNoteData[jsoutpt].witnesses.push_front(tree.witness());
1663 wtx.mapSproutNoteData[jsoutpt].witnessHeight = 100;
1665 // Now pretend we added the key for the second note, and
1666 // the tx was "added" to the wallet again to update it.
1667 // This happens via the 'z_importkey' RPC method.
1668 JSOutPoint jsoutpt2 {wtx2.GetHash(), 0, 1};
1669 SproutNoteData nd2 {sk.address(), nullifier2};
1670 noteData[jsoutpt2] = nd2;
1671 wtx2.SetSproutNoteData(noteData);
1673 // The txs should initially be different
1674 EXPECT_NE(wtx.mapSproutNoteData, wtx2.mapSproutNoteData);
1675 EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size());
1676 EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight);
1678 // After updating, they should be the same
1679 EXPECT_TRUE(wallet.UpdatedNoteData(wtx2, wtx));
1680 EXPECT_EQ(wtx.mapSproutNoteData, wtx2.mapSproutNoteData);
1681 EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size());
1682 EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight);
1683 // TODO: The new note should get witnessed (but maybe not here) (#1350)
1686 TEST(WalletTests, UpdatedSaplingNoteData) {
1687 SelectParams(CBaseChainParams::REGTEST);
1688 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
1689 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
1690 auto consensusParams = Params().GetConsensus();
1694 // Generate dummy Sapling address
1695 auto sk = libzcash::SaplingSpendingKey::random();
1696 auto expsk = sk.expanded_spending_key();
1697 auto fvk = sk.full_viewing_key();
1698 auto pk = sk.default_address();
1700 // Generate dummy recipient Sapling address
1701 auto sk2 = libzcash::SaplingSpendingKey::random();
1702 auto fvk2 = sk2.full_viewing_key();
1703 auto pk2 = sk2.default_address();
1705 // Generate dummy Sapling note
1706 libzcash::SaplingNote note(pk, 50000);
1707 auto cm = note.cm().get();
1708 SaplingMerkleTree saplingTree;
1709 saplingTree.append(cm);
1710 auto anchor = saplingTree.root();
1711 auto witness = saplingTree.witness();
1713 // Generate transaction
1714 auto builder = TransactionBuilder(consensusParams, 1);
1715 ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
1716 builder.AddSaplingOutput(fvk, pk2, 25000, {});
1717 auto maybe_tx = builder.Build();
1718 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
1719 auto tx = maybe_tx.get();
1721 // Wallet contains fvk1 but not fvk2
1722 CWalletTx wtx {&wallet, tx};
1723 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
1724 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
1725 ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk2));
1727 // Fake-mine the transaction
1728 EXPECT_EQ(-1, chainActive.Height());
1729 SproutMerkleTree sproutTree;
1731 block.vtx.push_back(wtx);
1732 block.hashMerkleRoot = block.BuildMerkleTree();
1733 auto blockHash = block.GetHash();
1734 CBlockIndex fakeIndex {block};
1735 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
1736 chainActive.SetTip(&fakeIndex);
1737 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
1738 EXPECT_EQ(0, chainActive.Height());
1740 // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
1741 auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
1742 ASSERT_TRUE(saplingNoteData.size() == 1); // wallet only has key for change output
1743 wtx.SetSaplingNoteData(saplingNoteData);
1744 wtx.SetMerkleBranch(block);
1745 wallet.AddToWallet(wtx, true, NULL);
1747 // Simulate receiving new block and ChainTip signal
1748 wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
1749 wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
1751 // Retrieve the updated wtx from wallet
1752 uint256 hash = wtx.GetHash();
1753 wtx = wallet.mapWallet[hash];
1755 // Now lets add key fvk2 so wallet can find the payment note sent to pk2
1756 ASSERT_TRUE(wallet.AddSaplingZKey(sk2));
1757 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk2));
1758 CWalletTx wtx2 = wtx;
1759 auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2);
1760 ASSERT_TRUE(saplingNoteData2.size() == 2);
1761 wtx2.SetSaplingNoteData(saplingNoteData2);
1763 // The payment note has not been witnessed yet, so let's fake the witness.
1764 SaplingOutPoint sop0(wtx2.GetHash(), 0);
1765 SaplingOutPoint sop1(wtx2.GetHash(), 1);
1766 wtx2.mapSaplingNoteData[sop0].witnesses.push_front(saplingTree.witness());
1767 wtx2.mapSaplingNoteData[sop0].witnessHeight = 0;
1769 // The txs are different as wtx is aware of just the change output,
1770 // whereas wtx2 is aware of both payment and change outputs.
1771 EXPECT_NE(wtx.mapSaplingNoteData, wtx2.mapSaplingNoteData);
1772 EXPECT_EQ(1, wtx.mapSaplingNoteData.size());
1773 EXPECT_EQ(1, wtx.mapSaplingNoteData[sop1].witnesses.size()); // wtx has witness for change
1775 EXPECT_EQ(2, wtx2.mapSaplingNoteData.size());
1776 EXPECT_EQ(1, wtx2.mapSaplingNoteData[sop0].witnesses.size()); // wtx2 has fake witness for payment output
1777 EXPECT_EQ(0, wtx2.mapSaplingNoteData[sop1].witnesses.size()); // wtx2 never had incrementnotewitness called
1779 // After updating, they should be the same
1780 EXPECT_TRUE(wallet.UpdatedNoteData(wtx2, wtx));
1782 // We can't do this:
1783 // EXPECT_EQ(wtx.mapSaplingNoteData, wtx2.mapSaplingNoteData);
1784 // because nullifiers (if part of == comparator) have not all been computed
1785 // Also note that mapwallet[hash] is not updated with the updated wtx.
1786 // wtx = wallet.mapWallet[hash];
1788 EXPECT_EQ(2, wtx.mapSaplingNoteData.size());
1789 EXPECT_EQ(2, wtx2.mapSaplingNoteData.size());
1790 // wtx copied over the fake witness from wtx2 for the payment output
1791 EXPECT_EQ(wtx.mapSaplingNoteData[sop0].witnesses.front(), wtx2.mapSaplingNoteData[sop0].witnesses.front());
1792 // wtx2 never had its change output witnessed even though it has been in wtx
1793 EXPECT_EQ(0, wtx2.mapSaplingNoteData[sop1].witnesses.size());
1794 EXPECT_EQ(wtx.mapSaplingNoteData[sop1].witnesses.front(), saplingTree.witness());
1797 chainActive.SetTip(NULL);
1798 mapBlockIndex.erase(blockHash);
1800 // Revert to default
1801 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1802 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1805 TEST(WalletTests, MarkAffectedSproutTransactionsDirty) {
1808 auto sk = libzcash::SproutSpendingKey::random();
1809 wallet.AddSproutSpendingKey(sk);
1811 auto wtx = GetValidReceive(sk, 10, true);
1812 auto hash = wtx.GetHash();
1813 auto note = GetNote(sk, wtx, 0, 1);
1814 auto nullifier = note.nullifier(sk);
1815 auto wtx2 = GetValidSpend(sk, note, 5);
1817 mapSproutNoteData_t noteData;
1818 JSOutPoint jsoutpt {hash, 0, 1};
1819 SproutNoteData nd {sk.address(), nullifier};
1820 noteData[jsoutpt] = nd;
1822 wtx.SetSproutNoteData(noteData);
1823 wallet.AddToWallet(wtx, true, NULL);
1824 wallet.MarkAffectedTransactionsDirty(wtx);
1826 // After getting a cached value, the first tx should be clean
1827 wallet.mapWallet[hash].GetDebit(ISMINE_ALL);
1828 EXPECT_TRUE(wallet.mapWallet[hash].fDebitCached);
1830 // After adding the note spend, the first tx should be dirty
1831 wallet.AddToWallet(wtx2, true, NULL);
1832 wallet.MarkAffectedTransactionsDirty(wtx2);
1833 EXPECT_FALSE(wallet.mapWallet[hash].fDebitCached);
1836 TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
1837 SelectParams(CBaseChainParams::REGTEST);
1838 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
1839 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
1840 auto consensusParams = Params().GetConsensus();
1844 // Generate Sapling address
1845 auto sk = libzcash::SaplingSpendingKey::random();
1846 auto expsk = sk.expanded_spending_key();
1847 auto fvk = sk.full_viewing_key();
1848 auto ivk = fvk.in_viewing_key();
1849 auto pk = sk.default_address();
1851 ASSERT_TRUE(wallet.AddSaplingZKey(sk));
1852 ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
1854 // Set up transparent address
1855 CBasicKeyStore keystore;
1856 CKey tsk = DecodeSecret(tSecretRegtest);
1857 keystore.AddKey(tsk);
1858 auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
1860 // Generate shielding tx from transparent to Sapling
1861 // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
1862 auto builder = TransactionBuilder(consensusParams, 1, &keystore);
1863 builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
1864 builder.AddSaplingOutput(fvk, pk, 40000, {});
1865 auto maybe_tx = builder.Build();
1866 ASSERT_EQ(static_cast<bool>(maybe_tx), true);
1867 auto tx1 = maybe_tx.get();
1869 EXPECT_EQ(tx1.vin.size(), 1);
1870 EXPECT_EQ(tx1.vout.size(), 0);
1871 EXPECT_EQ(tx1.vjoinsplit.size(), 0);
1872 EXPECT_EQ(tx1.vShieldedSpend.size(), 0);
1873 EXPECT_EQ(tx1.vShieldedOutput.size(), 1);
1874 EXPECT_EQ(tx1.valueBalance, -40000);
1876 CWalletTx wtx {&wallet, tx1};
1878 // Fake-mine the transaction
1879 EXPECT_EQ(-1, chainActive.Height());
1880 SaplingMerkleTree saplingTree;
1881 SproutMerkleTree sproutTree;
1883 block.vtx.push_back(wtx);
1884 block.hashMerkleRoot = block.BuildMerkleTree();
1885 auto blockHash = block.GetHash();
1886 CBlockIndex fakeIndex {block};
1887 mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
1888 chainActive.SetTip(&fakeIndex);
1889 EXPECT_TRUE(chainActive.Contains(&fakeIndex));
1890 EXPECT_EQ(0, chainActive.Height());
1892 // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
1893 auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
1894 ASSERT_TRUE(saplingNoteData.size() > 0);
1895 wtx.SetSaplingNoteData(saplingNoteData);
1896 wtx.SetMerkleBranch(block);
1897 wallet.AddToWallet(wtx, true, NULL);
1899 // Simulate receiving new block and ChainTip signal
1900 wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
1901 wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
1903 // Retrieve the updated wtx from wallet
1904 uint256 hash = wtx.GetHash();
1905 wtx = wallet.mapWallet[hash];
1907 // Prepare to spend the note that was just created
1908 auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
1909 tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cm);
1910 ASSERT_EQ(static_cast<bool>(maybe_pt), true);
1911 auto maybe_note = maybe_pt.get().note(ivk);
1912 ASSERT_EQ(static_cast<bool>(maybe_note), true);
1913 auto note = maybe_note.get();
1914 auto anchor = saplingTree.root();
1915 auto witness = saplingTree.witness();
1917 // Create a Sapling-only transaction
1918 // 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
1919 auto builder2 = TransactionBuilder(consensusParams, 2);
1920 ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
1921 builder2.AddSaplingOutput(fvk, pk, 25000, {});
1922 auto maybe_tx2 = builder2.Build();
1923 ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
1924 auto tx2 = maybe_tx2.get();
1926 EXPECT_EQ(tx2.vin.size(), 0);
1927 EXPECT_EQ(tx2.vout.size(), 0);
1928 EXPECT_EQ(tx2.vjoinsplit.size(), 0);
1929 EXPECT_EQ(tx2.vShieldedSpend.size(), 1);
1930 EXPECT_EQ(tx2.vShieldedOutput.size(), 2);
1931 EXPECT_EQ(tx2.valueBalance, 10000);
1933 CWalletTx wtx2 {&wallet, tx2};
1934 auto hash2 = wtx2.GetHash();
1936 wallet.MarkAffectedTransactionsDirty(wtx);
1938 // After getting a cached value, the first tx should be clean
1939 wallet.mapWallet[hash].GetDebit(ISMINE_ALL);
1940 EXPECT_TRUE(wallet.mapWallet[hash].fDebitCached);
1942 // After adding the note spend, the first tx should be dirty
1943 wallet.AddToWallet(wtx2, true, NULL);
1944 wallet.MarkAffectedTransactionsDirty(wtx2);
1945 EXPECT_FALSE(wallet.mapWallet[hash].fDebitCached);
1948 chainActive.SetTip(NULL);
1949 mapBlockIndex.erase(blockHash);
1951 // Revert to default
1952 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1953 UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
1956 TEST(WalletTests, SproutNoteLocking) {
1959 auto sk = libzcash::SproutSpendingKey::random();
1960 wallet.AddSproutSpendingKey(sk);
1962 auto wtx = GetValidReceive(sk, 10, true);
1963 auto wtx2 = GetValidReceive(sk, 10, true);
1965 JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
1966 JSOutPoint jsoutpt2 {wtx2.GetHash(),0, 0};
1968 // Test selective locking
1969 wallet.LockNote(jsoutpt);
1970 EXPECT_TRUE(wallet.IsLockedNote(jsoutpt));
1971 EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2));
1973 // Test selective unlocking
1974 wallet.UnlockNote(jsoutpt);
1975 EXPECT_FALSE(wallet.IsLockedNote(jsoutpt));
1977 // Test multiple locking
1978 wallet.LockNote(jsoutpt);
1979 wallet.LockNote(jsoutpt2);
1980 EXPECT_TRUE(wallet.IsLockedNote(jsoutpt));
1981 EXPECT_TRUE(wallet.IsLockedNote(jsoutpt2));
1984 wallet.UnlockAllNotes();
1985 EXPECT_FALSE(wallet.IsLockedNote(jsoutpt));
1986 EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2));