Browse Source

wallet: Switch from SaplingSpendingKey to SaplingExtendedSpendingKey

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.
metaverse
Jack Grigg 6 years ago
parent
commit
70b4ad2dcd
No known key found for this signature in database GPG Key ID: 1B8D649257DB0829
  1. 6
      src/chainparams.cpp
  2. 2
      src/chainparams.h
  3. 17
      src/gtest/test_keys.cpp
  4. 10
      src/gtest/test_keystore.cpp
  5. 12
      src/key_io.cpp
  6. 1
      src/key_io.h
  7. 4
      src/keystore.cpp
  8. 11
      src/keystore.h
  9. 16
      src/test/key_tests.cpp
  10. 10
      src/test/rpc_wallet_tests.cpp
  11. 4
      src/wallet/asyncrpcoperation_sendmany.cpp
  12. 18
      src/wallet/crypter.cpp
  13. 4
      src/wallet/crypter.h
  14. 89
      src/wallet/gtest/test_wallet.cpp
  15. 23
      src/wallet/gtest/test_wallet_zkeys.cpp
  16. 10
      src/wallet/rpcdump.cpp
  17. 1
      src/wallet/rpcwallet.cpp
  18. 55
      src/wallet/wallet.cpp
  19. 2
      src/wallet/wallet.h
  20. 4
      src/zcash/Address.cpp
  21. 4
      src/zcash/Address.hpp
  22. 4
      src/zcash/zip32.cpp
  23. 5
      src/zcash/zip32.h

6
src/chainparams.cpp

@ -158,7 +158,7 @@ public:
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-main";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
@ -329,7 +329,7 @@ public:
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-test";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
@ -457,7 +457,7 @@ public:
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-regtest";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest";
// Founders reward script expects a vector of 2-of-3 multisig addresses
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" };

2
src/chainparams.h

@ -60,7 +60,7 @@ public:
SAPLING_PAYMENT_ADDRESS,
SAPLING_FULL_VIEWING_KEY,
SAPLING_INCOMING_VIEWING_KEY,
SAPLING_SPENDING_KEY,
SAPLING_EXTENDED_SPEND_KEY,
MAX_BECH32_TYPES
};

17
src/gtest/test_keys.cpp

@ -1,6 +1,7 @@
#include <chainparams.h>
#include <key_io.h>
#include <zcash/Address.hpp>
#include <zcash/zip32.h>
#include <gtest/gtest.h>
@ -8,23 +9,27 @@ TEST(Keys, DISABLED_EncodeAndDecodeSapling)
{
SelectParams(CBaseChainParams::MAIN);
for (size_t i = 0; i < 1000; i++) {
auto sk = libzcash::SaplingSpendingKey::random();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
for (uint32_t i = 0; i < 1000; i++) {
auto sk = m.Derive(i);
{
std::string sk_string = EncodeSpendingKey(sk);
EXPECT_EQ(
sk_string.substr(0, 24),
Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY));
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY));
auto spendingkey2 = DecodeSpendingKey(sk_string);
EXPECT_TRUE(IsValidSpendingKey(spendingkey2));
ASSERT_TRUE(boost::get<libzcash::SaplingSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<libzcash::SaplingSpendingKey>(spendingkey2);
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
EXPECT_EQ(sk, sk2);
}
{
auto addr = sk.default_address();
auto addr = sk.DefaultAddress();
std::string addr_string = EncodePaymentAddress(addr);
EXPECT_EQ(

10
src/gtest/test_keystore.cpp

@ -194,14 +194,16 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) {
// Sapling
TEST(keystore_tests, StoreAndRetrieveSaplingSpendingKey) {
CBasicKeyStore keyStore;
libzcash::SaplingSpendingKey skOut;
libzcash::SaplingExtendedSpendingKey skOut;
libzcash::SaplingFullViewingKey fvkOut;
libzcash::SaplingIncomingViewingKey ivkOut;
auto sk = libzcash::SaplingSpendingKey::random();
auto fvk = sk.full_viewing_key();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto fvk = sk.expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.default_address();
auto addr = sk.DefaultAddress();
// Sanity-check: we can't get a key we haven't added
EXPECT_FALSE(keyStore.HaveSaplingSpendingKey(fvk));

12
src/key_io.cpp

@ -142,7 +142,7 @@ public:
return ret;
}
std::string operator()(const libzcash::SaplingSpendingKey& zkey) const
std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zkey;
@ -152,7 +152,7 @@ public:
// See calculation comment below
data.reserve((serkey.size() * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_SPENDING_KEY), data);
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY), data);
memory_cleanse(serkey.data(), serkey.size());
memory_cleanse(data.data(), data.size());
return ret;
@ -166,7 +166,7 @@ public:
// regular serialized size in bytes, convert to bits, and then
// perform ceiling division to get the number of 5-bit clusters.
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
const size_t ConvertedSaplingSpendingKeySize = (32 * 8 + 4) / 5;
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
} // namespace
CKey DecodeSecret(const std::string& str)
@ -360,13 +360,13 @@ libzcash::SpendingKey DecodeSpendingKey(const std::string& str)
Params().NetworkIDString() == "test" &&
GetBoolArg("-experimentalfeatures", false) &&
GetBoolArg("-developersapling", false));
if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY) &&
bech.second.size() == ConvertedSaplingSpendingKeySize) {
if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY) &&
bech.second.size() == ConvertedSaplingExtendedSpendingKeySize) {
// Bech32 decoding
data.reserve((bech.second.size() * 5) / 8);
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
libzcash::SaplingSpendingKey ret;
libzcash::SaplingExtendedSpendingKey ret;
ss >> ret;
memory_cleanse(data.data(), data.size());
return ret;

1
src/key_io.h

@ -12,6 +12,7 @@
#include <pubkey.h>
#include <script/standard.h>
#include <zcash/Address.hpp>
#include <zcash/zip32.h>
#include <string>

4
src/keystore.cpp

@ -119,11 +119,11 @@ bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk)
//! Sapling
bool CBasicKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
{
LOCK(cs_SpendingKeyStore);
auto fvk = sk.full_viewing_key();
auto fvk = sk.expsk.full_viewing_key();
// if SaplingFullViewingKey is not in SaplingFullViewingKeyMap, add it
if (!AddSaplingFullViewingKey(fvk, defaultAddr)){

11
src/keystore.h

@ -65,12 +65,12 @@ public:
//! Add a Sapling spending key to the store.
virtual bool AddSaplingSpendingKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none) =0;
//! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store.
virtual bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const =0;
virtual bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingSpendingKey& skOut) const =0;
virtual bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey& skOut) const =0;
//! Support for Sapling full viewing keys
virtual bool AddSaplingFullViewingKey(
@ -106,8 +106,7 @@ typedef std::map<libzcash::SproutPaymentAddress, ZCNoteDecryption> NoteDecryptor
// Full viewing key has equivalent functionality to a transparent address
// When encrypting wallet, encrypt SaplingSpendingKeyMap, while leaving SaplingFullViewingKeyMap unencrypted
// When implementing ZIP 32, add another map from SaplingFullViewingKey -> SaplingExpandedSpendingKey
typedef std::map<libzcash::SaplingFullViewingKey, libzcash::SaplingSpendingKey> SaplingSpendingKeyMap;
typedef std::map<libzcash::SaplingFullViewingKey, libzcash::SaplingExtendedSpendingKey> SaplingSpendingKeyMap;
typedef std::map<libzcash::SaplingIncomingViewingKey, libzcash::SaplingFullViewingKey> SaplingFullViewingKeyMap;
// Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses.
typedef std::map<libzcash::SaplingPaymentAddress, libzcash::SaplingIncomingViewingKey> SaplingIncomingViewingKeyMap;
@ -236,7 +235,7 @@ public:
//! Sapling
bool AddSaplingSpendingKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const
{
@ -247,7 +246,7 @@ public:
}
return result;
}
bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingSpendingKey &skOut) const
bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const
{
{
LOCK(cs_SpendingKeyStore);

16
src/test/key_tests.cpp

@ -225,21 +225,25 @@ BOOST_AUTO_TEST_CASE(zs_address_test)
{
SelectParams(CBaseChainParams::REGTEST);
for (size_t i = 0; i < 1000; i++) {
auto sk = SaplingSpendingKey::random();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
for (uint32_t i = 0; i < 1000; i++) {
auto sk = m.Derive(i);
{
std::string sk_string = EncodeSpendingKey(sk);
BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY)) == 0);
BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0);
auto spendingkey2 = DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_ASSERT(boost::get<SaplingSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<SaplingSpendingKey>(spendingkey2);
BOOST_ASSERT(boost::get<SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<SaplingExtendedSpendingKey>(spendingkey2);
BOOST_CHECK(sk == sk2);
}
{
auto addr = sk.default_address();
auto addr = sk.DefaultAddress();
std::string addr_string = EncodePaymentAddress(addr);
BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);

10
src/test/rpc_wallet_tests.cpp

@ -568,6 +568,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
pwalletMain->GetSaplingPaymentAddresses(saplingAddrs);
BOOST_CHECK(saplingAddrs.empty());
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
// verify import and export key
for (int i = 0; i < n1; i++) {
// create a random Sprout key locally
@ -578,10 +582,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testKey));
BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testAddr));
BOOST_CHECK_EQUAL(retValue.get_str(), testKey);
// create a random Sapling key locally
auto testSaplingSpendingKey = libzcash::SaplingSpendingKey::random();
auto testSaplingPaymentAddress = testSaplingSpendingKey.default_address();
auto testSaplingSpendingKey = m.Derive(i);
auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress();
std::string testSaplingAddr = EncodePaymentAddress(testSaplingPaymentAddress);
std::string testSaplingKey = EncodeSpendingKey(testSaplingSpendingKey);
BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey));

4
src/wallet/asyncrpcoperation_sendmany.cpp

@ -382,8 +382,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
SaplingExpandedSpendingKey expsk;
SaplingFullViewingKey from;
if (isfromzaddr_) {
auto sk = boost::get<libzcash::SaplingSpendingKey>(spendingkey_);
expsk = sk.expanded_spending_key();
auto sk = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
expsk = sk.expsk;
from = expsk.full_viewing_key();
} else {
// TODO: Set "from" to something!

18
src/wallet/crypter.cpp

@ -171,18 +171,18 @@ static bool DecryptSproutSpendingKey(const CKeyingMaterial& vMasterKey,
static bool DecryptSaplingSpendingKey(const CKeyingMaterial& vMasterKey,
const std::vector<unsigned char>& vchCryptedSecret,
const libzcash::SaplingFullViewingKey& fvk,
libzcash::SaplingSpendingKey& sk)
libzcash::SaplingExtendedSpendingKey& sk)
{
CKeyingMaterial vchSecret;
if (!DecryptSecret(vMasterKey, vchCryptedSecret, fvk.GetFingerprint(), vchSecret))
return false;
if (vchSecret.size() != libzcash::SerializedSaplingSpendingKeySize)
if (vchSecret.size() != ZIP32_XSK_SIZE)
return false;
CSecureDataStream ss(vchSecret, SER_NETWORK, PROTOCOL_VERSION);
ss >> sk;
return sk.full_viewing_key() == fvk;
return sk.expsk.full_viewing_key() == fvk;
}
bool CCryptoKeyStore::SetCrypted()
@ -263,7 +263,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
{
const libzcash::SaplingFullViewingKey &fvk = (*miSapling).first;
const std::vector<unsigned char> &vchCryptedSecret = (*miSapling).second;
libzcash::SaplingSpendingKey sk;
libzcash::SaplingExtendedSpendingKey sk;
if (!DecryptSaplingSpendingKey(vMasterKeyIn, vchCryptedSecret, fvk, sk))
{
keyFail = true;
@ -442,7 +442,7 @@ bool CCryptoKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk
}
bool CCryptoKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
{
{
@ -459,7 +459,7 @@ bool CCryptoKeyStore::AddSaplingSpendingKey(
CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
auto fvk = sk.full_viewing_key();
auto fvk = sk.expsk.full_viewing_key();
if (!EncryptSecret(vMasterKey, vchSecret, fvk.GetFingerprint(), vchCryptedSecret)) {
return false;
}
@ -525,7 +525,7 @@ bool CCryptoKeyStore::GetSproutSpendingKey(const libzcash::SproutPaymentAddress
return false;
}
bool CCryptoKeyStore::GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingSpendingKey &skOut) const
bool CCryptoKeyStore::GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const
{
{
LOCK(cs_SpendingKeyStore);
@ -597,11 +597,11 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
//! Sapling key support
BOOST_FOREACH(SaplingSpendingKeyMap::value_type& mSaplingSpendingKey, mapSaplingSpendingKeys)
{
const libzcash::SaplingSpendingKey &sk = mSaplingSpendingKey.second;
const auto &sk = mSaplingSpendingKey.second;
CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
libzcash::SaplingFullViewingKey fvk = sk.full_viewing_key();
libzcash::SaplingFullViewingKey fvk = sk.expsk.full_viewing_key();
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, fvk.GetFingerprint(), vchCryptedSecret)) {
return false;

4
src/wallet/crypter.h

@ -245,7 +245,7 @@ public:
const std::vector<unsigned char> &vchCryptedSecret,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
bool AddSaplingSpendingKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const
{
@ -257,7 +257,7 @@ public:
}
return false;
}
bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingSpendingKey &skOut) const;
bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const;
/**

89
src/wallet/gtest/test_wallet.cpp

@ -363,11 +363,13 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
TestWallet wallet;
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto pk = sk.DefaultAddress();
libzcash::SaplingNote note(pk, 50000);
auto cm = note.cm().get();
@ -484,10 +486,12 @@ TEST(WalletTests, FindMySaplingNotes) {
TestWallet wallet;
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto pk = sk.DefaultAddress();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
@ -618,11 +622,13 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
TestWallet wallet;
// Generate Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto pk = sk.default_address();
auto pk = sk.DefaultAddress();
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
@ -785,10 +791,12 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
TestWallet wallet;
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto pk = sk.DefaultAddress();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
@ -880,10 +888,12 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
TestWallet wallet;
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto pk = sk.DefaultAddress();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
@ -1013,11 +1023,13 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
TestWallet wallet;
// Generate Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto pk = sk.default_address();
auto pk = sk.DefaultAddress();
// Generate Sapling note A
libzcash::SaplingNote note(pk, 50000);
@ -1705,16 +1717,21 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
TestWallet wallet;
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
auto sk = m.Derive(0);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto pk = sk.DefaultAddress();
// Generate dummy recipient Sapling address
auto sk2 = libzcash::SaplingSpendingKey::random();
auto fvk2 = sk2.full_viewing_key();
auto pk2 = sk2.default_address();
auto sk2 = m.Derive(1);
auto expsk2 = sk2.expsk;
auto fvk2 = expsk2.full_viewing_key();
auto pk2 = sk2.DefaultAddress();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
@ -1856,11 +1873,13 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
TestWallet wallet;
// Generate Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto pk = sk.default_address();
auto pk = sk.DefaultAddress();
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));

23
src/wallet/gtest/test_wallet_zkeys.cpp

@ -21,8 +21,18 @@ TEST(wallet_zkeys_tests, store_and_load_sapling_zkeys) {
wallet.GetSaplingPaymentAddresses(addrs);
ASSERT_EQ(0, addrs.size());
// wallet should have one key
// 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());
@ -30,15 +40,16 @@ TEST(wallet_zkeys_tests, store_and_load_sapling_zkeys) {
ASSERT_TRUE(wallet.HaveSaplingIncomingViewingKey(address));
// manually add new spending key to wallet
auto sk = libzcash::SaplingSpendingKey::random();
ASSERT_TRUE(wallet.AddSaplingZKey(sk, sk.default_address()));
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.full_viewing_key();
auto fvk = sk.expsk.full_viewing_key();
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// verify spending key stored correctly
libzcash::SaplingSpendingKey keyOut;
libzcash::SaplingExtendedSpendingKey keyOut;
wallet.GetSaplingSpendingKey(fvk, keyOut);
ASSERT_EQ(sk, keyOut);
@ -46,7 +57,7 @@ TEST(wallet_zkeys_tests, store_and_load_sapling_zkeys) {
wallet.GetSaplingPaymentAddresses(addrs);
EXPECT_EQ(2, addrs.size());
EXPECT_EQ(1, addrs.count(address));
EXPECT_EQ(1, addrs.count(sk.default_address()));
EXPECT_EQ(1, addrs.count(sk.DefaultAddress()));
}
/**

10
src/wallet/rpcdump.cpp

@ -576,18 +576,18 @@ public:
return false;
}
}
bool operator()(const libzcash::SaplingSpendingKey &sk) const {
auto fvk = sk.full_viewing_key();
bool operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto fvk = sk.expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.default_address();
auto addr = sk.DefaultAddress();
{
// Don't throw error in case a key is already there
if (m_wallet->HaveSaplingSpendingKey(fvk)) {
return true;
} else {
m_wallet->MarkDirty();
if (!m_wallet-> AddSaplingZKey(sk, addr)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}

1
src/wallet/rpcwallet.cpp

@ -21,6 +21,7 @@
#include "primitives/transaction.h"
#include "zcbenchmarks.h"
#include "script/interpreter.h"
#include "zcash/zip32.h"
#include "utiltime.h"
#include "asyncrpcoperation.h"

55
src/wallet/wallet.cpp

@ -20,6 +20,7 @@
#include "utilmoneystr.h"
#include "zcash/Note.hpp"
#include "crypter.h"
#include "zcash/zip32.h"
#include <assert.h>
@ -104,22 +105,42 @@ libzcash::PaymentAddress CWallet::GenerateNewZKey()
SaplingPaymentAddress CWallet::GenerateNewSaplingZKey()
{
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
auto sk = SaplingSpendingKey::random();
auto fvk = sk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.default_address();
// Check for collision, even though it is unlikely to ever occur
if (CCryptoKeyStore::HaveSaplingSpendingKey(fvk)) {
throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Collision detected");
}
// Create new metadata
int64_t nCreationTime = GetTime();
mapSaplingZKeyMetadata[ivk] = CKeyMetadata(nCreationTime);
CKeyMetadata metadata(nCreationTime);
// Try to get the seed
HDSeed seed;
if (!GetHDSeed(seed))
throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found");
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
// We use a fixed keypath scheme of m/32'/coin_type'/account'
// Derive m/32'
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
// Derive m/32'/coin_type'
auto m_32h_cth = m_32h.Derive(Params().BIP44CoinType() | ZIP32_HARDENED_KEY_LIMIT);
// Derive account key at next index, skip keys already known to the wallet
libzcash::SaplingExtendedSpendingKey xsk;
do
{
xsk = m_32h_cth.Derive(hdChain.saplingAccountCounter | ZIP32_HARDENED_KEY_LIMIT);
// Increment childkey index
hdChain.saplingAccountCounter++;
} while (HaveSaplingSpendingKey(xsk.expsk.full_viewing_key()));
// Update the chain model in the database
if (fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(hdChain))
throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed");
auto ivk = xsk.expsk.full_viewing_key().in_viewing_key();
mapSaplingZKeyMetadata[ivk] = metadata;
if (!AddSaplingZKey(sk, addr)) {
auto addr = xsk.DefaultAddress();
if (!AddSaplingZKey(xsk, addr)) {
throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed");
}
// return default sapling payment address.
@ -128,7 +149,7 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey()
// Add spending key to keystore
bool CWallet::AddSaplingZKey(
const libzcash::SaplingSpendingKey &sk,
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
{
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
@ -4450,8 +4471,8 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAdd
{
libzcash::SaplingIncomingViewingKey ivk;
// If we have a SaplingSpendingKey or SaplingExpandedSpendingKey in the
// wallet, then we will also have the corresponding SaplingFullViewingKey.
// If we have a SaplingExtendedSpendingKey in the wallet, then we will
// also have the corresponding SaplingFullViewingKey.
return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
m_wallet->HaveSaplingFullViewingKey(ivk);
}
@ -4497,7 +4518,7 @@ boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator
{
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
libzcash::SaplingSpendingKey sk;
libzcash::SaplingExtendedSpendingKey sk;
if (m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
m_wallet->GetSaplingFullViewingKey(ivk, fvk) &&

2
src/wallet/wallet.h

@ -1052,7 +1052,7 @@ public:
libzcash::SaplingPaymentAddress GenerateNewSaplingZKey();
//! Adds Sapling spending key to the store, and saves it to disk
bool AddSaplingZKey(
const libzcash::SaplingSpendingKey &key,
const libzcash::SaplingExtendedSpendingKey &key,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
bool AddCryptedSaplingSpendingKey(
const libzcash::SaplingFullViewingKey &fvk,

4
src/zcash/Address.cpp

@ -118,7 +118,3 @@ bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) {
bool IsValidViewingKey(const libzcash::ViewingKey& vk) {
return vk.which() != 0;
}
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) {
return zkey.which() != 0;
}

4
src/zcash/Address.hpp

@ -220,7 +220,6 @@ public:
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingSpendingKey> SpendingKey;
}
@ -230,7 +229,4 @@ bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr);
/** Check whether a ViewingKey is not an InvalidEncoding. */
bool IsValidViewingKey(const libzcash::ViewingKey& vk);
/** Check whether a SpendingKey is not an InvalidEncoding. */
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey);
#endif // ZC_ADDRESS_H_

4
src/zcash/zip32.cpp

@ -137,3 +137,7 @@ libzcash::SaplingPaymentAddress SaplingExtendedSpendingKey::DefaultAddress() con
}
}
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) {
return zkey.which() != 0;
}

5
src/zcash/zip32.h

@ -116,6 +116,11 @@ struct SaplingExtendedSpendingKey {
}
};
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey;
}
/** Check whether a SpendingKey is not an InvalidEncoding. */
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey);
#endif // ZCASH_ZIP32_H

Loading…
Cancel
Save