Add unified key components to the transparent & Sapling wallet parts.

This commit is contained in:
Kris Nuttycombe 2021-11-22 16:47:59 -07:00
parent 24ff7b36ec
commit 6d36921b94
6 changed files with 157 additions and 24 deletions

View File

@ -79,6 +79,7 @@ static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) {
auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0); auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0);
return usk.value() return usk.value()
.first
.ToFullViewingKey() .ToFullViewingKey()
.GetSaplingKey().value() .GetSaplingKey().value()
.FindAddress(libzcash::diversifier_index_t(0)).first; .FindAddress(libzcash::diversifier_index_t(0)).first;

View File

@ -172,7 +172,6 @@ std::pair<SaplingExtendedSpendingKey, bool> CWallet::GenerateLegacySaplingZKey(u
} else { } else {
return std::make_pair(xsk.first, false); return std::make_pair(xsk.first, false);
} }
} }
// Add spending key to keystore // Add spending key to keystore
@ -288,14 +287,26 @@ CPubKey CWallet::GenerateNewKey()
// if we did not successfully generate a key, try again. // if we did not successfully generate a key, try again.
} while (!extKey.has_value()); } while (!extKey.has_value());
CKey secret = extKey.value().first.key; auto pubkey = AddKey(seed.Fingerprint(), extKey.value());
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
}
return pubkey;
}
CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair<CExtKey, HDKeyPath>& extSecret)
{
CKey secret = extSecret.first.key;
CPubKey pubkey = secret.GetPubKey(); CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey)); assert(secret.VerifyPubKey(pubkey));
// Create new metadata // Create new metadata
CKeyMetadata keyMeta(GetTime()); CKeyMetadata keyMeta(GetTime());
keyMeta.hdKeypath = extKey.value().second; keyMeta.hdKeypath = extSecret.second;
keyMeta.seedFp = seed.Fingerprint(); keyMeta.seedFp = seedFingerprint;
mapKeyMetadata[pubkey.GetID()] = keyMeta; mapKeyMetadata[pubkey.GetID()] = keyMeta;
if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey)
nTimeFirstKey = keyMeta.nCreateTime; nTimeFirstKey = keyMeta.nCreateTime;
@ -303,11 +314,6 @@ CPubKey CWallet::GenerateNewKey()
if (!AddKeyPubKey(secret, pubkey)) if (!AddKeyPubKey(secret, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
}
return pubkey; return pubkey;
} }
@ -407,7 +413,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi
return false; return false;
} }
ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { std::pair<ZcashdUnifiedSpendingKey, ZcashdUnifiedKeyMetadata> CWallet::GenerateNewUnifiedSpendingKey() {
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
if (!mnemonicHDChain.has_value()) { if (!mnemonicHDChain.has_value()) {
@ -431,7 +437,8 @@ ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() {
} }
} }
std::optional<libzcash::ZcashdUnifiedSpendingKey> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::ZcashdUnifiedKeyMetadata>>
CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) {
auto seed = GetMnemonicSeed(); auto seed = GetMnemonicSeed();
if (!seed.has_value()) { if (!seed.has_value()) {
throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed."); throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed.");
@ -439,14 +446,60 @@ std::optional<libzcash::ZcashdUnifiedSpendingKey> CWallet::GenerateUnifiedSpendi
auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId);
if (usk.has_value()) { if (usk.has_value()) {
// TODO: Save the unified full viewing key & metadata to the wallet if (!AddUnifiedSpendingKey(usk.value().first, usk.value().second)) {
throw std::runtime_error("CWallet::GenerateUnifiedSpendingKeyForAccount(): AddUnifiedSpendingKey failed.");
}
return usk.value().first; return usk.value();
} else { } else {
return std::nullopt; return std::nullopt;
} }
} }
// Add spending key to keystore
bool CWallet::AddUnifiedSpendingKey(
const libzcash::ZcashdUnifiedSpendingKey& sk,
const libzcash::ZcashdUnifiedKeyMetadata& metadata) {
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
auto ufvk = 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(),
std::make_pair(sk.GetTransparentKey().value(), metadata.TransparentKeyPath().value()));
}
// Add Sapling component to the wallet
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.
return false;
}
if (!fFileBacked) {
return true;
}
if (!IsCrypted()) {
//return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvk, metadata);
}
return true;
}
void CWallet::LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata
auto key = std::make_pair(meta.GetSeedFingerprint(), meta.GetAccountId());
mapUnifiedKeyMetadata.insert({key, meta});
}
void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
{ {
AssertLockHeld(cs_wallet); // mapKeyMetadata AssertLockHeld(cs_wallet); // mapKeyMetadata
@ -5542,7 +5595,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
if (m_wallet->HaveSaplingSpendingKey(extfvk)) { if (m_wallet->HaveSaplingSpendingKey(extfvk)) {
return KeyAlreadyExists; return KeyAlreadyExists;
} else { } else {
if (!m_wallet-> AddSaplingZKey(sk)) { if (!m_wallet->AddSaplingZKey(sk)) {
return KeyNotAdded; return KeyNotAdded;
} }
@ -5556,7 +5609,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
if (hdKeypath.has_value()) { if (hdKeypath.has_value()) {
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.value(); m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.value();
} }
if (seedFpStr) { if (seedFpStr.has_value()) {
uint256 seedFp; uint256 seedFp;
seedFp.SetHex(seedFpStr.value()); seedFp.SetHex(seedFpStr.value());
m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp; m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp;

View File

@ -803,6 +803,9 @@ private:
void SyncMetaData(std::pair<typename TxSpendMap<T>::iterator, typename TxSpendMap<T>::iterator>); void SyncMetaData(std::pair<typename TxSpendMap<T>::iterator, typename TxSpendMap<T>::iterator>);
void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree); 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);
protected: protected:
bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx);
void MarkAffectedTransactionsDirty(const CTransaction& tx); void MarkAffectedTransactionsDirty(const CTransaction& tx);
@ -830,6 +833,7 @@ public:
std::map<CKeyID, CKeyMetadata> mapKeyMetadata; std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapSproutZKeyMetadata; std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapSproutZKeyMetadata;
std::map<libzcash::SaplingIncomingViewingKey, CKeyMetadata> mapSaplingZKeyMetadata; std::map<libzcash::SaplingIncomingViewingKey, CKeyMetadata> mapSaplingZKeyMetadata;
std::map<std::pair<libzcash::SeedFingerprint, libzcash::AccountId>, libzcash::ZcashdUnifiedKeyMetadata> mapUnifiedKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap; typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys; MasterKeyMap mapMasterKeys;
@ -1096,11 +1100,27 @@ public:
bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::vector<unsigned char> &vchCryptedSecret); const std::vector<unsigned char> &vchCryptedSecret);
/** //
* Unified keys & addresses // Unified keys & addresses
*/ //
libzcash::ZcashdUnifiedSpendingKey GenerateNewUnifiedSpendingKey();
std::optional<libzcash::ZcashdUnifiedSpendingKey> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); //! Generate the unified spending key from the wallet's mnemonic seed
//! for the next unused account identifier.
std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::ZcashdUnifiedKeyMetadata>
GenerateNewUnifiedSpendingKey();
//! Generate the next available unified spending key from the wallet's
//! mnemonic seed.
std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::ZcashdUnifiedKeyMetadata>>
GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId);
//! Add the specified unified spending key to the wallet with the provided key
//! metadata.
bool AddUnifiedSpendingKey(
const libzcash::ZcashdUnifiedSpendingKey& sk,
const libzcash::ZcashdUnifiedKeyMetadata& metadata);
void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta);
/** /**
* Increment the next transaction order id * Increment the next transaction order id

View File

@ -169,6 +169,7 @@ bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libz
// pair is: tuple_key("zkey", paymentaddress) --> secretkey // pair is: tuple_key("zkey", paymentaddress) --> secretkey
return Write(std::make_pair(std::string("zkey"), addr), key, false); return Write(std::make_pair(std::string("zkey"), addr), key, false);
} }
bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingExtendedSpendingKey &key, const libzcash::SaplingExtendedSpendingKey &key,
const CKeyMetadata &keyMeta) const CKeyMetadata &keyMeta)

View File

@ -20,6 +20,30 @@ enum class ReceiverType: uint32_t {
Orchard = 0x03 Orchard = 0x03
}; };
class ZcashdUnifiedKeyMetadata;
// Serialization wrapper for reading and writing ReceiverType
// in CompactSize format.
class ReceiverTypeSer {
private:
ReceiverType t;
friend class ZcashdUnifiedKeyMetadata;
public:
ReceiverTypeSer() {} // for serialization only
ReceiverTypeSer(ReceiverType t): t(t) {}
template<typename Stream>
void Serialize(Stream &s) const {
WriteCompactSize<Stream>(s, (uint64_t) t);
}
template<typename Stream>
void Unserialize(Stream& s) {
t = (ReceiverType) ReadCompactSize<Stream>(s);
}
};
class ZcashdUnifiedSpendingKey; class ZcashdUnifiedSpendingKey;
class ZcashdUnifiedFullViewingKey; class ZcashdUnifiedFullViewingKey;
@ -30,23 +54,56 @@ class UnifiedFullViewingKey;
class ZcashdUnifiedKeyMetadata { class ZcashdUnifiedKeyMetadata {
private: private:
uint256 seedFp; SeedFingerprint seedFp;
uint32_t bip44CoinType; uint32_t bip44CoinType;
libzcash::AccountId accountId; libzcash::AccountId accountId;
std::vector<ReceiverType> receiverTypes; std::vector<libzcash::ReceiverType> receiverTypes;
ZcashdUnifiedKeyMetadata() {}
public: public:
ZcashdUnifiedKeyMetadata( ZcashdUnifiedKeyMetadata(
uint256 seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector<ReceiverType> receiverTypes): SeedFingerprint seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector<ReceiverType> receiverTypes):
seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), receiverTypes(receiverTypes) {} seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), receiverTypes(receiverTypes) {}
const uint256& GetSeedFingerprint() const { const SeedFingerprint& GetSeedFingerprint() const {
return seedFp; return seedFp;
} }
libzcash::AccountId GetAccountId() const {
return accountId;
}
const std::vector<ReceiverType>& GetReceiverTypes() const { const std::vector<ReceiverType>& GetReceiverTypes() const {
return receiverTypes; return receiverTypes;
} }
std::optional<HDKeyPath> TransparentKeyPath() const; std::optional<HDKeyPath> TransparentKeyPath() const;
std::optional<HDKeyPath> SaplingKeyPath() const; std::optional<HDKeyPath> SaplingKeyPath() const;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(seedFp);
READWRITE(bip44CoinType);
READWRITE(accountId);
if (ser_action.ForRead()) {
std::vector<ReceiverTypeSer> serReceiverTypes;
READWRITE(serReceiverTypes);
receiverTypes.clear();
for (ReceiverTypeSer r : serReceiverTypes)
receiverTypes.push_back(r.t);
} else {
std::vector<ReceiverTypeSer> serReceiverTypes;
for (ReceiverType r : receiverTypes)
serReceiverTypes.push_back(ReceiverTypeSer(r));
READWRITE(serReceiverTypes);
}
}
template <typename Stream>
static ZcashdUnifiedKeyMetadata Read(Stream& stream) {
ZcashdUnifiedKeyMetadata meta;
stream >> meta;
return meta;
}
}; };
/** /**

View File

@ -62,6 +62,7 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed);
namespace libzcash { namespace libzcash {
typedef uint256 SeedFingerprint;
typedef uint32_t AccountId; typedef uint32_t AccountId;
/** /**