Ensure that unified address metadata is always correctly populated.

This reworks the way that address metadata is added to the wallet
to make adding address metadata to the wallet's in-memory cache
the responsibility of a single method rather than distributed
across multiple places in the code.
This commit is contained in:
Kris Nuttycombe 2021-12-22 18:20:53 -07:00
parent ad5c4dcf09
commit 74e4bef6f2
11 changed files with 186 additions and 116 deletions

View File

@ -560,7 +560,7 @@ TEST(KeystoreTests, AddUnifiedAddress) {
auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling});
EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value());
keyStore.AddUnifiedAddress(ufvkid, addrPair);
keyStore.AddUnifiedAddress(ufvkid, addrPair.second, addrPair.first);
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value());
EXPECT_TRUE(ufvkmeta.has_value());

View File

@ -316,9 +316,10 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
return true;
}
void CBasicKeyStore::AddUnifiedAddress(
bool CBasicKeyStore::AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua)
const libzcash::diversifier_index_t& diversifierIndex,
const libzcash::UnifiedAddress& ua)
{
LOCK(cs_KeyStore);
@ -326,17 +327,19 @@ void CBasicKeyStore::AddUnifiedAddress(
// the UA; all other lookups of the associated UFVK will be
// made via the protocol-specific viewing key that is used
// to trial-decrypt a transaction.
auto addrEntry = std::make_pair(keyId, ua.second);
auto addrEntry = std::make_pair(keyId, diversifierIndex);
auto p2pkhReceiver = ua.first.GetP2PKHReceiver();
auto p2pkhReceiver = ua.GetP2PKHReceiver();
if (p2pkhReceiver.has_value()) {
mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), addrEntry));
}
auto p2shReceiver = ua.first.GetP2SHReceiver();
auto p2shReceiver = ua.GetP2SHReceiver();
if (p2shReceiver.has_value()) {
mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), addrEntry));
}
return true;
}
std::optional<libzcash::ZcashdUnifiedFullViewingKey> CBasicKeyStore::GetUnifiedFullViewingKey(

View File

@ -117,10 +117,10 @@ public:
* viewing key upon discovery of the address as having received
* funds.
*/
virtual void AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua
) = 0;
virtual bool AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const libzcash::diversifier_index_t& diversifierIndex,
const libzcash::UnifiedAddress& ua) = 0;
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId
@ -357,9 +357,10 @@ public:
virtual bool AddUnifiedFullViewingKey(
const libzcash::ZcashdUnifiedFullViewingKey &ufvk);
virtual void AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua);
virtual bool AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const libzcash::diversifier_index_t& diversifierIndex,
const libzcash::UnifiedAddress& ua);
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId) const;

24
src/util/match.h Normal file
View File

@ -0,0 +1,24 @@
// Copyright (c) 2021 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_UTIL_MATCH_H
#define ZCASH_UTIL_MATCH_H
// Helper for using `std::visit` with Rust-style match syntax.
//
// This can be used in place of defining an explicit visitor. `std::visit` requires an
// exhaustive match; to emulate Rust's catch-all binding, use an `(auto arg)` template
// operator().
//
// Care must be taken that implicit conversions are handled correctly. For instance, a
// `(double arg)` operator() *will also* bind to `int` and `long`, if there isn't an
// earlier satisfying match.
//
// This corresponds to visitor example #4 in the `std::visit` documentation:
// https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts> struct match : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> match(Ts...) -> match<Ts...>;
#endif // ZCASH_UTIL_MATCH_H

View File

@ -2199,19 +2199,19 @@ TEST(WalletTests, GenerateUnifiedAddress) {
// Create an account, then generate an address for that account.
auto skpair = wallet.GenerateNewUnifiedSpendingKey();
uaResult = wallet.GenerateUnifiedAddress(
skpair.second.GetAccountId(),
skpair.second,
diversifier_index_t(0),
{ReceiverType::P2PKH, ReceiverType::Sapling});
bool success = std::holds_alternative<std::pair<UnifiedAddress, ZcashdUnifiedAddressMetadata>>(uaResult);
EXPECT_TRUE(success);
auto ua = std::get_if<std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>>(&uaResult);
EXPECT_NE(ua, nullptr);
EXPECT_TRUE(ua->first.GetSaplingReceiver().has_value());
auto ufvk = skpair.first.ToFullViewingKey();
auto addrpair = std::get<std::pair<UnifiedAddress, ZcashdUnifiedAddressMetadata>>(uaResult);
EXPECT_TRUE(addrpair.first.GetSaplingReceiver().has_value());
EXPECT_EQ(
addrpair.first.GetSaplingReceiver(),
ufvk.GetSaplingKey().value().Address(addrpair.second.GetDiversifierIndex()));
ua->first.GetSaplingReceiver(),
ufvk.GetSaplingKey().value().Address(ua->second));
auto u4r = wallet.GetUnifiedForReceiver(addrpair.first.GetSaplingReceiver().value());
EXPECT_EQ(u4r, addrpair.first);
auto u4r = wallet.GetUnifiedForReceiver(ua->first.GetSaplingReceiver().value());
EXPECT_EQ(u4r, ua->first);
}

View File

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

View File

@ -421,7 +421,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi
return false;
}
std::pair<ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount> CWallet::GenerateNewUnifiedSpendingKey() {
std::pair<ZcashdUnifiedSpendingKey, libzcash::AccountId> CWallet::GenerateNewUnifiedSpendingKey() {
AssertLockHeld(cs_wallet);
if (!mnemonicHDChain.has_value()) {
@ -431,7 +431,8 @@ std::pair<ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount> CWallet::GenerateNewUn
CHDChain& hdChain = mnemonicHDChain.value();
while (true) {
auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter());
auto accountId = hdChain.GetAccountCounter();
auto usk = GenerateUnifiedSpendingKeyForAccount(accountId);
hdChain.IncrementAccountCounter();
if (usk.has_value()) {
@ -441,14 +442,14 @@ std::pair<ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount> CWallet::GenerateNewUn
"CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed");
}
return usk.value();
return std::make_pair(usk.value(), accountId);
}
}
}
std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount>>
std::optional<libzcash::ZcashdUnifiedSpendingKey>
CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) {
AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta
AssertLockHeld(cs_wallet); // mapUnifiedAccountKeys
auto seed = GetMnemonicSeed();
if (!seed.has_value()) {
@ -457,11 +458,10 @@ std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount
auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId);
if (usk.has_value()) {
auto sk = usk.value();
auto ufvk = sk.ToFullViewingKey();
auto ufvk = usk.value().ToFullViewingKey();
auto ufvkid = ufvk.GetKeyID(Params());
ZcashdUnifiedAccount skmeta(seed.value().Fingerprint(), BIP44CoinType(), accountId, ufvkid);
ZcashdUnifiedAccountMetadata skmeta(seed.value().Fingerprint(), BIP44CoinType(), accountId, ufvkid);
// We don't store the spending key directly; instead, we store each of
// the spending key's components, in order to not violate invariants
@ -471,19 +471,19 @@ std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount
// the fingerprint of the associated full viewing key.
auto metaKey = std::make_pair(skmeta.GetSeedFingerprint(), skmeta.GetAccountId());
mapUnifiedSpendingKeyMeta.insert({metaKey, skmeta});
mapUnifiedAccountKeys.insert({metaKey, skmeta.GetKeyID()});
// Add Transparent component to the wallet
AddTransparentSecretKey(
skmeta.GetSeedFingerprint(),
std::make_pair(
sk.GetTransparentKey().key,
usk.value().GetTransparentKey().key,
libzcash::Bip44TransparentAccountKeyPath(BIP44CoinType(), accountId)
)
);
// Add Sapling component to the wallet
auto saplingEsk = sk.GetSaplingExtendedSpendingKey();
auto saplingEsk = usk.value().GetSaplingExtendedSpendingKey();
auto saplingKeyPath = libzcash::Zip32AccountKeyPath(BIP44CoinType(), accountId);
auto addSpendingKey = AddSpendingKeyToWallet(
this, Params().GetConsensus(), GetTime(),
@ -501,13 +501,13 @@ std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount
if (fFileBacked) {
auto walletdb = CWalletDB(strWalletFile);
if (!( walletdb.WriteUnifiedFullViewingKey(ufvk) &&
walletdb.WriteUnifiedAccount(skmeta)
walletdb.WriteUnifiedAccountMetadata(skmeta)
)) {
throw std::runtime_error("CWalletDB::GenerateUnifiedSpendingKeyForAccount(): walletdb write failed.");
}
}
return std::make_pair(sk, skmeta);
return usk;
} else {
return std::nullopt;
}
@ -537,10 +537,9 @@ std::optional<ZcashdUnifiedFullViewingKey> CWallet::GetUnifiedFullViewingKeyByAc
}
auto seedfp = mnemonicHDChain.value().GetSeedFingerprint();
auto i = mapUnifiedSpendingKeyMeta.find(std::make_pair(seedfp, accountId));
if (i != mapUnifiedSpendingKeyMeta.end()) {
auto keyId = i->second.GetKeyID();
return CCryptoKeyStore::GetUnifiedFullViewingKey(keyId);
auto entry = mapUnifiedAccountKeys.find(std::make_pair(seedfp, accountId));
if (entry != mapUnifiedAccountKeys.end()) {
return CCryptoKeyStore::GetUnifiedFullViewingKey(entry->second);
} else {
return std::nullopt;
}
@ -571,37 +570,42 @@ UAGenerationResult CWallet::GenerateUnifiedAddress(
}
}
auto identifiedKey = GetUnifiedFullViewingKeyByAccount(accountId);
if (identifiedKey.has_value()) {
auto ufvk = identifiedKey.value();
auto ufvk = GetUnifiedFullViewingKeyByAccount(accountId);
if (ufvk.has_value()) {
auto ufvkid = ufvk.value().GetKeyID();
// Check whether an address has already been generated for this
// diversifier index. If so, ensure that the set of receiver types
// being requested is the same as the set of receiver types that was
// previously generated; if so, return the previously generated address,
// previously generated & return the previously generated address,
// otherwise return an error.
auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvk.GetKeyID());
if (metadata != mapUnifiedFullViewingKeyMeta.end()) {
auto metadata = mapUfvkAddressMetadata.find(ufvkid);
if (metadata != mapUfvkAddressMetadata.end()) {
auto receivers = metadata->second.GetReceivers(j);
if (receivers.has_value()) {
if (receivers.value() == receiverTypes) {
ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), j, receiverTypes);
auto addr = ufvk.Address(j, receiverTypes);
return std::make_pair(addr.value(), addrmeta);
return std::make_pair(ufvk.value().Address(j, receiverTypes).value(), j);
} else {
return AddressGenerationError::ExistingAddressMismatch;
}
}
} else {
mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()] = ZcashdUnifiedFullViewingKeyMetadata(accountId);
}
// Find a working diversifier and construct the associated address.
auto foundAddress = ufvk.FindAddress(j, receiverTypes);
auto foundAddress = ufvk.value().FindAddress(j, receiverTypes);
auto diversifierIndex = foundAddress.second;
// Persist the newly created address to the keystore
AddUnifiedAddress(ufvk.GetKeyID(), foundAddress);
mapUfvkAddressMetadata[ufvkid].SetReceivers(diversifierIndex, receiverTypes);
CCryptoKeyStore::AddUnifiedAddress(ufvkid, diversifierIndex, foundAddress.first);
// Save the metadata for the generated address so that we can re-derive
// it in the future.
ZcashdUnifiedAddressMetadata addrmeta(ufvkid, diversifierIndex, receiverTypes);
if (fFileBacked && !CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta)) {
throw std::runtime_error(
"CWallet::AdddUnifiedAddress(): Writing unified address metadata failed");
}
if (hasTransparent) {
// Regenerate the secret key for the transparent address and add it to
@ -612,65 +616,66 @@ UAGenerationResult CWallet::GenerateUnifiedAddress(
AddTransparentSecretKey(seed.Fingerprint(), key);
}
// Save the metadata for the generated address so that we can re-derive
// it in the future.
ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), diversifierIndex, receiverTypes);
// we can safely ignore the return value here; we know that we're adding a new
// set of receivers given the receivers.has_value() check above.
mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()].SetReceivers(diversifierIndex, receiverTypes);
if (fFileBacked) {
CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta);
}
return std::make_pair(foundAddress.first, addrmeta);
return foundAddress;
} else {
return AddressGenerationError::NoSuchAccount;
}
}
// NOTE: There is an important relationship between `LoadUnifiedFullViewingKey`
// and `LoadUnifiedAddressMetadata`. In all cases, by the end of the
// wallet-loading process, `mapUfvkAddressMetadata` needs to be fully populated
// for each unified full viewing key. However, we cannot guarantee in what
// order UFVKs and unified address and account metadata are loaded from disk.
// Therefore:
// * When we load a unified full viewing key from disk, we repopulate
// the cache of generated addresses by regenerating the address for each
// (diversifierIndex, {receiverType...}) pair in `mapUfvkAddressMetadata`
// that we have loaded so far.
// * When we load a unified address metadata record from disk:
// - if we have not yet loaded the unified full viewing key, simply add
// an entry `mapUfvkAddressMetadata` so that we can restore the address
// once the UFVK has been loaded.
// - if we have already loaded the unified full viewing key from disk,
// regenerate the address from its metadata and add it to the keystore
bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key)
{
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key);
auto metadata = mapUnifiedFullViewingKeyMeta.find(zufvk.GetKeyID());
if (metadata != mapUnifiedFullViewingKeyMeta.end()) {
auto metadata = mapUfvkAddressMetadata.find(zufvk.GetKeyID());
if (metadata != mapUfvkAddressMetadata.end()) {
// restore unified addresses that have been previously generated to the
// keystore
for (const auto &[j, receiverTypes] : metadata->second.GetAllReceivers()) {
auto addr = zufvk.Address(j, receiverTypes).value();
AddUnifiedAddress(zufvk.GetKeyID(), std::make_pair(addr, j));
CCryptoKeyStore::AddUnifiedAddress(zufvk.GetKeyID(), j, addr);
}
}
return CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk);
}
bool CWallet::LoadUnifiedAccount(const ZcashdUnifiedAccount &skmeta)
bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta)
{
AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta
AssertLockHeld(cs_wallet); // mapUnifiedAccountKeys
auto metaKey = std::make_pair(skmeta.GetSeedFingerprint(), skmeta.GetAccountId());
mapUnifiedSpendingKeyMeta.insert({metaKey, skmeta});
if (mapUnifiedFullViewingKeyMeta.count(skmeta.GetKeyID()) == 0) {
return mapUnifiedFullViewingKeyMeta[skmeta.GetKeyID()].SetAccountId(skmeta.GetAccountId());
}
return true;
mapUnifiedAccountKeys.insert({metaKey, skmeta.GetKeyID()});
return mapUfvkAddressMetadata[skmeta.GetKeyID()].SetAccountId(skmeta.GetAccountId());
}
bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta)
{
AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta
AssertLockHeld(cs_wallet);
mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers(
addrmeta.GetDiversifierIndex(),
addrmeta.GetReceiverTypes());
auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID());
if (ufvk.has_value()) {
// restore unified addresses that have been previously generated
// Regenerate the unified address and add it to the keystore.
auto j = addrmeta.GetDiversifierIndex();
auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes());
if (addr.has_value()) {
AddUnifiedAddress(addrmeta.GetKeyID(), std::make_pair(addr.value(), j));
} else {
// an error has occurred; the ufvk is loaded but cannot reproduce the
// address identified by the address metadata.
return false;
}
auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes()).value();
return CCryptoKeyStore::AddUnifiedAddress(addrmeta.GetKeyID(), j, addr);
}
return true;
}
@ -5811,8 +5816,8 @@ std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const l
}
diversifier_index_t j;
auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid);
if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) {
auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid);
if (metadata != wallet.mapUfvkAddressMetadata.end()) {
librustzcash_sapling_diversifier_index(
ufvk.value().GetSaplingKey().value().dk.begin(),
saplingAddr.d.begin(),
@ -5851,8 +5856,8 @@ std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const C
// Find the set of receivers at the diversifier index. If no metadata is available
// for the ufvk, or we do not know the receiver types for the address produced
// at this diversifier, we cannot reconstruct the address.
auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid);
if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) {
auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid);
if (metadata != wallet.mapUfvkAddressMetadata.end()) {
auto receivers = metadata->second.GetReceivers(j);
if (receivers.has_value()) {
return ufvk.value().Address(j, receivers.value());

View File

@ -412,7 +412,7 @@ enum class AddressGenerationError {
};
typedef std::variant<
std::pair<libzcash::UnifiedAddress, ZcashdUnifiedAddressMetadata>,
std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>,
AddressGenerationError> UAGenerationResult;
/**
@ -681,14 +681,14 @@ public:
}
};
class ZcashdUnifiedFullViewingKeyMetadata
class UFVKAddressMetadata
{
private:
std::optional<libzcash::AccountId> accountId;
std::map<libzcash::diversifier_index_t, std::set<libzcash::ReceiverType>> addressReceivers;
public:
ZcashdUnifiedFullViewingKeyMetadata() {}
ZcashdUnifiedFullViewingKeyMetadata(libzcash::AccountId accountId): accountId(accountId) {}
UFVKAddressMetadata() {}
UFVKAddressMetadata(libzcash::AccountId accountId): accountId(accountId) {}
const std::map<libzcash::diversifier_index_t, std::set<libzcash::ReceiverType>>& GetAllReceivers() const {
return addressReceivers;
@ -704,10 +704,21 @@ public:
}
}
/**
* Add the specified set of receivers at the provided diversifier index.
*
* Returns `true` if this is a new entry or if the operation would not
* alter the existing set of receiver types at this index, `false`
* otherwise.
*/
bool SetReceivers(
const libzcash::diversifier_index_t& j,
const std::set<libzcash::ReceiverType>& receivers) {
return addressReceivers.insert(std::make_pair(j, receivers)).second;
if (addressReceivers.count(j) > 0) {
return addressReceivers[j] == receivers;
} else {
return addressReceivers.insert(std::make_pair(j, receivers)).second;
}
}
bool SetAccountId(libzcash::AccountId accountIdIn) {
@ -888,8 +899,8 @@ public:
std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapSproutZKeyMetadata;
std::map<libzcash::SaplingIncomingViewingKey, CKeyMetadata> mapSaplingZKeyMetadata;
std::map<std::pair<libzcash::SeedFingerprint, libzcash::AccountId>, ZcashdUnifiedAccount> mapUnifiedSpendingKeyMeta;
std::map<libzcash::UFVKId, ZcashdUnifiedFullViewingKeyMetadata> mapUnifiedFullViewingKeyMeta;
std::map<std::pair<libzcash::SeedFingerprint, libzcash::AccountId>, libzcash::UFVKId> mapUnifiedAccountKeys;
std::map<libzcash::UFVKId, UFVKAddressMetadata> mapUfvkAddressMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
@ -1164,12 +1175,12 @@ public:
//! Generate the unified spending key from the wallet's mnemonic seed
//! for the next unused account identifier.
std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount>
std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::AccountId>
GenerateNewUnifiedSpendingKey();
//! Generate the next available unified spending key from the wallet's
//! mnemonic seed.
std::optional<std::pair<libzcash::ZcashdUnifiedSpendingKey, ZcashdUnifiedAccount>>
std::optional<libzcash::ZcashdUnifiedSpendingKey>
GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId);
//! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account.
@ -1183,14 +1194,14 @@ public:
const libzcash::diversifier_index_t& j,
const std::set<libzcash::ReceiverType>& receivers);
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver);
bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
bool LoadUnifiedAccount(const ZcashdUnifiedAccount &skmeta);
bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver);
/**
* Increment the next transaction order id
* @return next transaction order id

View File

@ -221,10 +221,9 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey(
// Unified address & key storage
//
bool CWalletDB::WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta)
bool CWalletDB::WriteUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata& keymeta)
{
nWalletDBUpdateCounter++;
auto ufvkId = keymeta.GetKeyID();
return Write(std::make_pair(std::string("unifiedaccount"), keymeta), 0x00);
}
@ -238,8 +237,7 @@ bool CWalletDB::WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey
bool CWalletDB::WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta)
{
nWalletDBUpdateCounter++;
auto ufvkId = addrmeta.GetKeyID();
return Write(std::make_pair(std::string("unifiedaddrmeta"), ufvkId), addrmeta);
return Write(std::make_pair(std::string("unifiedaddrmeta"), addrmeta), 0x00);
}
//
@ -688,23 +686,31 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
else if (strType == "unifiedaccount")
{
auto acct = ZcashdUnifiedAccount::Read(ssKey);
auto acct = ZcashdUnifiedAccountMetadata::Read(ssKey);
uint8_t value;
ssValue >> value;
if (value != 0x00) {
strErr = "Error reading wallet database: invalid unified account value.";
strErr = "Error reading wallet database: invalid unified account metadata.";
return false;
}
if (!pwallet->LoadUnifiedAccount(acct)) {
if (!pwallet->LoadUnifiedAccountMetadata(acct)) {
strErr = "Error reading wallet database: account ID mismatch for unified spending key.";
return false;
};
}
else if (strType == "unifiedaddrmeta")
{
auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssValue);
auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssKey);
uint8_t value;
ssValue >> value;
if (value != 0x00) {
strErr = "Error reading wallet database: invalid unified address metadata.";
return false;
}
if (!pwallet->LoadUnifiedAddressMetadata(keymeta)) {
strErr = "Error reading wallet database: cannot reproduce previously generated unified address.";
return false;

View File

@ -172,16 +172,16 @@ public:
}
};
class ZcashdUnifiedAccount {
class ZcashdUnifiedAccountMetadata {
private:
libzcash::SeedFingerprint seedFp;
uint32_t bip44CoinType;
libzcash::AccountId accountId;
libzcash::UFVKId ufvkId;
ZcashdUnifiedAccount() {}
ZcashdUnifiedAccountMetadata() {}
public:
ZcashdUnifiedAccount(
ZcashdUnifiedAccountMetadata(
libzcash::SeedFingerprint seedFp,
uint32_t bip44CoinType,
libzcash::AccountId accountId,
@ -216,8 +216,8 @@ public:
}
template <typename Stream>
static ZcashdUnifiedAccount Read(Stream& stream) {
ZcashdUnifiedAccount meta;
static ZcashdUnifiedAccountMetadata Read(Stream& stream) {
ZcashdUnifiedAccountMetadata meta;
stream >> meta;
return meta;
}
@ -386,7 +386,7 @@ public:
/// Unified key support.
bool WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta);
bool WriteUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata& keymeta);
bool WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk);
bool WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta);

View File

@ -6,6 +6,7 @@
#include "key_constants.h"
#include "script/script.h"
#include "uint256.h"
#include "util/match.h"
#include "zcash/address/orchard.hpp"
#include "zcash/address/sapling.hpp"
#include "zcash/address/sprout.hpp"
@ -116,6 +117,26 @@ public:
const std::vector<Receiver>& GetReceiversAsParsed() const { return receivers; }
std::set<ReceiverType> GetKnownReceiverTypes() const {
std::set<ReceiverType> result;
for (const auto& receiver : receivers) {
std::visit(match {
[&](const libzcash::SaplingPaymentAddress &zaddr) {
result.insert(ReceiverType::Sapling);
},
[&](const CScriptID &zaddr) {
result.insert(ReceiverType::P2SH);
},
[&](const CKeyID &zaddr) {
result.insert(ReceiverType::P2PKH);
},
[&](const libzcash::UnknownReceiver &uaddr) {
}
}, receiver);
}
return result;
}
ReceiverIterator begin() const {
return ReceiverIterator(GetSorted(), 0);
}