Store ufvks to the wallet database.

This commit is contained in:
Kris Nuttycombe 2021-11-30 08:30:50 -07:00
parent 6d36921b94
commit ef068c51a9
6 changed files with 151 additions and 21 deletions

View File

@ -287,3 +287,31 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen
GetSaplingFullViewingKey(ivk, extfvk) &&
GetSaplingSpendingKey(extfvk, extskOut);
}
//
// Unified Keys
//
bool CBasicKeyStore::AddUnifiedFullViewingKey(
const libzcash::UFVKId& keyId,
const libzcash::ZcashdUnifiedFullViewingKey &ufvk)
{
LOCK(cs_KeyStore);
auto tKey = ufvk.GetTransparentKey();
if (tKey.has_value()) {
// FIXME: this is probably not the right thing; we need to actually track
// addresses, not the id at the FVK level?
mapTKeyUnified.insert(std::make_pair(tKey.value().GetPubKey().GetID(), keyId));
}
auto saplingKey = ufvk.GetSaplingKey();
if (saplingKey.has_value()) {
auto ivk = saplingKey.value().fvk.in_viewing_key();
mapSaplingKeyUnified.insert(std::make_pair(ivk, keyId));
}
mapUnifiedFullViewingKeys.insert({keyId, ufvk});
return true;
}

View File

@ -12,6 +12,7 @@
#include "script/standard.h"
#include "sync.h"
#include "zcash/address/mnemonic.h"
#include "zcash/address/unified.h"
#include "zcash/Address.hpp"
#include "zcash/NoteEncryption.hpp"
@ -100,6 +101,12 @@ public:
virtual bool GetSproutViewingKey(
const libzcash::SproutPaymentAddress &address,
libzcash::SproutViewingKey& vkOut) const =0;
//! Unified addresses and keys
virtual bool AddUnifiedFullViewingKey(
const libzcash::UFVKId& keyId,
const libzcash::ZcashdUnifiedFullViewingKey &ufvk
) = 0;
};
typedef std::map<CKeyID, CKey> KeyMap;
@ -142,6 +149,10 @@ protected:
SaplingFullViewingKeyMap mapSaplingFullViewingKeys;
SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys;
// Unified key support
std::map<CKeyID, libzcash::UFVKId> mapTKeyUnified;
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
public:
bool SetMnemonicSeed(const MnemonicSeed& seed);
bool HaveMnemonicSeed() const;
@ -314,6 +325,10 @@ public:
virtual bool GetSproutViewingKey(
const libzcash::SproutPaymentAddress &address,
libzcash::SproutViewingKey& vkOut) const;
virtual bool AddUnifiedFullViewingKey(
const libzcash::UFVKId& keyId,
const libzcash::ZcashdUnifiedFullViewingKey &ufvk);
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;

View File

@ -195,7 +195,9 @@ bool CWallet::AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &sk)
return true;
}
bool CWallet::AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk)
bool CWallet::AddSaplingFullViewingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::optional<libzcash::UFVKId>& ufvkId)
{
AssertLockHeld(cs_wallet);
@ -287,7 +289,7 @@ CPubKey CWallet::GenerateNewKey()
// if we did not successfully generate a key, try again.
} while (!extKey.has_value());
auto pubkey = AddKey(seed.Fingerprint(), extKey.value());
auto pubkey = AddTransparentSecretKey(seed.Fingerprint(), extKey.value());
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
@ -297,7 +299,10 @@ CPubKey CWallet::GenerateNewKey()
return pubkey;
}
CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair<CExtKey, HDKeyPath>& extSecret)
CPubKey CWallet::AddTransparentSecretKey(
const uint256& seedFingerprint,
const std::pair<CExtKey, HDKeyPath>& extSecret,
const std::optional<libzcash::UFVKId>& ufvkId)
{
CKey secret = extSecret.first.key;
CPubKey pubkey = secret.GetPubKey();
@ -311,13 +316,16 @@ CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair<CExtKey,
if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey)
nTimeFirstKey = keyMeta.nCreateTime;
if (!AddKeyPubKey(secret, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
if (!AddKeyPubKey(secret, pubkey, ufvkId))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKeyPubKey failed");
return pubkey;
}
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
bool CWallet::AddKeyPubKey(
const CKey& secret,
const CPubKey &pubkey,
const std::optional<libzcash::UFVKId>& ufvkId)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))
@ -334,11 +342,13 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
if (!fFileBacked)
return true;
if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
return true;
}
@ -456,29 +466,42 @@ std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::ZcashdUnif
}
}
// Add spending key to keystore
bool CWallet::AddUnifiedSpendingKey(
const libzcash::ZcashdUnifiedSpendingKey& sk,
const libzcash::ZcashdUnifiedKeyMetadata& metadata) {
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata
auto ufvk = sk.ToFullViewingKey();
// We don't store the spending key directly; instead, we store each of the spending key's
// components, in order to not violate invariants with respect to the encryption of the
// wallet. Instead, we store each component in the appropriate wallet subsystem, and
// store the metadata that can be used to re-derive the key associated with the fingerprint
// of the associated full viewing key.
auto zufvk = sk.ToFullViewingKey();
auto metaKey = std::make_pair(metadata.GetSeedFingerprint(), metadata.GetAccountId());
mapUnifiedKeyMetadata.insert({metaKey, metadata});
// Add Transparent component to the wallet
if (sk.GetTransparentKey().has_value()) {
AddKey(metadata.GetSeedFingerprint(),
AddTransparentSecretKey(metadata.GetSeedFingerprint(),
std::make_pair(sk.GetTransparentKey().value(), metadata.TransparentKeyPath().value()));
}
// Add Sapling component to the wallet
auto addSpendingKey = AddSpendingKeyToWallet(
if (sk.GetSaplingExtendedSpendingKey().has_value()) {
auto esk = sk.GetSaplingExtendedSpendingKey().value();
auto addSpendingKey = AddSpendingKeyToWallet(
this, Params().GetConsensus(), GetTime(),
metadata.SaplingKeyPath(), metadata.GetSeedFingerprint().GetHex(), true);
if (sk.GetSaplingExtendedSpendingKey().has_value() &&
addSpendingKey(sk.GetSaplingExtendedSpendingKey().value()) == KeyNotAdded) {
// If adding the Sapling key to the wallet failed, abort the process.
if (addSpendingKey(esk) == KeyNotAdded) {
// If adding the Sapling key to the wallet failed, abort the process.
return false;
}
}
auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk);
auto ufvkid = ufvk.GetKeyID(Params());
if (!CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk)) {
return false;
}
@ -486,11 +509,32 @@ bool CWallet::AddUnifiedSpendingKey(
return true;
}
if (!IsCrypted()) {
//return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvk, metadata);
return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvkid, ufvk);
}
bool CWallet::AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk)
{
AssertLockHeld(cs_wallet);
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(ufvk);
auto keyId = ufvk.GetKeyID(Params());
if (!CCryptoKeyStore::AddUnifiedFullViewingKey(keyId, zufvk)) {
return false;
}
return true;
if (!fFileBacked) {
return true;
}
return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(keyId, ufvk);
}
bool CWallet::LoadUnifiedFullViewingKey(
const libzcash::UFVKId& keyId,
const libzcash::UnifiedFullViewingKey &key)
{
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(key);
return CCryptoKeyStore::AddUnifiedFullViewingKey(keyId, zufvk);
}
void CWallet::LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta)

View File

@ -702,8 +702,13 @@ private:
int nSetChainUpdates;
bool fBroadcastTransactions;
/**
* A map from protocol-specifiec transaction output identifier to
* a txid.
*/
template <class T>
using TxSpendMap = std::multimap<T, uint256>;
/**
* Used to keep track of spent outpoints, and
* detect and report conflicts (double-spends or
@ -711,6 +716,7 @@ private:
*/
typedef TxSpendMap<COutPoint> TxSpends;
TxSpends mapTxSpends;
/**
* Used to keep track of spent Notes, and
* detect and report conflicts (double-spends).
@ -804,7 +810,10 @@ private:
void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree);
/* Add an extended secret key to the wallet. Internal use only. */
CPubKey AddKey(const uint256& seedFingerprint, const std::pair<CExtKey, HDKeyPath>& extSecret);
CPubKey AddTransparentSecretKey(
const uint256& seedFingerprint,
const std::pair<CExtKey, HDKeyPath>& extSecret,
const std::optional<libzcash::UFVKId>& ufvkId = std::nullopt);
protected:
bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx);
@ -831,8 +840,10 @@ public:
std::set<int64_t> setKeyPool;
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapSproutZKeyMetadata;
std::map<libzcash::SaplingIncomingViewingKey, CKeyMetadata> mapSaplingZKeyMetadata;
std::map<std::pair<libzcash::SeedFingerprint, libzcash::AccountId>, libzcash::ZcashdUnifiedKeyMetadata> mapUnifiedKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
@ -999,7 +1010,10 @@ public:
*/
CPubKey GenerateNewKey();
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
bool AddKeyPubKey(
const CKey& key,
const CPubKey &pubkey,
const std::optional<libzcash::UFVKId>& ufvkId = std::nullopt);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
@ -1078,7 +1092,9 @@ public:
//! full viewing key to disk. Inside CCryptoKeyStore and CBasicKeyStore,
//! CBasicKeyStore::AddSaplingFullViewingKey is called directly when adding a
//! full viewing key to the keystore, to avoid this override.
bool AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk);
bool AddSaplingFullViewingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::optional<libzcash::UFVKId>& ufvkId = std::nullopt);
bool AddSaplingIncomingViewingKey(
const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingPaymentAddress &addr);
@ -1115,11 +1131,16 @@ public:
GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId);
//! Add the specified unified spending key to the wallet with the provided key
//! metadata.
//! metadata. -- TODO, this should probably not be part of the public API?
bool AddUnifiedSpendingKey(
const libzcash::ZcashdUnifiedSpendingKey& sk,
const libzcash::ZcashdUnifiedKeyMetadata& metadata);
bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
bool LoadUnifiedFullViewingKey(
const libzcash::UFVKId& keyId,
const libzcash::UnifiedFullViewingKey &key);
void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta);
/**

View File

@ -217,6 +217,22 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey(
return Erase(std::make_pair(std::string("sapextfvk"), extfvk));
}
//
// Unified address & key storage
//
bool CWalletDB::WriteUnifiedFullViewingKey(
const libzcash::UFVKId& ufvkId,
const libzcash::UnifiedFullViewingKey& ufvk)
{
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("unifiedfvk"), ufvkId), ufvk.Encode(Params()));
}
//
//
//
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdateCounter++;

View File

@ -249,6 +249,12 @@ public:
bool WriteSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk);
bool EraseSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk);
/// Unified key support.
bool WriteUnifiedFullViewingKey(
const libzcash::UFVKId& ufvkId,
const libzcash::UnifiedFullViewingKey& ufvk);
static void IncrementUpdateCounter();
static unsigned int GetUpdateCounter();
private: