Add RecipientType to GetPaymentAddressForRecipient result.
This now returns whether or not the address corresponds to an internal recipient, an external address for the wallet, or a counterparty's address (an address not derived from any of the wallet's keys) from GetPaymentAddressForRecipient. Fixes #5708
This commit is contained in:
parent
15bb06f452
commit
87cc0d22db
|
@ -568,14 +568,15 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
|
||||||
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
|
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
|
||||||
|
|
||||||
// Adding the Sapling addr -> ivk map entry causes us to find the same UFVK,
|
// 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();
|
auto saplingIvk = zufvk.GetSaplingKey().value().ToIncomingViewingKey();
|
||||||
keyStore.AddSaplingPaymentAddress(saplingIvk, saplingReceiver);
|
keyStore.AddSaplingPaymentAddress(saplingIvk, saplingReceiver);
|
||||||
|
|
||||||
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
||||||
EXPECT_TRUE(ufvkmeta.has_value());
|
EXPECT_TRUE(ufvkmeta.has_value());
|
||||||
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
|
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
|
||||||
EXPECT_FALSE(ufvkmeta.value().GetDiversifierIndex().has_value());
|
EXPECT_TRUE(ufvkmeta.value().GetDiversifierIndex().has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {
|
TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {
|
||||||
|
|
|
@ -396,13 +396,12 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
|
||||||
std::optional<libzcash::diversifier_index_t> j;
|
std::optional<libzcash::diversifier_index_t> j;
|
||||||
bool jConflict = false;
|
bool jConflict = false;
|
||||||
for (const auto& receiver : addr) {
|
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
|
// 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
|
// If the unified address contains receivers that are associated with
|
||||||
// different UFVKs, we cannot return a singular value.
|
// different UFVKs, we cannot return a singular value.
|
||||||
if (rmeta.value().GetUFVKId() != ufvkId.value()) {
|
if (rmeta.value().GetUFVKId() != ufvkId.value()) {
|
||||||
|
@ -419,7 +418,7 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
|
||||||
j = rmeta.value().GetDiversifierIndex().value();
|
j = rmeta.value().GetDiversifierIndex().value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (rmeta.has_value()) {
|
} else {
|
||||||
ufvkId = rmeta.value().GetUFVKId();
|
ufvkId = rmeta.value().GetUFVKId();
|
||||||
j = rmeta.value().GetDiversifierIndex();
|
j = rmeta.value().GetDiversifierIndex();
|
||||||
}
|
}
|
||||||
|
@ -427,7 +426,7 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ufvkId.has_value()) {
|
if (ufvkId.has_value()) {
|
||||||
return AddressUFVKMetadata(ufvkId.value(), j, false);
|
return AddressUFVKMetadata(ufvkId.value(), j, true);
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -476,13 +475,9 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Orchar
|
||||||
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
||||||
auto fvk = v.GetOrchardKey();
|
auto fvk = v.GetOrchardKey();
|
||||||
if (fvk.has_value()) {
|
if (fvk.has_value()) {
|
||||||
auto d_idx = fvk.value().ToIncomingViewingKey().DecryptDiversifier(orchardAddr);
|
auto d_idx = fvk.value().DecryptDiversifier(orchardAddr);
|
||||||
if (d_idx.has_value()) {
|
if (d_idx.has_value()) {
|
||||||
return AddressUFVKMetadata(k, d_idx, false);
|
return AddressUFVKMetadata(k, d_idx->first, d_idx->second);
|
||||||
}
|
|
||||||
auto internal_d_idx = fvk.value().ToInternalIncomingViewingKey().DecryptDiversifier(orchardAddr);
|
|
||||||
if (internal_d_idx.has_value()) {
|
|
||||||
return AddressUFVKMetadata(k, internal_d_idx, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,13 +491,19 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Saplin
|
||||||
// this via trial-decryption of a note.
|
// this via trial-decryption of a note.
|
||||||
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
|
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
|
||||||
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
|
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
|
||||||
return AddressUFVKMetadata(ufvkId->second, std::nullopt, false);
|
// We know that we have a UFVK, and that it has a Sapling key that
|
||||||
} else {
|
// 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,
|
// If we have the addr -> ivk map entry but not the ivk -> UFVK map entry,
|
||||||
// then this is definitely a legacy Sapling address.
|
// then this is definitely a legacy Sapling address.
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We haven't generated this receiver via `z_getaddressforaccount` (or this is a
|
// We haven't generated this receiver via `z_getaddressforaccount` (or this is a
|
||||||
// recovery from a backed-up mnemonic which doesn't store receiver types selected by
|
// recovery from a backed-up mnemonic which doesn't store receiver types selected by
|
||||||
|
@ -511,16 +512,9 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::Saplin
|
||||||
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
||||||
auto dfvk = v.GetSaplingKey();
|
auto dfvk = v.GetSaplingKey();
|
||||||
if (dfvk.has_value()) {
|
if (dfvk.has_value()) {
|
||||||
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr.d);
|
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr);
|
||||||
auto derived_addr = dfvk.value().Address(d_idx);
|
if (d_idx.has_value()) {
|
||||||
if (derived_addr.has_value() && derived_addr.value() == saplingAddr) {
|
return AddressUFVKMetadata(k, d_idx->first, d_idx->second);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,7 +527,7 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CScriptID& scrip
|
||||||
if (metadata != keystore.mapP2SHUnified.end()) {
|
if (metadata != keystore.mapP2SHUnified.end()) {
|
||||||
// At present we never generate transparent internal addresses, so this
|
// At present we never generate transparent internal addresses, so this
|
||||||
// must be an external address
|
// must be an external address
|
||||||
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
|
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -543,7 +537,7 @@ std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CKeyID& keyId) c
|
||||||
if (metadata != keystore.mapP2PKHUnified.end()) {
|
if (metadata != keystore.mapP2PKHUnified.end()) {
|
||||||
// At present we never generate transparent internal addresses, so this
|
// At present we never generate transparent internal addresses, so this
|
||||||
// must be an external address
|
// must be an external address
|
||||||
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, false);
|
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,14 @@ class AddressUFVKMetadata {
|
||||||
private:
|
private:
|
||||||
libzcash::UFVKId ufvkId;
|
libzcash::UFVKId ufvkId;
|
||||||
std::optional<libzcash::diversifier_index_t> j;
|
std::optional<libzcash::diversifier_index_t> j;
|
||||||
bool internalAddress;
|
bool externalAddress;
|
||||||
public:
|
public:
|
||||||
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool internalAddress)
|
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool externalAddress)
|
||||||
: ufvkId(ufvkId), j(j), internalAddress(internalAddress) {}
|
: ufvkId(ufvkId), j(j), externalAddress(externalAddress) {}
|
||||||
|
|
||||||
libzcash::UFVKId GetUFVKId() const { return ufvkId; }
|
libzcash::UFVKId GetUFVKId() const { return ufvkId; }
|
||||||
std::optional<libzcash::diversifier_index_t> GetDiversifierIndex() const { return j; }
|
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 */
|
/** A virtual base class for key stores */
|
||||||
|
|
|
@ -4225,9 +4225,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
// Show the address that was cached at transaction construction as the
|
// Show the address that was cached at transaction construction as the
|
||||||
// recipient.
|
// recipient.
|
||||||
std::optional<std::string> addrStr;
|
std::optional<std::string> addrStr;
|
||||||
if (!pwalletMain->IsInternalRecipient(pa)) {
|
|
||||||
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
|
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
|
||||||
addrStr = keyIO.EncodePaymentAddress(addr);
|
if (addr.second != RecipientType::WalletInternalAddress) {
|
||||||
|
addrStr = keyIO.EncodePaymentAddress(addr.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
|
@ -4278,17 +4278,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
// Show the address that was cached at transaction construction as the
|
// Show the address that was cached at transaction construction as the
|
||||||
// recipient.
|
// recipient.
|
||||||
std::optional<std::string> addrStr;
|
std::optional<std::string> addrStr;
|
||||||
bool isInternal = pwalletMain->IsInternalRecipient(pa);
|
|
||||||
if (!isInternal) {
|
|
||||||
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
|
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, pa);
|
||||||
addrStr = keyIO.EncodePaymentAddress(addr);
|
if (addr.second != RecipientType::WalletInternalAddress) {
|
||||||
|
addrStr = keyIO.EncodePaymentAddress(addr.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
entry.pushKV("type", ADDR_TYPE_SAPLING);
|
entry.pushKV("type", ADDR_TYPE_SAPLING);
|
||||||
entry.pushKV("output", (int)op.n);
|
entry.pushKV("output", (int)op.n);
|
||||||
entry.pushKV("outgoing", isOutgoing);
|
entry.pushKV("outgoing", isOutgoing);
|
||||||
entry.pushKV("walletInternal", isInternal);
|
entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress);
|
||||||
if (addrStr.has_value()) {
|
if (addrStr.has_value()) {
|
||||||
entry.pushKV("address", addrStr.value());
|
entry.pushKV("address", addrStr.value());
|
||||||
}
|
}
|
||||||
|
@ -4338,17 +4337,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
// Show the address that was cached at transaction construction as the
|
// Show the address that was cached at transaction construction as the
|
||||||
// recipient.
|
// recipient.
|
||||||
std::optional<std::string> addrStr;
|
std::optional<std::string> addrStr;
|
||||||
bool isInternal = pwalletMain->IsInternalRecipient(recipient);
|
|
||||||
if (!isInternal) {
|
|
||||||
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, recipient);
|
auto addr = pwalletMain->GetPaymentAddressForRecipient(txid, recipient);
|
||||||
addrStr = keyIO.EncodePaymentAddress(addr);
|
if (addr.second != RecipientType::WalletInternalAddress) {
|
||||||
|
addrStr = keyIO.EncodePaymentAddress(addr.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
entry.pushKV("type", ADDR_TYPE_ORCHARD);
|
entry.pushKV("type", ADDR_TYPE_ORCHARD);
|
||||||
entry.pushKV("action", (int) actionIdx);
|
entry.pushKV("action", (int) actionIdx);
|
||||||
entry.pushKV("outgoing", orchardActionOutput.IsOutgoing());
|
entry.pushKV("outgoing", orchardActionOutput.IsOutgoing());
|
||||||
entry.pushKV("walletInternal", isInternal);
|
entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress);
|
||||||
if (addrStr.has_value()) {
|
if (addrStr.has_value()) {
|
||||||
entry.pushKV("address", addrStr.value());
|
entry.pushKV("address", addrStr.value());
|
||||||
}
|
}
|
||||||
|
|
|
@ -723,7 +723,6 @@ WalletUAGenerationResult CWallet::GenerateUnifiedAddress(
|
||||||
auto metadata = mapUfvkAddressMetadata.find(ufvkid);
|
auto metadata = mapUfvkAddressMetadata.find(ufvkid);
|
||||||
if (metadata != mapUfvkAddressMetadata.end()) {
|
if (metadata != mapUfvkAddressMetadata.end()) {
|
||||||
if (j.has_value()) {
|
if (j.has_value()) {
|
||||||
|
|
||||||
auto receivers = metadata->second.GetReceivers(j.value());
|
auto receivers = metadata->second.GetReceivers(j.value());
|
||||||
if (receivers.has_value()) {
|
if (receivers.has_value()) {
|
||||||
// Ensure that the set of receiver types being requested is
|
// Ensure that the set of receiver types being requested is
|
||||||
|
@ -857,80 +856,115 @@ bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &add
|
||||||
addrmeta.GetReceiverTypes());
|
addrmeta.GetReceiverTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
PaymentAddress CWallet::GetPaymentAddressForRecipient(
|
std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
|
||||||
const uint256& txid,
|
const uint256& txid,
|
||||||
const libzcash::RecipientAddress& recipient) const
|
const libzcash::RecipientAddress& recipient) const
|
||||||
{
|
{
|
||||||
auto self = this;
|
auto self = this;
|
||||||
auto defaultAddress = [&]() -> PaymentAddress {
|
|
||||||
auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient));
|
auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient));
|
||||||
return std::visit(match {
|
std::pair<PaymentAddress, RecipientType> defaultAddress = std::visit(match {
|
||||||
[&](const CKeyID& addr) {
|
[&](const CKeyID& addr) {
|
||||||
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
return libzcash::PaymentAddress{ua.value()};
|
// we do not generate unified addresses for change keys
|
||||||
|
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// not associated with an UA, so we check legacy key metadata; if we don't
|
||||||
|
// have any metadata, it's a counterparty address
|
||||||
|
auto keyMeta = mapKeyMetadata.find(addr);
|
||||||
|
if (keyMeta == mapKeyMetadata.end()) {
|
||||||
|
return std::make_pair(PaymentAddress{addr}, RecipientType::CounterpartyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for legacy change keys, we can check the keypath to determine
|
||||||
|
// whether this is an internal or external address.
|
||||||
|
if (IsInternalKeyPath(44, BIP44CoinType(), keyMeta->second.hdKeypath)) {
|
||||||
|
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletInternalAddress);
|
||||||
} else {
|
} else {
|
||||||
return libzcash::PaymentAddress{addr};
|
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletExternalAddress);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](const CScriptID& addr) {
|
[&](const CScriptID& addr) {
|
||||||
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
return libzcash::PaymentAddress{ua.value()};
|
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
|
||||||
} else {
|
} else if (HaveCScript(addr)) {
|
||||||
return libzcash::PaymentAddress{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) {
|
[&](const SaplingPaymentAddress& addr) {
|
||||||
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
return libzcash::PaymentAddress{ua.value()};
|
// UAs are always external addresses
|
||||||
|
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
|
||||||
} else if (ufvk.has_value() && ufvk->GetSaplingKey().has_value()) {
|
} else if (ufvk.has_value() && ufvk->GetSaplingKey().has_value()) {
|
||||||
auto saplingKey = ufvk->GetSaplingKey().value();
|
auto saplingKey = ufvk->GetSaplingKey().value();
|
||||||
auto j = saplingKey.DecryptDiversifier(addr.d);
|
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
|
// 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>>(
|
auto defaultUA = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(
|
||||||
ufvk->Address(j, CWallet::DefaultReceiverTypes()));
|
ufvk->Address(j.value().first, CWallet::DefaultReceiverTypes()));
|
||||||
return libzcash::PaymentAddress{defaultUA.first};
|
return std::make_pair(PaymentAddress{defaultUA.first}, RecipientType::WalletExternalAddress);
|
||||||
} else {
|
} else {
|
||||||
return libzcash::PaymentAddress{addr};
|
return std::make_pair(PaymentAddress{addr}, RecipientType::WalletInternalAddress);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
[&](const OrchardRawAddress& addr) {
|
||||||
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
auto ua = self->FindUnifiedAddressByReceiver(addr);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
return libzcash::PaymentAddress{ua.value()};
|
// UAs are always external addresses
|
||||||
|
return std::make_pair(PaymentAddress{ua.value()}, RecipientType::WalletExternalAddress);
|
||||||
} else if (ufvk.has_value() && ufvk->GetOrchardKey().has_value()) {
|
} else if (ufvk.has_value() && ufvk->GetOrchardKey().has_value()) {
|
||||||
auto orchardKey = ufvk->GetOrchardKey().value();
|
auto orchardKey = ufvk->GetOrchardKey().value();
|
||||||
auto j = orchardKey.ToIncomingViewingKey().DecryptDiversifier(addr);
|
auto j = orchardKey.DecryptDiversifier(addr);
|
||||||
if (j.has_value()) {
|
if (j.has_value()) {
|
||||||
auto genResult = ufvk->Address(j.value(), CWallet::DefaultReceiverTypes());
|
if (j.value().second) {
|
||||||
|
// Attempt to reproduce the original unified address
|
||||||
|
auto genResult = ufvk->Address(j.value().first, CWallet::DefaultReceiverTypes());
|
||||||
auto defaultUA = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&genResult);
|
auto defaultUA = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&genResult);
|
||||||
if (defaultUA != nullptr) {
|
if (defaultUA != nullptr) {
|
||||||
return libzcash::PaymentAddress{defaultUA->first};
|
return std::make_pair(PaymentAddress{defaultUA->first}, RecipientType::WalletExternalAddress);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::WalletInternalAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return libzcash::PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)};
|
return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::CounterpartyAddress);
|
||||||
}
|
}
|
||||||
}, recipient);
|
}, recipient);
|
||||||
};
|
|
||||||
|
|
||||||
auto recipientsPtr = sendRecipients.find(txid);
|
auto recipientsPtr = sendRecipients.find(txid);
|
||||||
if (recipientsPtr == sendRecipients.end()) {
|
if (recipientsPtr == sendRecipients.end()) {
|
||||||
// we don't know the recipient, so we just return the simplest type
|
// we haven't sent to this address, so look up internally what kind
|
||||||
return defaultAddress();
|
// it is & attempt to reproduce the address as the user originally
|
||||||
|
// saw it.
|
||||||
|
return defaultAddress;
|
||||||
} else {
|
} else {
|
||||||
// search the list of recipient mappings for one corresponding to
|
// search the list of recipient mappings for one corresponding to
|
||||||
// our recipient, and return the known UA if it exists; otherwise
|
// our recipient, and return the known UA if it exists; otherwise
|
||||||
// just use the default.
|
// just use the default.
|
||||||
for (const auto& mapping : recipientsPtr->second) {
|
for (const auto& mapping : recipientsPtr->second) {
|
||||||
if (mapping.address == recipient && mapping.ua.has_value()) {
|
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 +2042,8 @@ bool CWallet::SelectorMatchesAddress(
|
||||||
return a0 == a1;
|
return a0 == a1;
|
||||||
},
|
},
|
||||||
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
|
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
|
||||||
auto j = extfvk.DecryptDiversifier(a0.d);
|
auto j = extfvk.DecryptDiversifier(a0);
|
||||||
auto addr = extfvk.Address(j);
|
return j.has_value();
|
||||||
return addr.has_value() && addr.value() == a0;
|
|
||||||
},
|
},
|
||||||
[&](const libzcash::UnifiedAddress& ua) {
|
[&](const libzcash::UnifiedAddress& ua) {
|
||||||
const auto a0Meta = self->GetUFVKMetadataForReceiver(a0);
|
const auto a0Meta = self->GetUFVKMetadataForReceiver(a0);
|
||||||
|
@ -2028,9 +2061,8 @@ bool CWallet::SelectorMatchesAddress(
|
||||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||||
auto saplingKey = ufvk.GetSaplingKey();
|
auto saplingKey = ufvk.GetSaplingKey();
|
||||||
if (saplingKey.has_value()) {
|
if (saplingKey.has_value()) {
|
||||||
auto j = saplingKey.value().DecryptDiversifier(a0.d);
|
auto j = saplingKey.value().DecryptDiversifier(a0);
|
||||||
auto addr = saplingKey.value().Address(j);
|
return j.has_value();
|
||||||
return addr.has_value() && addr.value() == a0;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -7165,10 +7197,12 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
|
||||||
const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid);
|
const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid);
|
||||||
auto saplingKey = ufvk.value().GetSaplingKey();
|
auto saplingKey = ufvk.value().GetSaplingKey();
|
||||||
if (saplingKey.has_value()) {
|
if (saplingKey.has_value()) {
|
||||||
diversifier_index_t j = saplingKey.value().DecryptDiversifier(saplingAddr.d);
|
auto j = saplingKey.value().DecryptDiversifier(saplingAddr);
|
||||||
auto receivers = metadata.GetReceivers(j);
|
if (j.has_value()) {
|
||||||
if (receivers.has_value()) {
|
auto receivers = metadata.GetReceivers(j.value().first);
|
||||||
auto addr = ufvk.value().Address(j, receivers.value());
|
// 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);
|
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
|
||||||
if (addrPtr != nullptr) {
|
if (addrPtr != nullptr) {
|
||||||
return addrPtr->first;
|
return addrPtr->first;
|
||||||
|
@ -7176,6 +7210,7 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CScriptID& scriptId) const {
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CScriptID& scriptId) const {
|
||||||
|
|
|
@ -836,6 +836,12 @@ public:
|
||||||
bool SelectsOrchard() const;
|
bool SelectsOrchard() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class RecipientType {
|
||||||
|
WalletExternalAddress,
|
||||||
|
WalletInternalAddress,
|
||||||
|
CounterpartyAddress
|
||||||
|
};
|
||||||
|
|
||||||
class SpendableInputs {
|
class SpendableInputs {
|
||||||
private:
|
private:
|
||||||
bool limited = false;
|
bool limited = false;
|
||||||
|
@ -1612,9 +1618,10 @@ public:
|
||||||
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
|
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
|
||||||
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
|
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
|
||||||
|
|
||||||
libzcash::PaymentAddress GetPaymentAddressForRecipient(
|
std::pair<libzcash::PaymentAddress, RecipientType> GetPaymentAddressForRecipient(
|
||||||
const uint256& txid,
|
const uint256& txid,
|
||||||
const libzcash::RecipientAddress& recipient) const;
|
const libzcash::RecipientAddress& recipient) const;
|
||||||
|
|
||||||
bool IsInternalRecipient(
|
bool IsInternalRecipient(
|
||||||
const libzcash::RecipientAddress& recipient) const;
|
const libzcash::RecipientAddress& recipient) const;
|
||||||
|
|
||||||
|
@ -2019,7 +2026,7 @@ enum class PaymentAddressSource {
|
||||||
MnemonicHDSeed,
|
MnemonicHDSeed,
|
||||||
Imported,
|
Imported,
|
||||||
ImportedWatchOnly,
|
ImportedWatchOnly,
|
||||||
AddressNotFound,
|
AddressNotFound
|
||||||
};
|
};
|
||||||
|
|
||||||
// GetSourceForPaymentAddress visitor :: (CWallet&, PaymentAddress) -> PaymentAddressSource
|
// GetSourceForPaymentAddress visitor :: (CWallet&, PaymentAddress) -> PaymentAddressSource
|
||||||
|
|
|
@ -208,6 +208,28 @@ public:
|
||||||
|
|
||||||
libzcash::OrchardRawAddress GetChangeAddress() const;
|
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 decrypion 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)
|
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
|
||||||
{
|
{
|
||||||
if (this != &key) {
|
if (this != &key) {
|
||||||
|
|
|
@ -135,6 +135,11 @@ class SaplingDiversifiableFullViewingKey {
|
||||||
protected:
|
protected:
|
||||||
SaplingDiversifiableFullViewingKey GetInternalDFVK() const;
|
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:
|
public:
|
||||||
libzcash::SaplingFullViewingKey fvk;
|
libzcash::SaplingFullViewingKey fvk;
|
||||||
uint256 dk;
|
uint256 dk;
|
||||||
|
@ -172,12 +177,6 @@ public:
|
||||||
*/
|
*/
|
||||||
std::pair<uint256, uint256> GetOVKs() const;
|
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
|
// Attempts to construct a valid internal payment address with diversifier
|
||||||
// index `j`; returns std::nullopt if `j` does not result in a valid diversifier
|
// index `j`; returns std::nullopt if `j` does not result in a valid diversifier
|
||||||
// given this xfvk.
|
// given this xfvk.
|
||||||
|
@ -185,8 +184,30 @@ public:
|
||||||
return GetInternalDFVK().Address(j);
|
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 decrypion 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;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
Loading…
Reference in New Issue