From e2416930eab5c1bc9f6c9f0b504418c81fa310d3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 4 Aug 2018 00:54:33 +0100 Subject: [PATCH] wallet: Store HDSeed and chain data --- src/wallet/wallet.cpp | 86 +++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.h | 28 ++++++++++++++ src/wallet/walletdb.cpp | 59 ++++++++++++++++++++++++++++ src/wallet/walletdb.h | 39 +++++++++++++++++++ 4 files changed, 212 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 62b344522..a093172b2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1865,6 +1865,92 @@ CAmount CWallet::GetChange(const CTransaction& tx) const return nChange; } +bool CWallet::IsHDFullyEnabled() const +{ + // Only Sapling addresses are HD for now + return false; +} + +void CWallet::GenerateNewSeed() +{ + LOCK(cs_wallet); + + auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH); + + int64_t nCreationTime = GetTime(); + + // If the wallet is encrypted and locked, this will fail. + if (!SetHDSeed(seed)) + throw std::runtime_error(std::string(__func__) + ": SetHDSeed failed"); + + // store the key creation time together with + // the child index counter in the database + // as a hdchain object + CHDChain newHdChain; + newHdChain.nVersion = CHDChain::VERSION_HD_BASE; + newHdChain.seedFp = seed.Fingerprint(); + newHdChain.nCreateTime = nCreationTime; + SetHDChain(newHdChain, false); +} + +bool CWallet::SetHDSeed(const HDSeed& seed) +{ + if (!CCryptoKeyStore::SetHDSeed(seed)) { + return false; + } + + if (!fFileBacked) { + return true; + } + + { + LOCK(cs_wallet); + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteHDSeed(seed); + } + } + return true; +} + +bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::SetCryptedHDSeed(seedFp, vchCryptedSecret)) { + return false; + } + + if (!fFileBacked) { + return true; + } + + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedHDSeed(seedFp, vchCryptedSecret); + else + return CWalletDB(strWalletFile).WriteCryptedHDSeed(seedFp, vchCryptedSecret); + } + return false; +} + +void CWallet::SetHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(chain)) + throw std::runtime_error(std::string(__func__) + ": writing chain failed"); + + hdChain = chain; +} + +bool CWallet::LoadHDSeed(const HDSeed& seed) +{ + return CBasicKeyStore::SetHDSeed(seed); +} + +bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed) +{ + return CCryptoKeyStore::SetCryptedHDSeed(seedFp, seed); +} + void CWalletTx::SetSproutNoteData(mapSproutNoteData_t ¬eData) { mapSproutNoteData.clear(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6c57d6112..00491b5ea 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -23,6 +23,7 @@ #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" #include "zcash/Address.hpp" +#include "zcash/zip32.h" #include "base58.h" #include @@ -61,6 +62,9 @@ static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; // unless there is some exceptional network disruption. static const unsigned int WITNESS_CACHE_SIZE = MAX_REORG_LENGTH + 1; +//! Size of HD seed in bytes +static const size_t HD_WALLET_SEED_LENGTH = 32; + class CBlockIndex; class CCoinControl; class COutput; @@ -823,6 +827,9 @@ protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); void MarkAffectedTransactionsDirty(const CTransaction& tx); + /* the hd chain data model (chain counters) */ + CHDChain hdChain; + public: /* * Main wallet lock. @@ -1222,6 +1229,27 @@ public: bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; } + + /* Returns true if HD is enabled for all address types, false if only for Sapling */ + bool IsHDFullyEnabled() const; + + /* Generates a new HD seed (will reset the chain child index counters) + Sets the seed's version based on the current wallet version (so the + caller must ensure the current wallet version is correct before calling + this function). */ + void GenerateNewSeed(); + + bool SetHDSeed(const HDSeed& seed); + bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + + /* Set the HD chain model (chain child index counters) */ + void SetHDChain(const CHDChain& chain, bool memonly); + const CHDChain& GetHDChain() const { return hdChain; } + + /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadHDSeed(const HDSeed& key); + /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); /* Find notes filtered by payment address, min depth, ability to spend */ void GetFilteredNotes(std::vector& sproutEntries, diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 0281c5e3f..699d71946 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -708,6 +708,45 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->nWitnessCacheSize; } + else if (strType == "hdseed") + { + uint256 seedFp; + RawHDSeed rawSeed; + ssKey >> seedFp; + ssValue >> rawSeed; + HDSeed seed(rawSeed); + + if (seed.Fingerprint() != seedFp) + { + strErr = "Error reading wallet database: HDSeed corrupt"; + return false; + } + + if (!pwallet->LoadHDSeed(seed)) + { + strErr = "Error reading wallet database: LoadHDSeed failed"; + return false; + } + } + else if (strType == "chdseed") + { + uint256 seedFp; + vector vchCryptedSecret; + ssKey >> seedFp; + ssValue >> vchCryptedSecret; + if (!pwallet->LoadCryptedHDSeed(seedFp, vchCryptedSecret)) + { + strErr = "Error reading wallet database: LoadCryptedSeed failed"; + return false; + } + wss.fIsEncrypted = true; + } + else if (strType == "hdchain") + { + CHDChain chain; + ssValue >> chain; + pwallet->SetHDChain(chain, true); + } } catch (...) { return false; @@ -718,6 +757,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, static bool IsKeyType(string strType) { return (strType== "key" || strType == "wkey" || + strType == "hdseed" || strType == "chdseed" || strType == "zkey" || strType == "czkey" || strType == "vkey" || strType == "mkey" || strType == "ckey"); @@ -1103,3 +1143,22 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key nWalletDBUpdated++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } + + +bool CWalletDB::WriteHDSeed(const HDSeed& seed) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("hdseed"), seed.Fingerprint()), seed.RawSeed()); +} + +bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret); +} + +bool CWalletDB::WriteHDChain(const CHDChain& chain) +{ + nWalletDBUpdated++; + return Write(std::string("hdchain"), chain); +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2dd301ed9..7cd446e9b 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -11,6 +11,7 @@ #include "key.h" #include "keystore.h" #include "zcash/Address.hpp" +#include "zcash/zip32.h" #include #include @@ -40,6 +41,39 @@ enum DBErrors DB_NEED_REWRITE }; +/* simple hd chain data model */ +class CHDChain +{ +public: + static const int VERSION_HD_BASE = 1; + static const int CURRENT_VERSION = VERSION_HD_BASE; + int nVersion; + uint256 seedFp; + int64_t nCreateTime; // 0 means unknown + uint32_t saplingAccountCounter; + + CHDChain() { SetNull(); } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(seedFp); + READWRITE(nCreateTime); + READWRITE(saplingAccountCounter); + } + + void SetNull() + { + nVersion = CHDChain::CURRENT_VERSION; + seedFp.SetNull(); + nCreateTime = 0; + saplingAccountCounter = 0; + } +}; + class CKeyMetadata { public: @@ -132,6 +166,11 @@ public: static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); + bool WriteHDSeed(const HDSeed& seed); + bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); + //! write the hdchain model (external chain child index counter) + bool WriteHDChain(const CHDChain& chain); + /// 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 WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr,