Add test for Orchard wallet note detection.

This tests the following operations:
* add a spending key to the wallet
* add notes from the Orchard bundle component of a transaction to the
  wallet
* detect notes in the wallet that correspond to the spending key
* verify that notes that are not ours are not mistakenly labeled
  as ours
This commit is contained in:
Kris Nuttycombe 2022-02-15 12:53:02 -07:00
parent 4c53499f11
commit dcf7f46260
5 changed files with 152 additions and 64 deletions

View File

@ -59,6 +59,7 @@ zcash_gtest_SOURCES += \
gtest/test_zip32.cpp
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_orchard_wallet.cpp \
wallet/gtest/test_paymentdisclosure.cpp \
wallet/gtest/test_wallet.cpp
endif

View File

@ -7,6 +7,7 @@
#include "pubkey.h"
#include "rpc/protocol.h"
#include "transaction_builder.h"
#include "gtest/test_transaction_builder.h"
#include "utiltest.h"
#include "zcash/Address.hpp"
#include "zcash/address/mnemonic.h"
@ -16,69 +17,6 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
// Fake an empty view
class TransactionBuilderCoinsViewDB : public CCoinsView {
public:
std::map<uint256, SproutMerkleTree> sproutTrees;
TransactionBuilderCoinsViewDB() {}
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
auto it = sproutTrees.find(rt);
if (it != sproutTrees.end()) {
tree = it->second;
return true;
} else {
return false;
}
}
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
return false;
}
bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
return false;
}
bool GetCoins(const uint256 &txid, CCoins &coins) const {
return false;
}
bool HaveCoins(const uint256 &txid) const {
return false;
}
uint256 GetBestBlock() const {
uint256 a;
return a;
}
uint256 GetBestAnchor(ShieldedType type) const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashSproutAnchor,
const uint256 &hashSaplingAnchor,
CAnchorsSproutMap &mapSproutAnchors,
CAnchorsSaplingMap &mapSaplingAnchors,
CNullifiersMap &mapSproutNullifiers,
CNullifiersMap saplingNullifiersMap) {
return false;
}
bool GetStats(CCoinsStats &stats) const {
return false;
}
};
TEST(TransactionBuilder, TransparentToSapling)
{
auto consensusParams = RegtestActivateSapling();

View File

@ -0,0 +1,73 @@
// Copyright (c) 2022 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_GTEST_TEST_TRANSACTION_BUILDER_H
#define ZCASH_GTEST_TEST_TRANSACTION_BUILDER_H
#include "coins.h"
// Fake an empty view
class TransactionBuilderCoinsViewDB : public CCoinsView {
public:
std::map<uint256, SproutMerkleTree> sproutTrees;
TransactionBuilderCoinsViewDB() {}
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
auto it = sproutTrees.find(rt);
if (it != sproutTrees.end()) {
tree = it->second;
return true;
} else {
return false;
}
}
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
return false;
}
bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
return false;
}
bool GetCoins(const uint256 &txid, CCoins &coins) const {
return false;
}
bool HaveCoins(const uint256 &txid) const {
return false;
}
uint256 GetBestBlock() const {
uint256 a;
return a;
}
uint256 GetBestAnchor(ShieldedType type) const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashSproutAnchor,
const uint256 &hashSaplingAnchor,
CAnchorsSproutMap &mapSproutAnchors,
CAnchorsSaplingMap &mapSaplingAnchors,
CNullifiersMap &mapSproutNullifiers,
CNullifiersMap saplingNullifiersMap) {
return false;
}
bool GetStats(CCoinsStats &stats) const {
return false;
}
};
#endif

View File

@ -82,7 +82,8 @@ bool orchard_wallet_tx_contains_my_notes(
/**
* Add the specified spending key to the wallet's key store.
* This will also compute and add the associated incoming viewing key.
* This will also compute and add the associated full and
* incoming viewing keys.
*/
void orchard_wallet_add_spending_key(
OrchardWalletPtr* wallet,

View File

@ -0,0 +1,75 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "random.h"
#include "transaction_builder.h"
#include "utiltest.h"
#include "wallet/orchard.h"
#include "zcash/Address.hpp"
#include "gtest/test_transaction_builder.h"
#include <optional>
using namespace libzcash;
OrchardSpendingKey RandomOrchardSpendingKey() {
auto coinType = Params().BIP44CoinType() ;
auto seed = MnemonicSeed::Random(coinType);
return OrchardSpendingKey::ForAccount(seed, coinType, 0);
}
CTransaction FakeOrchardTx(const OrchardSpendingKey& sk, libzcash::diversifier_index_t j) {
CBasicKeyStore keystore;
CKey tsk = AddTestCKeyToKeyStore(keystore);
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
auto fvk = sk.ToFullViewingKey();
auto ivk = fvk.ToIncomingViewingKey();
auto recipient = ivk.Address(j);
TransactionBuilderCoinsViewDB fakeDB;
auto orchardAnchor = fakeDB.GetBestAnchor(ShieldedType::ORCHARD);
// Create a shielding transaction from transparent to Orchard
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee
auto builder = TransactionBuilder(Params().GetConsensus(), 1, orchardAnchor, &keystore);
builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 50000);
builder.AddOrchardOutput(std::nullopt, recipient, 40000, std::nullopt);
auto maybeTx = builder.Build();
EXPECT_TRUE(maybeTx.IsTx());
return maybeTx.GetTxOrThrow();
}
TEST(OrchardWalletTests, TxContainsMyNotes) {
auto consensusParams = RegtestActivateNU5();
OrchardWallet wallet;
// Add a new spending key to the wallet
auto sk = RandomOrchardSpendingKey();
wallet.AddSpendingKey(sk);
// Create a transaction sending to the default address for that
// spending key and add it to the wallet.
auto tx = FakeOrchardTx(sk, libzcash::diversifier_index_t(0));
wallet.AddNotesIfInvolvingMe(tx);
// Check that we detect the transaction as ours
EXPECT_TRUE(wallet.TxContainsMyNotes(tx.GetHash()));
// Create a transaction sending to a different diversified address
auto tx1 = FakeOrchardTx(sk, libzcash::diversifier_index_t(0xffffffffffffffff));
wallet.AddNotesIfInvolvingMe(tx1);
// Check that we also detect this transaction as ours
EXPECT_TRUE(wallet.TxContainsMyNotes(tx1.GetHash()));
// Now generate a new key, and send a transaction to it without adding
// the key to the wallet; it should not be detected as ours.
auto skNotOurs = RandomOrchardSpendingKey();
auto tx2 = FakeOrchardTx(skNotOurs, libzcash::diversifier_index_t(0));
wallet.AddNotesIfInvolvingMe(tx2);
EXPECT_FALSE(wallet.TxContainsMyNotes(tx2.GetHash()));
}