The wallet now only stores Sapling extended spending keys, and thus can only be used with keys generated from an HDSeed via ZIP 32. Note that not all Sapling keys in the wallet will correspond to the wallet's HDSeed, as a standalone Sapling xsk can be imported via z_importkey. However, it must have been generated from a seed itself, and thus is more likely to be backed up elsewhere.
366 lines
11 KiB
C++
366 lines
11 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "zcash/Address.hpp"
|
|
#include "wallet/wallet.h"
|
|
#include "wallet/walletdb.h"
|
|
#include "util.h"
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
/**
|
|
* This test covers Sapling methods on CWallet
|
|
* GenerateNewSaplingZKey()
|
|
*/
|
|
TEST(wallet_zkeys_tests, store_and_load_sapling_zkeys) {
|
|
SelectParams(CBaseChainParams::MAIN);
|
|
|
|
CWallet wallet;
|
|
|
|
// wallet should be empty
|
|
std::set<libzcash::SaplingPaymentAddress> addrs;
|
|
wallet.GetSaplingPaymentAddresses(addrs);
|
|
ASSERT_EQ(0, addrs.size());
|
|
|
|
// No HD seed in the wallet
|
|
EXPECT_ANY_THROW(wallet.GenerateNewSaplingZKey());
|
|
|
|
// Load the all-zeroes seed
|
|
CKeyingMaterial rawSeed(32, 0);
|
|
HDSeed seed(rawSeed);
|
|
wallet.LoadHDSeed(seed);
|
|
|
|
// Now this call succeeds
|
|
auto address = wallet.GenerateNewSaplingZKey();
|
|
|
|
// wallet should have one key
|
|
wallet.GetSaplingPaymentAddresses(addrs);
|
|
ASSERT_EQ(1, addrs.size());
|
|
|
|
// verify wallet has incoming viewing key for the address
|
|
ASSERT_TRUE(wallet.HaveSaplingIncomingViewingKey(address));
|
|
|
|
// manually add new spending key to wallet
|
|
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
|
auto sk = m.Derive(0);
|
|
ASSERT_TRUE(wallet.AddSaplingZKey(sk, sk.DefaultAddress()));
|
|
|
|
// verify wallet did add it
|
|
auto fvk = sk.expsk.full_viewing_key();
|
|
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
|
|
|
|
// verify spending key stored correctly
|
|
libzcash::SaplingExtendedSpendingKey keyOut;
|
|
wallet.GetSaplingSpendingKey(fvk, keyOut);
|
|
ASSERT_EQ(sk, keyOut);
|
|
|
|
// verify there are two keys
|
|
wallet.GetSaplingPaymentAddresses(addrs);
|
|
EXPECT_EQ(2, addrs.size());
|
|
EXPECT_EQ(1, addrs.count(address));
|
|
EXPECT_EQ(1, addrs.count(sk.DefaultAddress()));
|
|
}
|
|
|
|
/**
|
|
* This test covers methods on CWallet
|
|
* GenerateNewZKey()
|
|
* AddZKey()
|
|
* LoadZKey()
|
|
* LoadZKeyMetadata()
|
|
*/
|
|
TEST(wallet_zkeys_tests, store_and_load_zkeys) {
|
|
SelectParams(CBaseChainParams::MAIN);
|
|
|
|
CWallet wallet;
|
|
|
|
// wallet should be empty
|
|
std::set<libzcash::SproutPaymentAddress> addrs;
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(0, addrs.size());
|
|
|
|
// wallet should have one key
|
|
auto address = wallet.GenerateNewZKey();
|
|
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address), nullptr);
|
|
auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(1, addrs.size());
|
|
|
|
// verify wallet has spending key for the address
|
|
ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr));
|
|
|
|
// manually add new spending key to wallet
|
|
auto sk = libzcash::SproutSpendingKey::random();
|
|
ASSERT_TRUE(wallet.AddZKey(sk));
|
|
|
|
// verify wallet did add it
|
|
addr = sk.address();
|
|
ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr));
|
|
|
|
// verify spending key stored correctly
|
|
libzcash::SproutSpendingKey keyOut;
|
|
wallet.GetSproutSpendingKey(addr, keyOut);
|
|
ASSERT_EQ(sk, keyOut);
|
|
|
|
// verify there are two keys
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(2, addrs.size());
|
|
ASSERT_EQ(1, addrs.count(addr));
|
|
|
|
// Load a third key into the wallet
|
|
sk = libzcash::SproutSpendingKey::random();
|
|
ASSERT_TRUE(wallet.LoadZKey(sk));
|
|
|
|
// attach metadata to this third key
|
|
addr = sk.address();
|
|
int64_t now = GetTime();
|
|
CKeyMetadata meta(now);
|
|
ASSERT_TRUE(wallet.LoadZKeyMetadata(addr, meta));
|
|
|
|
// check metadata is the same
|
|
CKeyMetadata m= wallet.mapZKeyMetadata[addr];
|
|
ASSERT_EQ(m.nCreateTime, now);
|
|
}
|
|
|
|
/**
|
|
* This test covers methods on CWallet
|
|
* AddSproutViewingKey()
|
|
* RemoveSproutViewingKey()
|
|
* LoadSproutViewingKey()
|
|
*/
|
|
TEST(wallet_zkeys_tests, StoreAndLoadViewingKeys) {
|
|
SelectParams(CBaseChainParams::MAIN);
|
|
|
|
CWallet wallet;
|
|
|
|
// wallet should be empty
|
|
std::set<libzcash::SproutPaymentAddress> addrs;
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(0, addrs.size());
|
|
|
|
// manually add new viewing key to wallet
|
|
auto sk = libzcash::SproutSpendingKey::random();
|
|
auto vk = sk.viewing_key();
|
|
ASSERT_TRUE(wallet.AddSproutViewingKey(vk));
|
|
|
|
// verify wallet did add it
|
|
auto addr = sk.address();
|
|
ASSERT_TRUE(wallet.HaveSproutViewingKey(addr));
|
|
// and that we don't have the corresponding spending key
|
|
ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr));
|
|
|
|
// verify viewing key stored correctly
|
|
libzcash::SproutViewingKey vkOut;
|
|
wallet.GetSproutViewingKey(addr, vkOut);
|
|
ASSERT_EQ(vk, vkOut);
|
|
|
|
// Load a second viewing key into the wallet
|
|
auto sk2 = libzcash::SproutSpendingKey::random();
|
|
ASSERT_TRUE(wallet.LoadSproutViewingKey(sk2.viewing_key()));
|
|
|
|
// verify wallet did add it
|
|
auto addr2 = sk2.address();
|
|
ASSERT_TRUE(wallet.HaveSproutViewingKey(addr2));
|
|
ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr2));
|
|
|
|
// Remove the first viewing key
|
|
ASSERT_TRUE(wallet.RemoveSproutViewingKey(vk));
|
|
ASSERT_FALSE(wallet.HaveSproutViewingKey(addr));
|
|
ASSERT_TRUE(wallet.HaveSproutViewingKey(addr2));
|
|
}
|
|
|
|
/**
|
|
* This test covers methods on CWalletDB
|
|
* WriteZKey()
|
|
*/
|
|
TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
|
|
SelectParams(CBaseChainParams::TESTNET);
|
|
|
|
// Get temporary and unique path for file.
|
|
// Note: / operator to append paths
|
|
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
boost::filesystem::create_directories(pathTemp);
|
|
mapArgs["-datadir"] = pathTemp.string();
|
|
|
|
bool fFirstRun;
|
|
CWallet wallet("wallet.dat");
|
|
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
|
|
|
// No default CPubKey set
|
|
ASSERT_TRUE(fFirstRun);
|
|
|
|
// wallet should be empty
|
|
std::set<libzcash::SproutPaymentAddress> addrs;
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(0, addrs.size());
|
|
|
|
// Add random key to the wallet
|
|
auto paymentAddress = wallet.GenerateNewZKey();
|
|
|
|
// wallet should have one key
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(1, addrs.size());
|
|
|
|
// create random key and add it to database directly, bypassing wallet
|
|
auto sk = libzcash::SproutSpendingKey::random();
|
|
auto addr = sk.address();
|
|
int64_t now = GetTime();
|
|
CKeyMetadata meta(now);
|
|
CWalletDB db("wallet.dat");
|
|
db.WriteZKey(addr, sk, meta);
|
|
|
|
// wallet should not be aware of key
|
|
ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr));
|
|
|
|
// wallet sees one key
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(1, addrs.size());
|
|
|
|
// wallet should have default metadata for addr with null createtime
|
|
CKeyMetadata m = wallet.mapZKeyMetadata[addr];
|
|
ASSERT_EQ(m.nCreateTime, 0);
|
|
ASSERT_NE(m.nCreateTime, now);
|
|
|
|
// load the wallet again
|
|
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
|
|
|
// wallet can now see the spending key
|
|
ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr));
|
|
|
|
// check key is the same
|
|
libzcash::SproutSpendingKey keyOut;
|
|
wallet.GetSproutSpendingKey(addr, keyOut);
|
|
ASSERT_EQ(sk, keyOut);
|
|
|
|
// wallet should have two keys
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(2, addrs.size());
|
|
|
|
// check metadata is now the same
|
|
m = wallet.mapZKeyMetadata[addr];
|
|
ASSERT_EQ(m.nCreateTime, now);
|
|
}
|
|
|
|
/**
|
|
* This test covers methods on CWalletDB
|
|
* WriteSproutViewingKey()
|
|
*/
|
|
TEST(wallet_zkeys_tests, WriteViewingKeyDirectToDB) {
|
|
SelectParams(CBaseChainParams::TESTNET);
|
|
|
|
// Get temporary and unique path for file.
|
|
// Note: / operator to append paths
|
|
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
boost::filesystem::create_directories(pathTemp);
|
|
mapArgs["-datadir"] = pathTemp.string();
|
|
|
|
bool fFirstRun;
|
|
CWallet wallet("wallet-vkey.dat");
|
|
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
|
|
|
// No default CPubKey set
|
|
ASSERT_TRUE(fFirstRun);
|
|
|
|
// create random viewing key and add it to database directly, bypassing wallet
|
|
auto sk = libzcash::SproutSpendingKey::random();
|
|
auto vk = sk.viewing_key();
|
|
auto addr = sk.address();
|
|
int64_t now = GetTime();
|
|
CKeyMetadata meta(now);
|
|
CWalletDB db("wallet-vkey.dat");
|
|
db.WriteSproutViewingKey(vk);
|
|
|
|
// wallet should not be aware of viewing key
|
|
ASSERT_FALSE(wallet.HaveSproutViewingKey(addr));
|
|
|
|
// load the wallet again
|
|
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
|
|
|
// wallet can now see the viewing key
|
|
ASSERT_TRUE(wallet.HaveSproutViewingKey(addr));
|
|
|
|
// check key is the same
|
|
libzcash::SproutViewingKey vkOut;
|
|
wallet.GetSproutViewingKey(addr, vkOut);
|
|
ASSERT_EQ(vk, vkOut);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* This test covers methods on CWalletDB to load/save crypted z keys.
|
|
*/
|
|
TEST(wallet_zkeys_tests, write_cryptedzkey_direct_to_db) {
|
|
SelectParams(CBaseChainParams::TESTNET);
|
|
|
|
// Get temporary and unique path for file.
|
|
// Note: / operator to append paths
|
|
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
boost::filesystem::create_directories(pathTemp);
|
|
mapArgs["-datadir"] = pathTemp.string();
|
|
|
|
bool fFirstRun;
|
|
CWallet wallet("wallet_crypted.dat");
|
|
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
|
|
|
// No default CPubKey set
|
|
ASSERT_TRUE(fFirstRun);
|
|
|
|
// wallet should be empty
|
|
std::set<libzcash::SproutPaymentAddress> addrs;
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(0, addrs.size());
|
|
|
|
// Add random key to the wallet
|
|
auto address = wallet.GenerateNewZKey();
|
|
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address), nullptr);
|
|
auto paymentAddress = boost::get<libzcash::SproutPaymentAddress>(address);
|
|
|
|
// wallet should have one key
|
|
wallet.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(1, addrs.size());
|
|
|
|
// encrypt wallet
|
|
SecureString strWalletPass;
|
|
strWalletPass.reserve(100);
|
|
strWalletPass = "hello";
|
|
ASSERT_TRUE(wallet.EncryptWallet(strWalletPass));
|
|
|
|
// adding a new key will fail as the wallet is locked
|
|
EXPECT_ANY_THROW(wallet.GenerateNewZKey());
|
|
|
|
// unlock wallet and then add
|
|
wallet.Unlock(strWalletPass);
|
|
auto address2 = wallet.GenerateNewZKey();
|
|
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address2), nullptr);
|
|
auto paymentAddress2 = boost::get<libzcash::SproutPaymentAddress>(address2);
|
|
|
|
// Create a new wallet from the existing wallet path
|
|
CWallet wallet2("wallet_crypted.dat");
|
|
ASSERT_EQ(DB_LOAD_OK, wallet2.LoadWallet(fFirstRun));
|
|
|
|
// Confirm it's not the same as the other wallet
|
|
ASSERT_TRUE(&wallet != &wallet2);
|
|
|
|
// wallet should have two keys
|
|
wallet2.GetSproutPaymentAddresses(addrs);
|
|
ASSERT_EQ(2, addrs.size());
|
|
|
|
// check we have entries for our payment addresses
|
|
ASSERT_TRUE(addrs.count(paymentAddress));
|
|
ASSERT_TRUE(addrs.count(paymentAddress2));
|
|
|
|
// spending key is crypted, so we can't extract valid payment address
|
|
libzcash::SproutSpendingKey keyOut;
|
|
wallet2.GetSproutSpendingKey(paymentAddress, keyOut);
|
|
ASSERT_FALSE(paymentAddress == keyOut.address());
|
|
|
|
// unlock wallet to get spending keys and verify payment addresses
|
|
wallet2.Unlock(strWalletPass);
|
|
|
|
wallet2.GetSproutSpendingKey(paymentAddress, keyOut);
|
|
ASSERT_EQ(paymentAddress, keyOut.address());
|
|
|
|
wallet2.GetSproutSpendingKey(paymentAddress2, keyOut);
|
|
ASSERT_EQ(paymentAddress2, keyOut.address());
|
|
}
|
|
|