Correctly report change outputs in z_viewtransaction.

One of the invariants that we want to hold for unified addresses
is that we never want change addresses to be visible to users.
This updates address metadata retrieval to also indicate whether
or not an address is associated with a UFVK's internal key,
and omits and flags change addresses in z_viewtransaction output.
This commit is contained in:
Kris Nuttycombe 2022-03-18 11:09:35 -06:00
parent 7c3afad111
commit 4df6c028f4
7 changed files with 208 additions and 116 deletions

View File

@ -564,8 +564,8 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
// we trial-decrypt diversifiers (which also means we learn the index).
auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_TRUE(ufvkmetaUnadded.has_value());
EXPECT_EQ(ufvkmetaUnadded.value().first, ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().second.value(), addrPair.second);
EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
// Adding the Sapling addr -> ivk map entry causes us to find the same UFVK,
// but as we're no longer trial-decrypting we don't learn the index.
@ -574,8 +574,8 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_TRUE(ufvkmeta.has_value());
EXPECT_EQ(ufvkmeta.value().first, ufvkid);
EXPECT_FALSE(ufvkmeta.value().second.has_value());
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
EXPECT_FALSE(ufvkmeta.value().GetDiversifierIndex().has_value());
}
TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {
@ -603,8 +603,8 @@ TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {
// we trial-decrypt diversifiers (which also means we learn the index).
auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(orchardReceiver);
EXPECT_TRUE(ufvkmetaUnadded.has_value());
EXPECT_EQ(ufvkmetaUnadded.value().first, ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().second.value(), addrPair.second);
EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
}
TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) {
@ -627,7 +627,7 @@ TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) {
ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value());
EXPECT_TRUE(ufvkmeta.has_value());
EXPECT_EQ(ufvkmeta.value().first, ufvkid);
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
}

View File

@ -383,13 +383,13 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> CBasicKeyStore::GetUnifiedF
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
std::optional<AddressUFVKMetadata>
CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) const
{
return std::visit(FindUFVKId(*this), receiver);
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
std::optional<AddressUFVKMetadata>
CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) const
{
std::optional<libzcash::UFVKId> ufvkId;
@ -398,33 +398,36 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
for (const auto& receiver : addr) {
// skip unknown receivers
if (libzcash::HasKnownReceiverType(receiver)) {
auto tmp = GetUFVKMetadataForReceiver(receiver);
if (ufvkId.has_value() && tmp.has_value()) {
auto rmeta = GetUFVKMetadataForReceiver(receiver);
// We should never generate unified addresses with internal receivers
assert(!(rmeta.has_value() && rmeta.value().IsInternalAddress()));
if (ufvkId.has_value() && rmeta.has_value()) {
// If the unified address contains receivers that are associated with
// different UFVKs, we cannot return a singular value.
if (tmp.value().first != ufvkId.value()) {
if (rmeta.value().GetUFVKId() != ufvkId.value()) {
return std::nullopt;
}
if (tmp.value().second.has_value()) {
if (rmeta.value().GetDiversifierIndex().has_value()) {
if (j.has_value()) {
if (tmp.value().second.value() != j.value()) {
if (rmeta.value().GetDiversifierIndex().value() != j.value()) {
jConflict = true;
j = std::nullopt;
}
} else if (!jConflict) {
j = tmp.value().second.value();
j = rmeta.value().GetDiversifierIndex().value();
}
}
} else if (tmp.has_value()) {
ufvkId = tmp.value().first;
j = tmp.value().second;
} else if (rmeta.has_value()) {
ufvkId = rmeta.value().GetUFVKId();
j = rmeta.value().GetDiversifierIndex();
}
}
}
if (ufvkId.has_value()) {
return std::make_pair(ufvkId.value(), j);
return AddressUFVKMetadata(ufvkId.value(), j, false);
} else {
return std::nullopt;
}
@ -465,22 +468,27 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
return result;
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
//
// FindUFVKId :: (KeyStore, Receiver) -> std::optional<AddressUFVKMetadata>
//
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
auto fvk = v.GetOrchardKey();
if (fvk.has_value()) {
auto d_idx = fvk.value().ToIncomingViewingKey().DecryptDiversifier(orchardAddr);
if (d_idx.has_value()) {
return std::make_pair(k, d_idx);
return AddressUFVKMetadata(k, d_idx, false);
}
auto internal_d_idx = fvk.value().ToInternalIncomingViewingKey().DecryptDiversifier(orchardAddr);
if (internal_d_idx.has_value()) {
return AddressUFVKMetadata(k, internal_d_idx, true);
}
}
}
return std::nullopt;
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr);
if (saplingIvk != keystore.mapSaplingIncomingViewingKeys.end()) {
// We have either generated this as a receiver via `z_getaddressforaccount` or a
@ -488,7 +496,7 @@ FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const
// this via trial-decryption of a note.
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
return std::make_pair(ufvkId->second, std::nullopt);
return AddressUFVKMetadata(ufvkId->second, std::nullopt, false);
} else {
// If we have the addr -> ivk map entry but not the ivk -> UFVK map entry,
// then this is definitely a legacy Sapling address.
@ -506,7 +514,13 @@ FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr.d);
auto derived_addr = dfvk.value().Address(d_idx);
if (derived_addr.has_value() && derived_addr.value() == saplingAddr) {
return std::make_pair(k, d_idx);
return AddressUFVKMetadata(k, d_idx, false);
}
auto internal_d_idx = dfvk.value().DecryptInternalDiversifier(saplingAddr.d);
auto derived_internal_addr = dfvk.value().InternalAddress(internal_d_idx);
if (derived_internal_addr.has_value() && derived_internal_addr.value() == saplingAddr) {
return AddressUFVKMetadata(k, internal_d_idx, true);
}
}
}
@ -514,25 +528,26 @@ FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const
// We definitely don't know of any UFVK linked to this Sapling address.
return std::nullopt;
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CScriptID& scriptId) const {
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CScriptID& scriptId) const {
const auto metadata = keystore.mapP2SHUnified.find(scriptId);
if (metadata != keystore.mapP2SHUnified.end()) {
return metadata->second;
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CKeyID& keyId) const {
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CKeyID& keyId) const {
const auto metadata = keystore.mapP2PKHUnified.find(keyId);
if (metadata != keystore.mapP2PKHUnified.end()) {
return metadata->second;
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const {
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const {
return std::nullopt;
}

View File

@ -18,6 +18,20 @@
#include <boost/signals2/signal.hpp>
class AddressUFVKMetadata {
private:
libzcash::UFVKId ufvkId;
std::optional<libzcash::diversifier_index_t> j;
bool internalAddress;
public:
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool internalAddress)
: ufvkId(ufvkId), j(j), internalAddress(internalAddress) {}
libzcash::UFVKId GetUFVKId() const { return ufvkId; }
std::optional<libzcash::diversifier_index_t> GetDiversifierIndex() const { return j; }
bool IsInternalAddress() const { return internalAddress; }
};
/** A virtual base class for key stores */
class CKeyStore
{
@ -127,8 +141,7 @@ public:
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId) const = 0;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver) const = 0;
/**
@ -136,8 +149,7 @@ public:
* UFVK, return that key's metadata. If all the receivers correspond to
* the same diversifier index, that diversifier index is also returned.
*/
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForAddress(
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForAddress(
const libzcash::UnifiedAddress& addr) const = 0;
virtual std::optional<libzcash::UFVKId> GetUFVKIdForViewingKey(
@ -379,29 +391,27 @@ public:
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId) const;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUFVKForReceiver(
const libzcash::Receiver& receiver) const {
auto ufvkMeta = GetUFVKMetadataForReceiver(receiver);
if (ufvkMeta.has_value()) {
return GetUnifiedFullViewingKey(ufvkMeta.value().first);
return GetUnifiedFullViewingKey(ufvkMeta.value().GetUFVKId());
} else {
return std::nullopt;
}
}
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForAddress(
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForAddress(
const libzcash::UnifiedAddress& addr) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUFVKForAddress(
const libzcash::UnifiedAddress& addr) const {
auto ufvkMeta = GetUFVKMetadataForAddress(addr);
if (ufvkMeta.has_value()) {
return GetUnifiedFullViewingKey(ufvkMeta.value().first);
return GetUnifiedFullViewingKey(ufvkMeta.value().GetUFVKId());
} else {
return std::nullopt;
}
@ -425,16 +435,11 @@ private:
public:
FindUFVKId(const CBasicKeyStore& keystore): keystore(keystore) {}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CScriptID& scriptId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CKeyID& keyId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::UnknownReceiver& receiver) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<AddressUFVKMetadata> operator()(const CScriptID& scriptId) const;
std::optional<AddressUFVKMetadata> operator()(const CKeyID& keyId) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::UnknownReceiver& receiver) const;
};
#endif // BITCOIN_KEYSTORE_H

View File

@ -4224,16 +4224,20 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::string address = keyIO.EncodePaymentAddress(
pwalletMain->GetPaymentAddressForRecipient(txid, pa)
);
std::optional<std::string> addrStr;
if (!pwalletMain->IsInternalRecipient(pa)) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
addrStr = keyIO.EncodePaymentAddress(addr);
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING);
entry.pushKV("spend", (int)i);
entry.pushKV("txidPrev", op.hash.GetHex());
entry.pushKV("outputPrev", (int)op.n);
entry.pushKV("address", address);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}
entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value());
spends.push_back(entry);
@ -4273,15 +4277,21 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::string address = keyIO.EncodePaymentAddress(
pwalletMain->GetPaymentAddressForRecipient(txid, pa)
);
std::optional<std::string> addrStr;
bool isInternal = pwalletMain->IsInternalRecipient(pa);
if (!isInternal) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
addrStr = keyIO.EncodePaymentAddress(addr);
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING);
entry.pushKV("output", (int)op.n);
entry.pushKV("outgoing", isOutgoing);
entry.pushKV("address", address);
entry.pushKV("walletInternal", isInternal);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}
entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value());
addMemo(entry, memo);
@ -4293,24 +4303,27 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Orchard spends
for (auto & pair : orchardActions.GetSpends()) {
// TODO test with Orchard spends after rebased on feature/wallet-orchard
auto actionIdx = pair.first;
OrchardActionSpend orchardActionSpend = pair.second;
auto outpoint = orchardActionSpend.GetOutPoint();
auto receivedAt = orchardActionSpend.GetReceivedAt();
auto noteValue = orchardActionSpend.GetNoteValue();
// if mapWallet doesn't contain the spent outpoint's txid,
// the wallet is corrupt, so using `.at` here is fine.
std::optional<std::string> addrStr;
if (!pwalletMain->IsInternalRecipient(receivedAt)) {
auto ua = pwalletMain->FindUnifiedAddressByReceiver(receivedAt);
assert(ua.has_value());
addrStr = keyIO.EncodePaymentAddress(ua.value());
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_ORCHARD);
entry.pushKV("action", (int) actionIdx);
entry.pushKV("txidPrev", outpoint.hash.GetHex());
entry.pushKV("actionPrev", (int) outpoint.n);
auto ua = pwalletMain->FindUnifiedAddressByReceiver(receivedAt);
assert(ua.has_value());
std::string addrStr = keyIO.EncodePaymentAddress(ua.value());
entry.pushKV("address", addrStr);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}
entry.pushKV("value", ValueFromAmount(noteValue));
entry.pushKV("valueZat", noteValue);
spends.push_back(entry);
@ -4324,15 +4337,21 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::string address = keyIO.EncodePaymentAddress(
pwalletMain->GetPaymentAddressForRecipient(txid, recipient)
);
std::optional<std::string> addrStr;
bool isInternal = pwalletMain->IsInternalRecipient(recipient);
if (!isInternal) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, recipient);
addrStr = keyIO.EncodePaymentAddress(addr);
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_ORCHARD);
entry.pushKV("action", (int) actionIdx);
entry.pushKV("outgoing", orchardActionOutput.IsOutgoing());
entry.pushKV("address", address);
entry.pushKV("walletInternal", isInternal);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}
entry.pushKV("value", ValueFromAmount(noteValue));
entry.pushKV("valueZat", noteValue);
addMemo(entry, memo);

View File

@ -853,8 +853,10 @@ bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &add
addrmeta.GetReceiverTypes());
}
PaymentAddress CWallet::GetPaymentAddressForRecipient(const uint256& txid, const libzcash::RecipientAddress recipient) const {
// TODO: We should scrub change addresses from the results here.
PaymentAddress CWallet::GetPaymentAddressForRecipient(
const uint256& txid,
const libzcash::RecipientAddress& recipient) const
{
auto defaultAddress = [&]() -> PaymentAddress {
return std::visit(match {
[&](const CKeyID& addr) {
@ -909,6 +911,41 @@ PaymentAddress CWallet::GetPaymentAddressForRecipient(const uint256& txid, const
}
}
bool CWallet::IsInternalRecipient(const libzcash::RecipientAddress& recipient) const
{
return std::visit(match {
[&](const CKeyID& addr) {
// we never send transparent change when sending to or from a
// unified address
return false;
},
[&](const CScriptID& addr) {
// we never use P2SH for change or shielding
return false;
},
[&](const SaplingPaymentAddress& addr) {
auto ufvk = pwalletMain->GetUFVKForReceiver(addr);
if (ufvk.has_value()) {
auto changeAddr = ufvk->GetChangeAddress(SaplingChangeRequest());
if (changeAddr.has_value()) {
return changeAddr.value() == recipient;
}
}
return false;
},
[&](const OrchardRawAddress& addr) {
auto ufvk = pwalletMain->GetUFVKForReceiver(addr);
if (ufvk.has_value()) {
auto changeAddr = ufvk->GetChangeAddress(OrchardChangeRequest());
if (changeAddr.has_value()) {
return changeAddr.value() == recipient;
}
}
return false;
}
}, recipient);
}
void CWallet::LoadRecipientMapping(const uint256& txid, const RecipientMapping& mapping) {
sendRecipients[txid].push_back(mapping);
}
@ -1733,14 +1770,14 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(
}
},
[&](const libzcash::UnifiedAddress& ua) {
auto ufvkMeta = this->GetUFVKMetadataForAddress(ua);
auto ufvkMeta = GetUFVKMetadataForAddress(ua);
if (ufvkMeta.has_value()) {
// TODO: at present, the `false` value for the `requireSpendingKey` argument
// is not respected for unified addresses, because we have no notion of
// an account for which we do not control the spending key. An alternate
// approach would be to use the UFVK directly in the case that we cannot
// determine a local account.
auto accountId = this->GetUnifiedAccountId(ufvkMeta.value().first);
auto accountId = this->GetUnifiedAccountId(ufvkMeta.value().GetUFVKId());
if (accountId.has_value()) {
if (allowAddressLinkability) {
pattern = AccountZTXOPattern(accountId.value(), ua.GetKnownReceiverTypes());
@ -1809,13 +1846,13 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
[&](const CKeyID& addr) {
auto meta = self->GetUFVKMetadataForReceiver(addr);
if (meta.has_value()) {
result = self->GetUnifiedAccountId(meta.value().first);
result = self->GetUnifiedAccountId(meta.value().GetUFVKId());
}
},
[&](const CScriptID& addr) {
auto meta = self->GetUFVKMetadataForReceiver(addr);
if (meta.has_value()) {
result = self->GetUnifiedAccountId(meta.value().first);
result = self->GetUnifiedAccountId(meta.value().GetUFVKId());
}
},
[&](const libzcash::SproutPaymentAddress& addr) { },
@ -1823,7 +1860,7 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
[&](const libzcash::SaplingPaymentAddress& addr) {
auto meta = GetUFVKMetadataForReceiver(addr);
if (meta.has_value()) {
result = self->GetUnifiedAccountId(meta.value().first);
result = self->GetUnifiedAccountId(meta.value().GetUFVKId());
}
},
[&](const libzcash::SaplingExtendedFullViewingKey& vk) {
@ -1835,7 +1872,7 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
[&](const libzcash::UnifiedAddress& addr) {
auto meta = GetUFVKMetadataForAddress(addr);
if (meta.has_value()) {
result = self->GetUnifiedAccountId(meta.value().first);
result = self->GetUnifiedAccountId(meta.value().GetUFVKId());
}
},
[&](const libzcash::UnifiedFullViewingKey& vk) {
@ -1894,16 +1931,16 @@ bool CWallet::SelectorMatchesAddress(
return false;
},
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>> meta;
std::optional<AddressUFVKMetadata> meta;
std::visit(match {
[&](const CNoDestination& none) { meta = std::nullopt; },
[&](const auto& addr) { meta = self->GetUFVKMetadataForReceiver(addr); }
}, address);
return (meta.has_value() && meta.value().first == ufvk.GetKeyID(Params()));
return (meta.has_value() && meta.value().GetUFVKId() == ufvk.GetKeyID(Params()));
},
[&](const AccountZTXOPattern& acct) {
if (acct.IncludesP2PKH() || acct.IncludesP2SH()) {
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>> meta;
std::optional<AddressUFVKMetadata> meta;
std::visit(match {
[&](const CNoDestination& none) { meta = std::nullopt; },
[&](const auto& addr) { meta = self->GetUFVKMetadataForReceiver(addr); }
@ -1911,7 +1948,7 @@ bool CWallet::SelectorMatchesAddress(
if (meta.has_value()) {
// use the coin if the account id corresponding to the UFVK is
// the payment source account.
return self->GetUnifiedAccountId(meta.value().first) == std::optional(acct.GetAccountId());
return self->GetUnifiedAccountId(meta.value().GetUFVKId()) == std::optional(acct.GetAccountId());
} else {
// The legacy account is treated as a single pool of
// transparent funds, reproducing wallet behavior prior to
@ -1960,7 +1997,7 @@ bool CWallet::SelectorMatchesAddress(
// the Sapling component of the unified address, we consider that a
// match
return a0Meta.has_value() && uaMeta.has_value() &&
a0Meta.value().first == uaMeta.value().first;
a0Meta.value().GetUFVKId() == uaMeta.value().GetUFVKId();
}
return false;
},
@ -1980,7 +2017,7 @@ bool CWallet::SelectorMatchesAddress(
if (meta.has_value()) {
// use the coin if the account id corresponding to the UFVK is
// the payment source account.
return self->GetUnifiedAccountId(meta.value().first) == std::optional(acct.GetAccountId());
return self->GetUnifiedAccountId(meta.value().GetUFVKId()) == std::optional(acct.GetAccountId());
} else {
return false;
}
@ -2182,7 +2219,7 @@ SpendableInputs CWallet::FindSpendableInputs(
if (orchardReceiver.has_value()) {
auto meta = GetUFVKMetadataForReceiver(orchardReceiver.value());
if (meta.has_value()) {
auto ufvk = GetUnifiedFullViewingKey(meta.value().first);
auto ufvk = GetUnifiedFullViewingKey(meta.value().GetUFVKId());
if (ufvk.has_value()) {
auto fvk = ufvk->GetOrchardKey();
if (fvk.has_value()) {
@ -6772,7 +6809,7 @@ PaymentAddressSource GetSourceForPaymentAddress::GetUnifiedSource(const libzcash
auto hdChain = m_wallet->GetMnemonicHDChain();
auto ufvkMeta = m_wallet->GetUFVKMetadataForReceiver(receiver);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().first;
auto ufvkid = ufvkMeta.value().GetUFVKId();
// Look through the UFVKs that we have generated, and confirm that the
// seed fingerprint for the key we find for the ufvkid corresponds to
// the wallet's mnemonic seed.
@ -6855,13 +6892,13 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
auto hdChain = m_wallet->GetMnemonicHDChain();
auto ufvkid = m_wallet->GetUFVKMetadataForAddress(uaddr);
if (ufvkid.has_value()) {
auto ufvkMeta = m_wallet->GetUFVKMetadataForAddress(uaddr);
if (ufvkMeta.has_value()) {
// Look through the UFVKs that we have generated, and confirm that the
// seed fingerprint for the key we find for the ufvkid corresponds to
// seed fingerprint for the key we find for the ufvkMeta corresponds to
// the wallet's mnemonic seed.
for (const auto& [k, v] : m_wallet->mapUnifiedAccountKeys) {
if (v == ufvkid.value().first && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) {
if (v == ufvkMeta.value().GetUFVKId() && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) {
return PaymentAddressSource::MnemonicHDSeed;
}
}
@ -7011,9 +7048,9 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(orchardAddr);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(orchardAddr);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
// If we have UFVK metadata, `GetUnifiedFullViewingKey` should always
// return a non-nullopt value, and since we obtained that metadata by
@ -7025,9 +7062,9 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()
}
}
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
// If we have UFVK metadata, `GetUnifiedFullViewingKey` should always
// return a non-nullopt value, and since we obtained that metadata by
@ -7044,12 +7081,12 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()
return std::nullopt;
}
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const CKeyID& keyId) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(keyId);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
// transparent address UFVK metadata is always accompanied by the child
// index at which the address was produced
assert(ufvkPair.value().second.has_value());
assert(ufvkMeta.value().GetDiversifierIndex().has_value());
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
assert(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value());
return ufvk.value();
@ -7065,9 +7102,9 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(
const libzcash::OrchardRawAddress& orchardAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(orchardAddr);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(orchardAddr);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
assert(ufvk.has_value());
@ -7093,9 +7130,9 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(
}
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
assert(ufvk.has_value());
@ -7123,13 +7160,13 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
return std::nullopt;
}
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CKeyID& keyId) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(keyId);
if (ufvkMeta.has_value()) {
auto ufvkid = ufvkMeta.value().GetUFVKId();
// transparent address UFVK metadata is always accompanied by the child
// index at which the address was produced
assert(ufvkPair.value().second.has_value());
diversifier_index_t j = ufvkPair.value().second.value();
assert(ufvkMeta.value().GetDiversifierIndex().has_value());
diversifier_index_t j = ufvkMeta.value().GetDiversifierIndex().value();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) {
throw std::runtime_error("CWallet::UnifiedAddressForReceiver(): UFVK has no P2PKH key part.");

View File

@ -1583,7 +1583,12 @@ public:
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
libzcash::PaymentAddress GetPaymentAddressForRecipient(const uint256& txid, const libzcash::RecipientAddress recipient) const;
libzcash::PaymentAddress GetPaymentAddressForRecipient(
const uint256& txid,
const libzcash::RecipientAddress& recipient) const;
bool IsInternalRecipient(
const libzcash::RecipientAddress& recipient) const;
void LoadRecipientMapping(const uint256& txid, const RecipientMapping& mapping);
//! Reconstructs (in memory) caches and mappings for unified accounts,

View File

@ -178,6 +178,17 @@ public:
return j;
}
// Attempts to construct a valid internal payment address with diversifier
// index `j`; returns std::nullopt if `j` does not result in a valid diversifier
// given this xfvk.
std::optional<libzcash::SaplingPaymentAddress> InternalAddress(diversifier_index_t j) const {
return GetInternalDFVK().Address(j);
}
diversifier_index_t DecryptInternalDiversifier(const diversifier_t& d) const {
return GetInternalDFVK().DecryptDiversifier(d);
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>