Merge pull request #5715 from nuttycom/z_viewtransaction_cleanup

z_viewtransaction cleanup following #5700
This commit is contained in:
Charlie O'Keefe 2022-03-22 09:16:11 -06:00 committed by GitHub
commit b62d4917be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 257 additions and 138 deletions

View File

@ -568,14 +568,15 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
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.
// and since we trial-decrypt with both external and internal IVKs to
// verify whether it's an internal address, we learn the index.
auto saplingIvk = zufvk.GetSaplingKey().value().ToIncomingViewingKey();
keyStore.AddSaplingPaymentAddress(saplingIvk, saplingReceiver);
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_TRUE(ufvkmeta.has_value());
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
EXPECT_FALSE(ufvkmeta.value().GetDiversifierIndex().has_value());
EXPECT_TRUE(ufvkmeta.value().GetDiversifierIndex().has_value());
}
TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {

View File

@ -396,13 +396,12 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
std::optional<libzcash::diversifier_index_t> j;
bool jConflict = false;
for (const auto& receiver : addr) {
// skip unknown receivers
if (libzcash::HasKnownReceiverType(receiver)) {
auto rmeta = GetUFVKMetadataForReceiver(receiver);
auto rmeta = GetUFVKMetadataForReceiver(receiver);
if (rmeta.has_value()) {
// We should never generate unified addresses with internal receivers
assert(!(rmeta.has_value() && rmeta.value().IsInternalAddress()));
assert(rmeta.value().IsExternalAddress());
if (ufvkId.has_value() && rmeta.has_value()) {
if (ufvkId.has_value()) {
// If the unified address contains receivers that are associated with
// different UFVKs, we cannot return a singular value.
if (rmeta.value().GetUFVKId() != ufvkId.value()) {
@ -419,7 +418,7 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
j = rmeta.value().GetDiversifierIndex().value();
}
}
} else if (rmeta.has_value()) {
} else {
ufvkId = rmeta.value().GetUFVKId();
j = rmeta.value().GetDiversifierIndex();
}
@ -427,7 +426,7 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
}
if (ufvkId.has_value()) {
return AddressUFVKMetadata(ufvkId.value(), j, false);
return AddressUFVKMetadata(ufvkId.value(), j, true);
} else {
return std::nullopt;
}
@ -476,13 +475,9 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Orchar
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
auto fvk = v.GetOrchardKey();
if (fvk.has_value()) {
auto d_idx = fvk.value().ToIncomingViewingKey().DecryptDiversifier(orchardAddr);
auto d_idx = fvk.value().DecryptDiversifier(orchardAddr);
if (d_idx.has_value()) {
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 AddressUFVKMetadata(k, d_idx->first, d_idx->second);
}
}
}
@ -496,12 +491,18 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Saplin
// this via trial-decryption of a note.
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
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.
return std::nullopt;
// We know that we have a UFVK, and that it has a Sapling key that
// produced this address, so decrypt the diversifier to determine
// whether it was an internal or external address
auto ufvk = keystore.GetUnifiedFullViewingKey(ufvkId->second).value();
auto saplingKey = ufvk.GetSaplingKey().value();
auto d_idx = saplingKey.DecryptDiversifier(saplingAddr).value();
return AddressUFVKMetadata(ufvkId->second, d_idx.first, d_idx.second);
}
// If we have the addr -> ivk map entry but not the ivk -> UFVK map entry,
// then this is definitely a legacy Sapling address.
return std::nullopt;
}
// We haven't generated this receiver via `z_getaddressforaccount` (or this is a
@ -511,16 +512,9 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Saplin
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
auto dfvk = v.GetSaplingKey();
if (dfvk.has_value()) {
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 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);
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr);
if (d_idx.has_value()) {
return AddressUFVKMetadata(k, d_idx->first, d_idx->second);
}
}
}
@ -533,7 +527,7 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CScriptID& scrip
if (metadata != keystore.mapP2SHUnified.end()) {
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
} else {
return std::nullopt;
}
@ -543,7 +537,7 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CKeyID& keyId) c
if (metadata != keystore.mapP2PKHUnified.end()) {
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
} else {
return std::nullopt;
}

View File

@ -22,14 +22,14 @@ class AddressUFVKMetadata {
private:
libzcash::UFVKId ufvkId;
std::optional<libzcash::diversifier_index_t> j;
bool internalAddress;
bool externalAddress;
public:
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool internalAddress)
: ufvkId(ufvkId), j(j), internalAddress(internalAddress) {}
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool externalAddress)
: ufvkId(ufvkId), j(j), externalAddress(externalAddress) {}
libzcash::UFVKId GetUFVKId() const { return ufvkId; }
std::optional<libzcash::diversifier_index_t> GetDiversifierIndex() const { return j; }
bool IsInternalAddress() const { return internalAddress; }
bool IsExternalAddress() const { return externalAddress; }
};
/** A virtual base class for key stores */

View File

@ -283,7 +283,7 @@ struct RawOrchardActionSpend {
*/
struct RawOrchardActionOutput {
uint32_t outputActionIdx;
OrchardRawAddressPtr* addr;
OrchardRawAddressPtr* recipient;
CAmount noteValue;
unsigned char memo[512];
bool isOutgoing;
@ -304,7 +304,8 @@ typedef void (*push_output_t)(void* callbackReceiver, const RawOrchardActionOutp
*
* `raw_ovks` must be a pointer to an array of `unsigned char[32]`.
*
* The `addr` pointer on each `RawOrchardActionOutput` value must be freed using
* The `recipient` pointer for each `RawOrchardActionOutput` value, and the `receivedAt`
* pointer for each `RawOrchardActionSpend` value, must be freed using
* `orchard_address_free`.
*/
bool orchard_wallet_get_txdata(

View File

@ -443,7 +443,7 @@ public:
std::array<unsigned char, 512> memo;
std::move(std::begin(rawOutput.memo), std::end(rawOutput.memo), memo.begin());
auto output = OrchardActionOutput(
libzcash::OrchardRawAddress(rawOutput.addr),
libzcash::OrchardRawAddress(rawOutput.recipient),
rawOutput.noteValue,
memo,
rawOutput.isOutgoing);

View File

@ -3110,7 +3110,9 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled.");
}
LOCK(pwalletMain->cs_wallet);
// cs_main is required for obtaining the current height, for
// CWallet::DefaultReceiverTypes
LOCK2(cs_main, pwalletMain->cs_wallet);
int64_t accountInt = params[0].get_int64();
if (accountInt < 0 || accountInt >= ZCASH_LEGACY_ACCOUNT) {
@ -3136,7 +3138,7 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp)
}
if (receivers.empty()) {
// Default is the best and second-best shielded pools, and the transparent pool.
receivers = CWallet::DefaultReceiverTypes();
receivers = CWallet::DefaultReceiverTypes(chainActive.Height());
}
std::optional<libzcash::diversifier_index_t> j = std::nullopt;
@ -4225,9 +4227,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::optional<std::string> addrStr;
if (!pwalletMain->IsInternalRecipient(pa)) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
addrStr = keyIO.EncodePaymentAddress(addr);
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
if (addr.second != RecipientType::WalletInternalAddress) {
addrStr = keyIO.EncodePaymentAddress(addr.first);
}
UniValue entry(UniValue::VOBJ);
@ -4278,17 +4280,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::optional<std::string> addrStr;
bool isInternal = pwalletMain->IsInternalRecipient(pa);
if (!isInternal) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
addrStr = keyIO.EncodePaymentAddress(addr);
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
if (addr.second != RecipientType::WalletInternalAddress) {
addrStr = keyIO.EncodePaymentAddress(addr.first);
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING);
entry.pushKV("output", (int)op.n);
entry.pushKV("outgoing", isOutgoing);
entry.pushKV("walletInternal", isInternal);
entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}
@ -4338,17 +4339,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// Show the address that was cached at transaction construction as the
// recipient.
std::optional<std::string> addrStr;
bool isInternal = pwalletMain->IsInternalRecipient(recipient);
if (!isInternal) {
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, recipient);
addrStr = keyIO.EncodePaymentAddress(addr);
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, recipient);
if (addr.second != RecipientType::WalletInternalAddress) {
addrStr = keyIO.EncodePaymentAddress(addr.first);
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_ORCHARD);
entry.pushKV("action", (int) actionIdx);
entry.pushKV("outgoing", orchardActionOutput.IsOutgoing());
entry.pushKV("walletInternal", isInternal);
entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress);
if (addrStr.has_value()) {
entry.pushKV("address", addrStr.value());
}

View File

@ -61,7 +61,9 @@ const char * DEFAULT_WALLET_DAT = "wallet.dat";
*/
CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
std::set<ReceiverType> CWallet::DefaultReceiverTypes() {
std::set<ReceiverType> CWallet::DefaultReceiverTypes(int nHeight) {
// For now, just ignore the height information because the default
// is always the same.
return {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard};
}
@ -723,7 +725,6 @@ WalletUAGenerationResult CWallet::GenerateUnifiedAddress(
auto metadata = mapUfvkAddressMetadata.find(ufvkid);
if (metadata != mapUfvkAddressMetadata.end()) {
if (j.has_value()) {
auto receivers = metadata->second.GetReceivers(j.value());
if (receivers.has_value()) {
// Ensure that the set of receiver types being requested is
@ -857,80 +858,142 @@ bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &add
addrmeta.GetReceiverTypes());
}
PaymentAddress CWallet::GetPaymentAddressForRecipient(
std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
const uint256& txid,
const libzcash::RecipientAddress& recipient) const
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
auto self = this;
auto defaultAddress = [&]() -> PaymentAddress {
auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient));
return std::visit(match {
[&](const CKeyID& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
int nHeight = chainActive.Height();
auto wtxPtr = mapWallet.find(txid);
if (wtxPtr != mapWallet.end()) {
const CBlockIndex* pTxIndex{nullptr};
if (wtxPtr->second.GetDepthInMainChain(pTxIndex) > 0) {
nHeight = pTxIndex->nHeight;
}
}
auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient));
std::pair<PaymentAddress, RecipientType> defaultAddress = std::visit(match {
[&](const CKeyID& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
// we do not generate unified addresses for change keys
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
}
// If it's in the address book, it's a legacy external address
if (mapAddressBook.count(addr)) {
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletExternalAddress);
}
if (::IsMine(*this, addr)) {
// For keys that are ours, check if they are legacy change keys. Anything that has an
// internal BIP-44 keypath is a post-mnemonic internal address.
auto keyMeta = mapKeyMetadata.find(addr);
if (keyMeta == mapKeyMetadata.end() || keyMeta->second.hdKeypath == "") {
return std::make_pair(PaymentAddress{addr}, RecipientType::LegacyChangeAddress);
} else if (IsInternalKeyPath(44, BIP44CoinType(), keyMeta->second.hdKeypath)) {
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletInternalAddress);
} else {
return libzcash::PaymentAddress{addr};
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletExternalAddress);
}
},
[&](const CScriptID& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else {
return libzcash::PaymentAddress{addr};
}
},
[&](const SaplingPaymentAddress& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else if (ufvk.has_value() && ufvk->GetSaplingKey().has_value()) {
auto saplingKey = ufvk->GetSaplingKey().value();
auto j = saplingKey.DecryptDiversifier(addr.d);
// std::get is safe here because we know we have a valid Sapling diversifier index
auto defaultUA = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(
ufvk->Address(j, CWallet::DefaultReceiverTypes()));
return libzcash::PaymentAddress{defaultUA.first};
} else {
return libzcash::PaymentAddress{addr};
}
},
[&](const OrchardRawAddress& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else if (ufvk.has_value() && ufvk->GetOrchardKey().has_value()) {
auto orchardKey = ufvk->GetOrchardKey().value();
auto j = orchardKey.ToIncomingViewingKey().DecryptDiversifier(addr);
if (j.has_value()) {
auto genResult = ufvk->Address(j.value(), CWallet::DefaultReceiverTypes());
auto defaultUA = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&genResult);
if (defaultUA != nullptr) {
return libzcash::PaymentAddress{defaultUA->first};
}
}
// It really doesn't appear to be ours, so treat it as a counterparty address.
return std::make_pair(PaymentAddress{addr}, RecipientType::CounterpartyAddress);
},
[&](const CScriptID& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
} else if (HaveCScript(addr)) {
// we never generate internal p2sh addresses, so all are wallet external
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletExternalAddress);
}
return std::make_pair(PaymentAddress{addr}, RecipientType::CounterpartyAddress);
},
[&](const SaplingPaymentAddress& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
// UAs are always external addresses
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
}
if (ufvk.has_value() && ufvk->GetSaplingKey().has_value()) {
auto saplingKey = ufvk->GetSaplingKey().value();
auto j = saplingKey.DecryptDiversifier(addr);
if (j.has_value()) {
// external addresses correspond to UAs.
if (j.value().second) {
// std::get is safe here because we know we have a valid Sapling diversifier index
auto defaultUA = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(
ufvk->Address(j.value().first, CWallet::DefaultReceiverTypes(nHeight)));
return std::make_pair(PaymentAddress{defaultUA.first}, RecipientType::WalletExternalAddress);
} else {
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletInternalAddress);
}
}
return libzcash::PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)};
}
}, recipient);
};
// legacy Sapling keys that we recognize are all external addresses
libzcash::SaplingIncomingViewingKey ivk;
if (GetSaplingIncomingViewingKey(addr, ivk)) {
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletExternalAddress);
}
// We don't produce internal change addresses for legacy Sapling
// addresses, so this must be a counterparty address
return std::make_pair(PaymentAddress{addr}, RecipientType::CounterpartyAddress);
},
[&](const OrchardRawAddress& addr) {
auto ua = self->FindUnifiedAddressByReceiver(addr);
if (ua.has_value()) {
// UAs are always external addresses
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
} else if (ufvk.has_value() && ufvk->GetOrchardKey().has_value()) {
auto orchardKey = ufvk->GetOrchardKey().value();
auto j = orchardKey.DecryptDiversifier(addr);
if (j.has_value()) {
if (j.value().second) {
// Attempt to reproduce the original unified address
auto genResult = ufvk->Address(j.value().first, CWallet::DefaultReceiverTypes(nHeight));
auto defaultUA = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&genResult);
if (defaultUA != nullptr) {
return std::make_pair(PaymentAddress{defaultUA->first}, RecipientType::WalletExternalAddress);
}
} else {
return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::WalletInternalAddress);
}
}
}
return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::CounterpartyAddress);
}
}, recipient);
auto recipientsPtr = sendRecipients.find(txid);
if (recipientsPtr == sendRecipients.end()) {
// we don't know the recipient, so we just return the simplest type
return defaultAddress();
// we haven't sent to this address, so look up internally what kind
// it is & attempt to reproduce the address as the user originally
// saw it.
return defaultAddress;
} else {
// search the list of recipient mappings for one corresponding to
// our recipient, and return the known UA if it exists; otherwise
// just use the default.
for (const auto& mapping : recipientsPtr->second) {
if (mapping.address == recipient && mapping.ua.has_value()) {
return PaymentAddress{mapping.ua.value()};
// use the recipient type from the default address, but ensure that we use
// the cached address value to which we actually sent the funds
return std::make_pair(PaymentAddress{mapping.ua.value()}, defaultAddress.second);
}
}
return defaultAddress();
return defaultAddress;
}
}
@ -2008,9 +2071,8 @@ bool CWallet::SelectorMatchesAddress(
return a0 == a1;
},
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
auto j = extfvk.DecryptDiversifier(a0.d);
auto addr = extfvk.Address(j);
return addr.has_value() && addr.value() == a0;
auto j = extfvk.DecryptDiversifier(a0);
return j.has_value();
},
[&](const libzcash::UnifiedAddress& ua) {
const auto a0Meta = self->GetUFVKMetadataForReceiver(a0);
@ -2028,9 +2090,8 @@ bool CWallet::SelectorMatchesAddress(
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
auto saplingKey = ufvk.GetSaplingKey();
if (saplingKey.has_value()) {
auto j = saplingKey.value().DecryptDiversifier(a0.d);
auto addr = saplingKey.value().Address(j);
return addr.has_value() && addr.value() == a0;
auto j = saplingKey.value().DecryptDiversifier(a0);
return j.has_value();
} else {
return false;
}
@ -7165,13 +7226,16 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid);
auto saplingKey = ufvk.value().GetSaplingKey();
if (saplingKey.has_value()) {
diversifier_index_t j = saplingKey.value().DecryptDiversifier(saplingAddr.d);
auto receivers = metadata.GetReceivers(j);
if (receivers.has_value()) {
auto addr = ufvk.value().Address(j, receivers.value());
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
if (addrPtr != nullptr) {
return addrPtr->first;
auto j = saplingKey.value().DecryptDiversifier(saplingAddr);
if (j.has_value()) {
auto receivers = metadata.GetReceivers(j.value().first);
// Only return a unified address for external addresses
if (receivers.has_value() && j.value().second) {
auto addr = ufvk.value().Address(j.value().first, receivers.value());
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
if (addrPtr != nullptr) {
return addrPtr->first;
}
}
}
}

View File

@ -836,6 +836,13 @@ public:
bool SelectsOrchard() const;
};
enum class RecipientType {
WalletExternalAddress,
WalletInternalAddress,
LegacyChangeAddress,
CounterpartyAddress
};
class SpendableInputs {
private:
bool limited = false;
@ -1612,9 +1619,10 @@ public:
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
libzcash::PaymentAddress GetPaymentAddressForRecipient(
std::pair<libzcash::PaymentAddress, RecipientType> GetPaymentAddressForRecipient(
const uint256& txid,
const libzcash::RecipientAddress& recipient) const;
bool IsInternalRecipient(
const libzcash::RecipientAddress& recipient) const;
@ -1708,11 +1716,14 @@ public:
{
sendRecipients[txid].push_back(recipient);
if (recipient.ua.has_value()) {
assert(CWalletDB(strWalletFile).WriteRecipientMapping(
if (!CWalletDB(strWalletFile).WriteRecipientMapping(
txid,
recipient.address,
recipient.ua.value()
));
)) {
LogPrintf("SaveRecipientMappings: Failed to write recipient mappings to the wallet database.");
return false;
};
}
}
@ -1734,10 +1745,10 @@ public:
static CAmount GetRequiredFee(unsigned int nTxBytes);
/**
* The current set of default receiver types used when the wallet generates
* unified addresses
* The set of default receiver types used when the wallet generates
* unified addresses, as of the specified chain height.
*/
static std::set<libzcash::ReceiverType> DefaultReceiverTypes();
static std::set<libzcash::ReceiverType> DefaultReceiverTypes(int nHeight);
private:
bool NewKeyPool();
@ -2019,7 +2030,7 @@ enum class PaymentAddressSource {
MnemonicHDSeed,
Imported,
ImportedWatchOnly,
AddressNotFound,
AddressNotFound
};
// GetSourceForPaymentAddress visitor :: (CWallet&, PaymentAddress) -> PaymentAddressSource
@ -2106,6 +2117,11 @@ public:
};
// UnifiedAddressForReceiver :: (CWallet&, Receiver) -> std::optional<UnifiedAddress>
//
// When this visitor returns `std::nullopt` it means that either the receiver is not
// recognized as belonging to any key known to the wallet, or that the receiver
// is an internal change receiver for which it is not permitted to generate a
// unified address per ZIP 315.
class UnifiedAddressForReceiver {
private:
const CWallet& wallet;

View File

@ -208,6 +208,28 @@ public:
libzcash::OrchardRawAddress GetChangeAddress() const;
/**
* Extracts the diversifier from the specified address and decrypts it as
* a diversifier index, then verifies that this diversifier index produces
* the same address. This method attempts decryption using both the internal
* and external parts of the full viewing key.
*
* Returns a pair consisting of the diversifier index and a flag indicating
* whether the address is external (set to true in the case this is an external
* address), or std::nullopt if the address was not generated from this key.
*/
std::optional<std::pair<diversifier_index_t, bool>> DecryptDiversifier(const OrchardRawAddress& addr) const {
auto j_external = ToIncomingViewingKey().DecryptDiversifier(addr);
if (j_external.has_value()) {
return std::make_pair(j_external.value(), true);
}
auto j_internal = ToInternalIncomingViewingKey().DecryptDiversifier(addr);
if (j_internal.has_value()) {
return std::make_pair(j_internal.value(), false);
}
return std::nullopt;
}
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
{
if (this != &key) {

View File

@ -135,6 +135,11 @@ class SaplingDiversifiableFullViewingKey {
protected:
SaplingDiversifiableFullViewingKey GetInternalDFVK() const;
diversifier_index_t DecryptDiversifier(const diversifier_t& d) const {
diversifier_index_t j;
librustzcash_sapling_diversifier_index(dk.begin(), d.begin(), j.begin());
return j;
}
public:
libzcash::SaplingFullViewingKey fvk;
uint256 dk;
@ -172,12 +177,6 @@ public:
*/
std::pair<uint256, uint256> GetOVKs() const;
diversifier_index_t DecryptDiversifier(const diversifier_t& d) const {
diversifier_index_t j;
librustzcash_sapling_diversifier_index(dk.begin(), d.begin(), j.begin());
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.
@ -185,8 +184,30 @@ public:
return GetInternalDFVK().Address(j);
}
diversifier_index_t DecryptInternalDiversifier(const diversifier_t& d) const {
return GetInternalDFVK().DecryptDiversifier(d);
/**
* Extracts the diversifier from the specified address and decrypts it as
* a diversifier index, then verifies that this diversifier index produces
* the same address. This method attempts decryption using both the internal
* and external parts of the full viewing key.
*
* Returns a pair consisting of the diversifier index and a flag indicating
* whether the address is external (set to true in the case this is an external
* address), or std::nullopt if the address was not generated from this key.
*/
std::optional<std::pair<diversifier_index_t, bool>> DecryptDiversifier(const SaplingPaymentAddress& addr) const {
auto j_external = DecryptDiversifier(addr.d);
auto addr_external = Address(j_external);
if (addr_external.has_value() && addr_external.value() == addr) {
return std::make_pair(j_external, true);
}
auto dfvk_internal = GetInternalDFVK();
auto j_internal = dfvk_internal.DecryptDiversifier(addr.d);
auto addr_internal = dfvk_internal.Address(j_internal);
if (addr_internal.has_value() && addr_internal.value() == addr) {
return std::make_pair(j_internal, false);
}
return std::nullopt;
}
ADD_SERIALIZE_METHODS;