Persist Sapling payment address to IVK map

This ensures we remember any diversified addresses manually generated
outside the wallet.
This commit is contained in:
Jack Grigg 2018-10-06 00:45:39 +01:00
parent 9ce6f8425b
commit 3e471410f1
No known key found for this signature in database
GPG Key ID: 1B8D649257DB0829
5 changed files with 112 additions and 3 deletions

View File

@ -11,7 +11,9 @@
* This test covers Sapling methods on CWallet * This test covers Sapling methods on CWallet
* GenerateNewSaplingZKey() * GenerateNewSaplingZKey()
* AddSaplingZKey() * AddSaplingZKey()
* AddSaplingIncomingViewingKey()
* LoadSaplingZKey() * LoadSaplingZKey()
* LoadSaplingIncomingViewingKey()
* LoadSaplingZKeyMetadata() * LoadSaplingZKeyMetadata()
*/ */
TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) { TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) {
@ -62,6 +64,24 @@ TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) {
EXPECT_EQ(1, addrs.count(address)); EXPECT_EQ(1, addrs.count(address));
EXPECT_EQ(1, addrs.count(sk.DefaultAddress())); EXPECT_EQ(1, addrs.count(sk.DefaultAddress()));
// Generate a diversified address different to the default
// If we can't get an early diversified address, we are very unlucky
blob88 diversifier;
diversifier.begin()[0] = 10;
auto dpa = sk.ToXFVK().Address(diversifier).get().second;
// verify wallet only has the default address
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress()));
EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa));
// manually add a diversified address
auto ivk = fvk.in_viewing_key();
EXPECT_TRUE(wallet.AddSaplingIncomingViewingKey(ivk, dpa));
// verify wallet did add it
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress()));
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa));
// Load a third key into the wallet // Load a third key into the wallet
auto sk2 = m.Derive(1); auto sk2 = m.Derive(1);
ASSERT_TRUE(wallet.LoadSaplingZKey(sk2)); ASSERT_TRUE(wallet.LoadSaplingZKey(sk2));
@ -74,6 +94,13 @@ TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) {
// check metadata is the same // check metadata is the same
ASSERT_EQ(wallet.mapSaplingZKeyMetadata[ivk2].nCreateTime, now); ASSERT_EQ(wallet.mapSaplingZKeyMetadata[ivk2].nCreateTime, now);
// Load a diversified address for the third key into the wallet
auto dpa2 = sk2.ToXFVK().Address(diversifier).get().second;
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.DefaultAddress()));
EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa2));
EXPECT_TRUE(wallet.LoadSaplingPaymentAddress(dpa2, ivk2));
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa2));
} }
/** /**
@ -413,6 +440,18 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) {
wallet.GetSaplingPaymentAddresses(addrs); wallet.GetSaplingPaymentAddresses(addrs);
ASSERT_EQ(1, addrs.size()); ASSERT_EQ(1, addrs.size());
// Generate a diversified address different to the default
// If we can't get an early diversified address, we are very unlucky
libzcash::SaplingExtendedSpendingKey extsk;
EXPECT_TRUE(wallet.GetSaplingExtendedSpendingKey(address, extsk));
blob88 diversifier;
diversifier.begin()[0] = 10;
auto dpa = extsk.ToXFVK().Address(diversifier).get().second;
// Add diversified address to the wallet
auto ivk = extsk.expsk.full_viewing_key().in_viewing_key();
EXPECT_TRUE(wallet.AddSaplingIncomingViewingKey(ivk, dpa));
// encrypt wallet // encrypt wallet
SecureString strWalletPass; SecureString strWalletPass;
strWalletPass.reserve(100); strWalletPass.reserve(100);
@ -434,19 +473,25 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) {
ASSERT_TRUE(&wallet != &wallet2); ASSERT_TRUE(&wallet != &wallet2);
ASSERT_TRUE(wallet2.HaveHDSeed()); ASSERT_TRUE(wallet2.HaveHDSeed());
// wallet should have two keys // wallet should have three addresses
wallet2.GetSaplingPaymentAddresses(addrs); wallet2.GetSaplingPaymentAddresses(addrs);
ASSERT_EQ(2, addrs.size()); ASSERT_EQ(3, addrs.size());
//check we have entries for our payment addresses //check we have entries for our payment addresses
ASSERT_TRUE(addrs.count(address)); ASSERT_TRUE(addrs.count(address));
ASSERT_TRUE(addrs.count(address2)); ASSERT_TRUE(addrs.count(address2));
ASSERT_TRUE(addrs.count(dpa));
// spending key is crypted, so we can't extract valid payment address // spending key is crypted, so we can't extract valid payment address
libzcash::SaplingExtendedSpendingKey keyOut; libzcash::SaplingExtendedSpendingKey keyOut;
EXPECT_FALSE(wallet2.GetSaplingExtendedSpendingKey(address, keyOut)); EXPECT_FALSE(wallet2.GetSaplingExtendedSpendingKey(address, keyOut));
ASSERT_FALSE(address == keyOut.DefaultAddress()); ASSERT_FALSE(address == keyOut.DefaultAddress());
// address -> ivk mapping is not crypted
libzcash::SaplingIncomingViewingKey ivkOut;
EXPECT_TRUE(wallet2.GetSaplingIncomingViewingKey(dpa, ivkOut));
EXPECT_EQ(ivk, ivkOut);
// unlock wallet to get spending keys and verify payment addresses // unlock wallet to get spending keys and verify payment addresses
wallet2.Unlock(strWalletPass); wallet2.Unlock(strWalletPass);

View File

@ -174,6 +174,28 @@ bool CWallet::AddSaplingZKey(
return true; return true;
} }
// Add payment address -> incoming viewing key map entry
bool CWallet::AddSaplingIncomingViewingKey(
const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingPaymentAddress &addr)
{
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
if (!CCryptoKeyStore::AddSaplingIncomingViewingKey(ivk, addr)) {
return false;
}
if (!fFileBacked) {
return true;
}
if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteSaplingPaymentAddress(addr, ivk);
}
return true;
}
// Add spending key to keystore and persist to disk // Add spending key to keystore and persist to disk
bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key)
@ -365,6 +387,13 @@ bool CWallet::LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key)
return CCryptoKeyStore::AddSaplingSpendingKey(key, key.DefaultAddress()); return CCryptoKeyStore::AddSaplingSpendingKey(key, key.DefaultAddress());
} }
bool CWallet::LoadSaplingPaymentAddress(
const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk)
{
return CCryptoKeyStore::AddSaplingIncomingViewingKey(ivk, addr);
}
bool CWallet::LoadZKey(const libzcash::SproutSpendingKey &key) bool CWallet::LoadZKey(const libzcash::SproutSpendingKey &key)
{ {
return CCryptoKeyStore::AddSproutSpendingKey(key); return CCryptoKeyStore::AddSproutSpendingKey(key);

View File

@ -1075,6 +1075,9 @@ public:
bool AddSaplingZKey( bool AddSaplingZKey(
const libzcash::SaplingExtendedSpendingKey &key, const libzcash::SaplingExtendedSpendingKey &key,
const libzcash::SaplingPaymentAddress &defaultAddr); const libzcash::SaplingPaymentAddress &defaultAddr);
bool AddSaplingIncomingViewingKey(
const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingPaymentAddress &addr);
bool AddCryptedSaplingSpendingKey( bool AddCryptedSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk, const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::vector<unsigned char> &vchCryptedSecret, const std::vector<unsigned char> &vchCryptedSecret,
@ -1083,6 +1086,11 @@ public:
bool LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key); bool LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key);
//! Load spending key metadata (used by LoadWallet) //! Load spending key metadata (used by LoadWallet)
bool LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta); bool LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta);
//! Adds a Sapling payment address -> incoming viewing key map entry,
//! without saving it to disk (used by LoadWallet)
bool LoadSaplingPaymentAddress(
const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk);
//! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet) //! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::vector<unsigned char> &vchCryptedSecret); const std::vector<unsigned char> &vchCryptedSecret);

View File

@ -175,6 +175,15 @@ bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk,
return Write(std::make_pair(std::string("sapzkey"), ivk), key, false); return Write(std::make_pair(std::string("sapzkey"), ivk), key, false);
} }
bool CWalletDB::WriteSaplingPaymentAddress(
const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("sapzaddr"), addr), ivk, false);
}
bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk) bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk)
{ {
nWalletDBUpdated++; nWalletDBUpdated++;
@ -416,13 +425,14 @@ public:
unsigned int nZKeys; unsigned int nZKeys;
unsigned int nCZKeys; unsigned int nCZKeys;
unsigned int nZKeyMeta; unsigned int nZKeyMeta;
unsigned int nSapZAddrs;
bool fIsEncrypted; bool fIsEncrypted;
bool fAnyUnordered; bool fAnyUnordered;
int nFileVersion; int nFileVersion;
vector<uint256> vWalletUpgrade; vector<uint256> vWalletUpgrade;
CWalletScanState() { CWalletScanState() {
nKeys = nCKeys = nKeyMeta = nZKeys = nCZKeys = nZKeyMeta = 0; nKeys = nCKeys = nKeyMeta = nZKeys = nCZKeys = nZKeyMeta = nSapZAddrs = 0;
fIsEncrypted = false; fIsEncrypted = false;
fAnyUnordered = false; fAnyUnordered = false;
nFileVersion = 0; nFileVersion = 0;
@ -729,6 +739,21 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
pwallet->LoadSaplingZKeyMetadata(ivk, keyMeta); pwallet->LoadSaplingZKeyMetadata(ivk, keyMeta);
} }
else if (strType == "sapzaddr")
{
libzcash::SaplingPaymentAddress addr;
ssKey >> addr;
libzcash::SaplingIncomingViewingKey ivk;
ssValue >> ivk;
wss.nSapZAddrs++;
if (!pwallet->LoadSaplingPaymentAddress(addr, ivk))
{
strErr = "Error reading wallet database: LoadSaplingPaymentAddress failed";
return false;
}
}
else if (strType == "defaultkey") else if (strType == "defaultkey")
{ {
ssValue >> pwallet->vchDefaultKey; ssValue >> pwallet->vchDefaultKey;

View File

@ -187,6 +187,8 @@ public:
bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingExtendedSpendingKey &key, const libzcash::SaplingExtendedSpendingKey &key,
const CKeyMetadata &keyMeta); const CKeyMetadata &keyMeta);
bool WriteSaplingPaymentAddress(const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk);
bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr,
const libzcash::ReceivingKey & rk, const libzcash::ReceivingKey & rk,
const std::vector<unsigned char>& vchCryptedSecret, const std::vector<unsigned char>& vchCryptedSecret,