diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 5e9863cc1..6cdc8023d 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -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); } diff --git a/src/keystore.cpp b/src/keystore.cpp index 1af6a3218..30a8ede83 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -383,13 +383,13 @@ std::optional CBasicKeyStore::GetUnifiedF } } -std::optional>> +std::optional CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) const { return std::visit(FindUFVKId(*this), receiver); } -std::optional>> +std::optional CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) const { std::optional 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 CBasicKeyStore::GetUFVKIdForViewingKey(const lib return result; } -std::optional>> -FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const { +// +// FindUFVKId :: (KeyStore, Receiver) -> std::optional +// + +std::optional 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>> -FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const { +std::optional 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>> -FindUFVKId::operator()(const CScriptID& scriptId) const { +std::optional 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>> -FindUFVKId::operator()(const CKeyID& keyId) const { +std::optional 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>> -FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const { +std::optional FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const { return std::nullopt; } diff --git a/src/keystore.h b/src/keystore.h index 10a421cc8..34184299e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -18,6 +18,20 @@ #include +class AddressUFVKMetadata { +private: + libzcash::UFVKId ufvkId; + std::optional j; + bool internalAddress; +public: + AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional j, bool internalAddress) + : ufvkId(ufvkId), j(j), internalAddress(internalAddress) {} + + libzcash::UFVKId GetUFVKId() const { return ufvkId; } + std::optional 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 GetUnifiedFullViewingKey( const libzcash::UFVKId& keyId) const = 0; - virtual std::optional>> - GetUFVKMetadataForReceiver( + virtual std::optional 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>> - GetUFVKMetadataForAddress( + virtual std::optional GetUFVKMetadataForAddress( const libzcash::UnifiedAddress& addr) const = 0; virtual std::optional GetUFVKIdForViewingKey( @@ -379,29 +391,27 @@ public: virtual std::optional GetUnifiedFullViewingKey( const libzcash::UFVKId& keyId) const; - virtual std::optional>> - GetUFVKMetadataForReceiver( + virtual std::optional GetUFVKMetadataForReceiver( const libzcash::Receiver& receiver) const; std::optional 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>> - GetUFVKMetadataForAddress( + virtual std::optional GetUFVKMetadataForAddress( const libzcash::UnifiedAddress& addr) const; std::optional 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>> - operator()(const libzcash::OrchardRawAddress& orchardAddr) const; - std::optional>> - operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const; - std::optional>> - operator()(const CScriptID& scriptId) const; - std::optional>> - operator()(const CKeyID& keyId) const; - std::optional>> - operator()(const libzcash::UnknownReceiver& receiver) const; + std::optional operator()(const libzcash::OrchardRawAddress& orchardAddr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const; + std::optional operator()(const CScriptID& scriptId) const; + std::optional operator()(const CKeyID& keyId) const; + std::optional operator()(const libzcash::UnknownReceiver& receiver) const; }; #endif // BITCOIN_KEYSTORE_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index bd236b952..d9f44fce5 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -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 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 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 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 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); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c995fb399..fe97e6035 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -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 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 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 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 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>> meta; + std::optional 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>> meta; + std::optional 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 std::optional 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 UFVKForReceiver::operator() } } std::optional 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 UFVKForReceiver::operator() return std::nullopt; } std::optional 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 UFVKForReceiver::operator() std::optional 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 UnifiedAddressForReceiver::operator()( } std::optional 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 UnifiedAddressForReceiver::operator()(co return std::nullopt; } std::optional 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."); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bff473dcc..cefd742b7 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -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, diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 7bdc5b890..d275d1cee 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -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 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