TEST(wallet_tests, navigate_from_nullifier_to_note) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); EXPECT_EQ(0, wallet.mapNullifiersToNotes.count(nullifier)); wallet.AddToWallet(wtx, true, NULL); EXPECT_EQ(1, wallet.mapNullifiersToNotes.count(nullifier)); EXPECT_EQ(wtx.GetHash(), wallet.mapNullifiersToNotes[nullifier].hash); EXPECT_EQ(0, wallet.mapNullifiersToNotes[nullifier].js); EXPECT_EQ(1, wallet.mapNullifiersToNotes[nullifier].n); }
TEST(wallet_tests, spent_note_is_from_me) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); auto wtx2 = GetValidSpend(sk, note, 5); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_FALSE(wallet.IsFromMe(wtx2)); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_FALSE(wallet.IsFromMe(wtx2)); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_TRUE(wallet.IsFromMe(wtx2)); }
TEST(wallet_tests, get_conflicted_notes) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); auto wtx2 = GetValidSpend(sk, note, 5); auto wtx3 = GetValidSpend(sk, note, 10); auto hash2 = wtx2.GetHash(); auto hash3 = wtx3.GetHash(); // No conflicts for no spends EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); wallet.AddToWallet(wtx, true, NULL); EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); // No conflicts for one spend wallet.AddToWallet(wtx2, true, NULL); EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); // Conflicts for two spends wallet.AddToWallet(wtx3, true, NULL); auto c3 = wallet.GetConflicts(hash2); EXPECT_EQ(2, c3.size()); EXPECT_EQ(std::set<uint256>({hash2, hash3}), c3); }
TEST(wallet_tests, GetNoteNullifier) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); auto address = sk.address(); auto dec = ZCNoteDecryption(sk.viewing_key()); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); auto hSig = wtx.vjoinsplit[0].h_sig( *params, wtx.joinSplitPubKey); auto ret = wallet.GetNoteNullifier( wtx.vjoinsplit[0], address, dec, hSig, 1); EXPECT_NE(nullifier, ret); wallet.AddSpendingKey(sk); ret = wallet.GetNoteNullifier( wtx.vjoinsplit[0], address, dec, hSig, 1); EXPECT_EQ(nullifier, ret); }
double benchmark_increment_note_witnesses(size_t nTxs) { CWallet wallet; ZCIncrementalMerkleTree tree; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); // First block CBlock block1; for (int i = 0; i < nTxs; i++) { auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); block1.vtx.push_back(wtx); } CBlockIndex index1(block1); index1.nHeight = 1; // Increment to get transactions witnessed wallet.ChainTip(&index1, &block1, tree, true); // Second block CBlock block2; block2.hashPrevBlock = block1.GetHash(); { auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); block2.vtx.push_back(wtx); } CBlockIndex index2(block2); index2.nHeight = 2; struct timeval tv_start; timer_start(tv_start); wallet.ChainTip(&index2, &block2, tree, true); return timer_stop(tv_start); }
TEST(wallet_tests, FindMyNotes) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); auto sk2 = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk2); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); auto noteMap = wallet.FindMyNotes(wtx); EXPECT_EQ(0, noteMap.size()); wallet.AddSpendingKey(sk); noteMap = wallet.FindMyNotes(wtx); EXPECT_EQ(2, noteMap.size()); JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; EXPECT_EQ(1, noteMap.count(jsoutpt)); EXPECT_EQ(nd, noteMap[jsoutpt]); }
double benchmark_try_decrypt_notes(size_t nAddrs) { CWallet wallet; for (int i = 0; i < nAddrs; i++) { auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); } auto sk = libzcash::SpendingKey::random(); auto tx = GetValidReceive(*pzcashParams, sk, 10, true); struct timeval tv_start; timer_start(tv_start); auto nd = wallet.FindMyNotes(tx); return timer_stop(tv_start); }
TEST(wallet_tests, nullifier_is_spent) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); EXPECT_FALSE(wallet.IsSpent(nullifier)); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); auto wtx2 = GetValidSpend(sk, note, 5); wallet.AddToWallet(wtx2, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // Fake-mine the transaction EXPECT_EQ(-1, chainActive.Height()); CBlock block; block.vtx.push_back(wtx2); block.hashMerkleRoot = block.BuildMerkleTree(); auto blockHash = block.GetHash(); CBlockIndex fakeIndex {block}; mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); chainActive.SetTip(&fakeIndex); EXPECT_TRUE(chainActive.Contains(&fakeIndex)); EXPECT_EQ(0, chainActive.Height()); wtx2.SetMerkleBranch(block); wallet.AddToWallet(wtx2, true, NULL); EXPECT_TRUE(wallet.IsSpent(nullifier)); // Tear down chainActive.SetTip(NULL); mapBlockIndex.erase(blockHash); }
TEST(wallet_tests, find_unspent_notes) { SelectParams(CBaseChainParams::TESTNET); CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // We currently have an unspent and unconfirmed note in the wallet (depth of -1) std::vector<CNotePlaintextEntry> entries; wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(0, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", -1); EXPECT_EQ(1, entries.size()); entries.clear(); // Fake-mine the transaction EXPECT_EQ(-1, chainActive.Height()); CBlock block; block.vtx.push_back(wtx); block.hashMerkleRoot = block.BuildMerkleTree(); auto blockHash = block.GetHash(); CBlockIndex fakeIndex {block}; mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); chainActive.SetTip(&fakeIndex); EXPECT_TRUE(chainActive.Contains(&fakeIndex)); EXPECT_EQ(0, chainActive.Height()); wtx.SetMerkleBranch(block); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // We now have an unspent and confirmed note in the wallet (depth of 1) wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(1, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", 1); EXPECT_EQ(1, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", 2); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's spend the note. auto wtx2 = GetValidSpend(sk, note, 5); wallet.AddToWallet(wtx2, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // Fake-mine a spend transaction EXPECT_EQ(0, chainActive.Height()); CBlock block2; block2.vtx.push_back(wtx2); block2.hashMerkleRoot = block2.BuildMerkleTree(); block2.hashPrevBlock = blockHash; auto blockHash2 = block2.GetHash(); CBlockIndex fakeIndex2 {block2}; mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2)); fakeIndex2.nHeight = 1; chainActive.SetTip(&fakeIndex2); EXPECT_TRUE(chainActive.Contains(&fakeIndex2)); EXPECT_EQ(1, chainActive.Height()); wtx2.SetMerkleBranch(block2); wallet.AddToWallet(wtx2, true, NULL); EXPECT_TRUE(wallet.IsSpent(nullifier)); // The note has been spent. By default, GetFilteredNotes() ignores spent notes. wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's include spent notes to retrieve it. wallet.GetFilteredNotes(entries, "", 0, false); EXPECT_EQ(1, entries.size()); entries.clear(); // The spent note has two confirmations. wallet.GetFilteredNotes(entries, "", 2, false); EXPECT_EQ(1, entries.size()); entries.clear(); // It does not have 3 confirmations. wallet.GetFilteredNotes(entries, "", 3, false); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's receive a new note CWalletTx wtx3; { auto wtx = GetValidReceive(sk, 20, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); wtx3 = wtx; } // Fake-mine the new transaction EXPECT_EQ(1, chainActive.Height()); CBlock block3; block3.vtx.push_back(wtx3); block3.hashMerkleRoot = block3.BuildMerkleTree(); block3.hashPrevBlock = blockHash2; auto blockHash3 = block3.GetHash(); CBlockIndex fakeIndex3 {block3}; mapBlockIndex.insert(std::make_pair(blockHash3, &fakeIndex3)); fakeIndex3.nHeight = 2; chainActive.SetTip(&fakeIndex3); EXPECT_TRUE(chainActive.Contains(&fakeIndex3)); EXPECT_EQ(2, chainActive.Height()); wtx3.SetMerkleBranch(block3); wallet.AddToWallet(wtx3, true, NULL); // We now have an unspent note which has one confirmation, in addition to our spent note. wallet.GetFilteredNotes(entries, "", 1); EXPECT_EQ(1, entries.size()); entries.clear(); // Let's return the spent note too. wallet.GetFilteredNotes(entries, "", 1, false); EXPECT_EQ(2, entries.size()); entries.clear(); // Increasing number of confirmations will exclude our new unspent note. wallet.GetFilteredNotes(entries, "", 2, false); EXPECT_EQ(1, entries.size()); entries.clear(); // If we also ignore spent notes at thie depth, we won't find any notes. wallet.GetFilteredNotes(entries, "", 2, true); EXPECT_EQ(0, entries.size()); entries.clear(); // Tear down chainActive.SetTip(NULL); mapBlockIndex.erase(blockHash); mapBlockIndex.erase(blockHash2); mapBlockIndex.erase(blockHash3); }