From 2fcf06077ff44b112a92505c33ee17766940b521 Mon Sep 17 00:00:00 2001 From: mdr0id Date: Thu, 13 Sep 2018 14:22:00 -0700 Subject: [PATCH] Persist Sapling key material in the wallet to disk --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/wallet_persistence.py | 31 ++++++++++++ src/wallet/wallet.cpp | 35 ++++++++++++- src/wallet/wallet.h | 7 +++ src/wallet/walletdb.cpp | 81 +++++++++++++++++++++++++++++- src/wallet/walletdb.h | 6 +++ 6 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 qa/rpc-tests/wallet_persistence.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index cbc711844..0819a89ad 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -23,6 +23,7 @@ testScripts=( 'wallet_mergetoaddress.py' 'wallet.py' 'wallet_overwintertx.py' + 'wallet_persistence.py' 'wallet_nullifiers.py' 'wallet_1941.py' 'wallet_addresses.py' diff --git a/qa/rpc-tests/wallet_persistence.py b/qa/rpc-tests/wallet_persistence.py new file mode 100644 index 000000000..364e7b450 --- /dev/null +++ b/qa/rpc-tests/wallet_persistence.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_true, start_nodes, stop_nodes, \ + wait_bitcoinds + +class WalletPersistenceTest (BitcoinTestFramework): + + def setup_network(self, split=False): + self.nodes = start_nodes(1, self.options.tmpdir) + self.is_network_split = False + self.sync_all() + + def run_test(self): + sapling_addr = self.nodes[0].z_getnewaddress('sapling') + addresses = self.nodes[0].z_listaddresses() + # make sure the node has the addresss + assert_true(sapling_addr in addresses, "Should contain address before restart") + # restart the nodes + stop_nodes(self.nodes) + wait_bitcoinds() + self.nodes = self.setup_nodes() + addresses = self.nodes[0].z_listaddresses() + # make sure we still have the address after restarting + assert_true(sapling_addr in addresses, "Should contain address after restart") + +if __name__ == '__main__': + WalletPersistenceTest().main() \ No newline at end of file diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 09a9d15d5..ee242c233 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -166,7 +166,10 @@ bool CWallet::AddSaplingZKey( return true; } - // TODO: Persist to disk + if (!IsCrypted()) { + auto ivk = sk.expsk.full_viewing_key().in_viewing_key(); + return CWalletDB(strWalletFile).WriteSaplingZKey(ivk, sk, mapSaplingZKeyMetadata[ivk]); + } return true; } @@ -302,7 +305,16 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi if (!fFileBacked) return true; { - // TODO: Sapling - Write to disk + LOCK(cs_wallet); + if (pwalletdbEncryption) { + return pwalletdbEncryption->WriteCryptedSaplingZKey(extfvk, + vchCryptedSecret, + mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]); + } else { + return CWalletDB(strWalletFile).WriteCryptedSaplingZKey(extfvk, + vchCryptedSecret, + mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]); + } } return false; } @@ -334,6 +346,25 @@ bool CWallet::LoadCryptedZKey(const libzcash::SproutPaymentAddress &addr, const return CCryptoKeyStore::AddCryptedSproutSpendingKey(addr, rk, vchCryptedSecret); } +bool CWallet::LoadCryptedSaplingZKey( + const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::vector &vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedSaplingSpendingKey(extfvk, vchCryptedSecret, extfvk.DefaultAddress()); +} + +bool CWallet::LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta) +{ + AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata + mapSaplingZKeyMetadata[ivk] = meta; + return true; +} + +bool CWallet::LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key) +{ + return CCryptoKeyStore::AddSaplingSpendingKey(key, key.DefaultAddress()); +} + bool CWallet::LoadZKey(const libzcash::SproutSpendingKey &key) { return CCryptoKeyStore::AddSproutSpendingKey(key); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 95cd7b63e..642898827 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1063,6 +1063,13 @@ public: const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret, const libzcash::SaplingPaymentAddress &defaultAddr); + //! Adds spending key to the store, without saving it to disk (used by LoadWallet) + bool LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key); + //! Load spending key metadata (used by LoadWallet) + bool LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta); + //! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::vector &vchCryptedSecret); /** * Increment the next transaction order id diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 699d71946..9bab20858 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -125,6 +125,28 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, return true; } +bool CWalletDB::WriteCryptedSaplingZKey( + const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::vector& vchCryptedSecret, + const CKeyMetadata &keyMeta) +{ + const bool fEraseUnencryptedKey = true; + nWalletDBUpdated++; + auto ivk = extfvk.fvk.in_viewing_key(); + + if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta)) + return false; + + if (!Write(std::make_pair(std::string("csapzkey"), ivk), std::make_pair(extfvk, vchCryptedSecret), false)) + return false; + + if (fEraseUnencryptedKey) + { + Erase(std::make_pair(std::string("sapzkey"), ivk)); + } + return true; +} + bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { nWalletDBUpdated++; @@ -141,6 +163,17 @@ bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libz // pair is: tuple_key("zkey", paymentaddress) --> secretkey return Write(std::make_pair(std::string("zkey"), addr), key, false); } +bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, + const libzcash::SaplingExtendedSpendingKey &key, + const CKeyMetadata &keyMeta) +{ + nWalletDBUpdated++; + + if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta)) + return false; + + return Write(std::make_pair(std::string("sapzkey"), ivk), key, false); +} bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk) { @@ -511,6 +544,23 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, wss.nZKeys++; } + else if (strType == "sapzkey") + { + libzcash::SaplingIncomingViewingKey ivk; + ssKey >> ivk; + libzcash::SaplingExtendedSpendingKey key; + ssValue >> key; + + if (!pwallet->LoadSaplingZKey(key)) + { + strErr = "Error reading wallet database: LoadSaplingZKey failed"; + return false; + } + + //add checks for integrity + wss.nZKeys++; + } + else if (strType == "key" || strType == "wkey") { CPubKey vchPubKey; @@ -624,6 +674,23 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } wss.fIsEncrypted = true; } + else if (strType == "csapzkey") + { + libzcash::SaplingIncomingViewingKey ivk; + ssKey >> ivk; + libzcash::SaplingExtendedFullViewingKey extfvk; + ssValue >> extfvk; + vector vchCryptedSecret; + ssValue >> vchCryptedSecret; + wss.nCKeys++; + + if (!pwallet->LoadCryptedSaplingZKey(extfvk, vchCryptedSecret)) + { + strErr = "Error reading wallet database: LoadCryptedSaplingZKey failed"; + return false; + } + wss.fIsEncrypted = true; + } else if (strType == "keymeta") { CPubKey vchPubKey; @@ -651,6 +718,17 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, // ignore earliest key creation time as taddr will exist before any zaddr } + else if (strType == "sapzkeymeta") + { + libzcash::SaplingIncomingViewingKey ivk; + ssKey >> ivk; + CKeyMetadata keyMeta; + ssValue >> keyMeta; + + wss.nZKeyMeta++; + + pwallet->LoadSaplingZKeyMetadata(ivk, keyMeta); + } else if (strType == "defaultkey") { ssValue >> pwallet->vchDefaultKey; @@ -736,7 +814,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> vchCryptedSecret; if (!pwallet->LoadCryptedHDSeed(seedFp, vchCryptedSecret)) { - strErr = "Error reading wallet database: LoadCryptedSeed failed"; + strErr = "Error reading wallet database: LoadCryptedHDSeed failed"; return false; } wss.fIsEncrypted = true; @@ -759,6 +837,7 @@ static bool IsKeyType(string strType) return (strType== "key" || strType == "wkey" || strType == "hdseed" || strType == "chdseed" || strType == "zkey" || strType == "czkey" || + strType == "sapzkey" || strType == "csapzkey" || strType == "vkey" || strType == "mkey" || strType == "ckey"); } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index dc58adb65..a009b1ab7 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -184,10 +184,16 @@ public: /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); + bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, + const libzcash::SaplingExtendedSpendingKey &key, + const CKeyMetadata &keyMeta); bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, const libzcash::ReceivingKey & rk, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta); + bool WriteCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::vector& vchCryptedSecret, + const CKeyMetadata &keyMeta); bool WriteSproutViewingKey(const libzcash::SproutViewingKey &vk); bool EraseSproutViewingKey(const libzcash::SproutViewingKey &vk);