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:
parent
ad5c4dcf09
commit
74e4bef6f2
|
@ -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());
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue