Merge pull request #5692 from nuttycom/feature/wallet_orchard-limited_ua_inputs

Add UnifiedAddress variant to ZTXOSelector
This commit is contained in:
Kris Nuttycombe 2022-03-17 13:42:41 -06:00 committed by GitHub
commit b708b8ef60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 291 additions and 109 deletions

View File

@ -1683,7 +1683,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!zaddr.has_value()) {
return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address."));
}
auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true);
auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true, false);
minerAddressInLocalWallet = ztxoSelector.has_value();
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {

View File

@ -389,6 +389,47 @@ CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) c
return std::visit(FindUFVKId(*this), receiver);
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) const
{
std::optional<libzcash::UFVKId> ufvkId;
std::optional<libzcash::diversifier_index_t> j;
bool jConflict = false;
for (const auto& receiver : addr) {
// skip unknown receivers
if (libzcash::HasKnownReceiverType(receiver)) {
auto tmp = GetUFVKMetadataForReceiver(receiver);
if (ufvkId.has_value() && tmp.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()) {
return std::nullopt;
}
if (tmp.value().second.has_value()) {
if (j.has_value()) {
if (tmp.value().second.value() != j.value()) {
jConflict = true;
j = std::nullopt;
}
} else if (!jConflict) {
j = tmp.value().second.value();
}
}
} else if (tmp.has_value()) {
ufvkId = tmp.value().first;
j = tmp.value().second;
}
}
}
if (ufvkId.has_value()) {
return std::make_pair(ufvkId.value(), j);
} else {
return std::nullopt;
}
}
std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const libzcash::ViewingKey& vk) const
{
std::optional<libzcash::UFVKId> result;

View File

@ -125,18 +125,23 @@ public:
const libzcash::UnifiedAddress& ua) = 0;
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId
) const = 0;
const libzcash::UFVKId& keyId) const = 0;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const = 0;
const libzcash::Receiver& receiver) const = 0;
virtual std::optional<libzcash::UFVKId>
GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk
) const = 0;
/**
* If all the receivers of the specified address correspond to a single
* 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(
const libzcash::UnifiedAddress& addr) const = 0;
virtual std::optional<libzcash::UFVKId> GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk) const = 0;
};
typedef std::map<CKeyID, CKey> KeyMap;
@ -376,13 +381,34 @@ public:
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const;
const libzcash::Receiver& receiver) const;
virtual std::optional<libzcash::UFVKId>
GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk
) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUFVKForReceiver(
const libzcash::Receiver& receiver) const {
auto ufvkMeta = GetUFVKMetadataForReceiver(receiver);
if (ufvkMeta.has_value()) {
return GetUnifiedFullViewingKey(ufvkMeta.value().first);
} else {
return std::nullopt;
}
}
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
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);
} else {
return std::nullopt;
}
}
virtual std::optional<libzcash::UFVKId> GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk) const;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;

View File

@ -319,6 +319,25 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
}
auto ovks = this->SelectOVKs(spendable);
auto allowChangeTypes = [&](const std::set<ReceiverType>& receiverTypes) {
for (ReceiverType rtype : receiverTypes) {
switch (rtype) {
case ReceiverType::P2PKH:
case ReceiverType::P2SH:
allowedChangeTypes.insert(OutputPool::Transparent);
break;
case ReceiverType::Sapling:
allowedChangeTypes.insert(OutputPool::Sapling);
break;
case ReceiverType::Orchard:
if (builder_.SupportsOrchard()) {
allowedChangeTypes.insert(OutputPool::Orchard);
}
break;
}
}
};
std::visit(match {
[&](const CKeyID& keyId) {
allowedChangeTypes.insert(OutputPool::Transparent);
@ -368,34 +387,37 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
builder_.SendChangeTo(changeAddr.value(), ovks.first);
}
},
[&](const libzcash::UnifiedFullViewingKey& fvk) {
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), fvk);
auto changeAddr = zufvk.GetChangeAddress();
[&](const libzcash::UnifiedAddress& ua) {
allowChangeTypes(ua.GetKnownReceiverTypes());
auto zufvk = pwalletMain->GetUFVKForAddress(ua);
if (!zufvk.has_value()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Could not determine full viewing key for unified address.");
}
auto changeAddr = zufvk.value().GetChangeAddress(allowedChangeTypes);
if (!changeAddr.has_value()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Could not generate a change address from the specified full viewing key ");
"Could not generate a change address from the inferred full viewing key.");
}
builder_.SendChangeTo(changeAddr.value(), ovks.first);
},
[&](const libzcash::UnifiedFullViewingKey& fvk) {
allowChangeTypes(fvk.GetKnownReceiverTypes());
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), fvk);
auto changeAddr = zufvk.GetChangeAddress(allowedChangeTypes);
if (!changeAddr.has_value()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Could not generate a change address from the specified full viewing key.");
}
builder_.SendChangeTo(changeAddr.value(), ovks.first);
},
[&](const AccountZTXOPattern& acct) {
for (ReceiverType rtype : acct.GetReceiverTypes()) {
switch (rtype) {
case ReceiverType::P2PKH:
case ReceiverType::P2SH:
allowedChangeTypes.insert(OutputPool::Transparent);
break;
case ReceiverType::Sapling:
allowedChangeTypes.insert(OutputPool::Sapling);
break;
case ReceiverType::Orchard:
if (builder_.SupportsOrchard()) {
allowedChangeTypes.insert(OutputPool::Orchard);
}
break;
}
}
allowChangeTypes(acct.GetReceiverTypes());
auto changeAddr = pwalletMain->GenerateChangeAddressForAccount(
acct.GetAccountId(),
allowedChangeTypes);
@ -477,7 +499,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
},
[&](const libzcash::SaplingPaymentAddress& addr) {
auto value = r.amount;
auto memo = get_memo_from_hex_string(r.memo.has_value() ? r.memo.value() : "");
auto memo = get_memo_from_hex_string(r.memo.value_or(""));
builder_.AddSaplingOutput(ovks.second, addr, value, memo);
},
@ -553,15 +575,27 @@ std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(const Spendab
if (!spendable.orchardNoteMetadata.empty()) {
std::optional<OrchardFullViewingKey> fvk;
std::visit(match {
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
[&](const UnifiedAddress& addr) {
auto ufvk = pwalletMain->GetUFVKForAddress(addr);
// This is safe because spending key checks will have ensured that we
// have a UFVK corresponding to this address, and Orchard notes will
// not have been selected if the UFVK does not contain an Orchard key.
fvk = ufvk.value().GetOrchardKey().value();
},
[&](const UnifiedFullViewingKey& ufvk) {
// Orchard notes will not have been selected if the UFVK does not contain
// an Orchard key.
fvk = ufvk.GetOrchardKey().value();
},
[&](const AccountZTXOPattern& acct) {
// By definition, we have a UFVK for every known account.
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId());
// Orchard notes will not have been selected if the UFVK does not contain
// an Orchard key.
fvk = ufvk.value().GetOrchardKey().value();
},
[&](const auto& other) {
throw std::runtime_error("unreachable");
throw std::runtime_error("SelectOVKs: Selector cannot select Orchard notes.");
}
}, this->ztxoSelector_.GetPattern());
assert(fvk.has_value());
@ -576,12 +610,27 @@ std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(const Spendab
assert(pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk));
dfvk = extsk.ToXFVK();
},
[&](const UnifiedAddress& addr) {
auto ufvk = pwalletMain->GetUFVKForAddress(addr);
// This is safe because spending key checks will have ensured that we
// have a UFVK corresponding to this address, and Sapling notes will
// not have been selected if the UFVK does not contain a Sapling key.
dfvk = ufvk.value().GetSaplingKey().value();
},
[&](const UnifiedFullViewingKey& ufvk) {
// Sapling notes will not have been selected if the UFVK does not contain
// a Sapling key.
dfvk = ufvk.GetSaplingKey().value();
},
[&](const AccountZTXOPattern& acct) {
// By definition, we have a UFVK for every known account.
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId());
// Sapling notes will not have been selected if the UFVK does not contain
// a Sapling key.
dfvk = ufvk.value().GetSaplingKey().value();
},
[&](const auto& other) {
throw std::runtime_error("unreachable");
throw std::runtime_error("SelectOVKs: Selector cannot select Sapling notes.");
}
}, this->ztxoSelector_.GetPattern());
assert(dfvk.has_value());
@ -598,16 +647,31 @@ std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(const Spendab
[&](const CScriptID& keyId) {
tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey();
},
[&](const UnifiedAddress& addr) {
// This is safe because spending key checks will have ensured that we
// have a UFVK corresponding to this address, and transparent UTXOs will
// not have been selected if the UFVK does not contain a transparent key.
auto ufvk = pwalletMain->GetUFVKForAddress(addr);
tfvk = ufvk.value().GetTransparentKey().value();
},
[&](const UnifiedFullViewingKey& ufvk) {
// Transparent UTXOs will not have been selected if the UFVK does not contain
// a transparent key.
tfvk = ufvk.GetTransparentKey().value();
},
[&](const AccountZTXOPattern& acct) {
if (acct.GetAccountId() == ZCASH_LEGACY_ACCOUNT) {
tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey();
} else {
// By definition, we have a UFVK for every known account.
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()).value();
// Transparent UTXOs will not have been selected if the UFVK does not contain
// a transparent key.
tfvk = ufvk.GetTransparentKey().value();
}
},
[&](const auto& other) {
throw std::runtime_error("unreachable");
throw std::runtime_error("SelectOVKs: Selector cannot select transparent UTXOs.");
}
}, this->ztxoSelector_.GetPattern());
assert(tfvk.has_value());

View File

@ -6,6 +6,8 @@
#include "zcash/address/sapling.hpp"
#include "zcash/address/sprout.hpp"
using namespace libzcash;
void PrintTo(const OutputPool& pool, std::ostream* os) {
switch (pool) {
case OutputPool::Orchard: *os << "Orchard"; break;

View File

@ -3707,7 +3707,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
nBalance = getBalanceZaddr(addr, nMinDepth, INT_MAX, false);
},
[&](const libzcash::UnifiedAddress& addr) {
auto selector = pwalletMain->ZTXOSelectorForAddress(addr, true);
auto selector = pwalletMain->ZTXOSelectorForAddress(addr, true, false);
if (!selector.has_value()) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
@ -4525,7 +4525,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
"Invalid from address: should be a taddr, zaddr, UA, or the string 'ANY_TADDR'.");
}
auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress(decoded.value(), true);
auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress(decoded.value(), true, false);
if (!ztxoSelectorOpt.has_value()) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,

View File

@ -799,7 +799,7 @@ void CheckHaveAddr(const std::optional<libzcash::PaymentAddress>& addr) {
auto addr_of_type = std::get_if<ADDR_TYPE>(&(addr.value()));
BOOST_ASSERT(addr_of_type != nullptr);
BOOST_CHECK(pwalletMain->ZTXOSelectorForAddress(*addr_of_type, true).has_value());
BOOST_CHECK(pwalletMain->ZTXOSelectorForAddress(*addr_of_type, true, false).has_value());
}
BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) {
@ -1233,7 +1233,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
// there are no utxos to spend
{
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr1, true).value();
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr1, true, false).value();
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
@ -1245,7 +1245,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
// there are no unspent notes to spend
{
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true, false).value();
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, taddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
@ -1257,7 +1257,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
// get_memo_from_hex_string())
{
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true, false).value();
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
@ -1358,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
auto builder = TransactionBuilder(consensusParams, nextBlockHeight, std::nullopt, pwalletMain);
mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true).value();
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true, false).value();
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, pa, 1*COIN, "ABCD") };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0));
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);

View File

@ -1644,7 +1644,11 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAccount(
}
}
std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(const libzcash::PaymentAddress& addr, bool requireSpendingKey) const {
std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(
const libzcash::PaymentAddress& addr,
bool requireSpendingKey,
bool allowAddressLinkability) const
{
auto self = this;
std::optional<ZTXOPattern> pattern = std::nullopt;
std::visit(match {
@ -1669,17 +1673,23 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(const libzcash::Paym
}
},
[&](const libzcash::UnifiedAddress& ua) {
auto ufvkId = this->FindUnifiedFullViewingKey(ua);
if (ufvkId.has_value()) {
auto ufvkMeta = this->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(ufvkId.value());
auto accountId = this->GetUnifiedAccountId(ufvkMeta.value().first);
if (accountId.has_value()) {
pattern = AccountZTXOPattern(accountId.value(), ua.GetKnownReceiverTypes());
if (allowAddressLinkability) {
pattern = AccountZTXOPattern(accountId.value(), ua.GetKnownReceiverTypes());
} else {
pattern = ua;
}
}
} else {
pattern = ua;
}
}
}, addr);
@ -1762,6 +1772,12 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
result = self->GetUnifiedAccountId(ufvkid.value());
}
},
[&](const libzcash::UnifiedAddress& addr) {
auto meta = GetUFVKMetadataForAddress(addr);
if (meta.has_value()) {
result = self->GetUnifiedAccountId(meta.value().first);
}
},
[&](const libzcash::UnifiedFullViewingKey& vk) {
result = self->GetUnifiedAccountId(vk.GetKeyID(Params()));
},
@ -1796,6 +1812,27 @@ bool CWallet::SelectorMatchesAddress(
[&](const libzcash::SproutViewingKey& vk) { return false; },
[&](const libzcash::SaplingPaymentAddress& addr) { return false; },
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) { return false; },
[&](const libzcash::UnifiedAddress& uaSelector) {
// for a UA selector when matching transparent addresses, we only match addresses
// that explicitly appear as receivers in the UA.
for (const auto& receiver : uaSelector) {
bool matches = std::visit(match {
[&](const libzcash::OrchardRawAddress& orchardAddr) { return false; },
[&](const libzcash::SaplingPaymentAddress& saplingAddr) { return false; },
[&](const libzcash::UnknownReceiver& receiver) { return false; },
[&](const CScriptID& scriptId) {
CTxDestination scriptIdDest = scriptId;
return address == scriptIdDest;
},
[&](const CKeyID& keyId) {
CTxDestination keyIdDest = keyId;
return address == keyIdDest;
}
}, receiver);
if (matches) return true;
}
return false;
},
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>> meta;
std::visit(match {
@ -1854,6 +1891,19 @@ bool CWallet::SelectorMatchesAddress(
auto addr = extfvk.Address(j);
return addr.has_value() && addr.value() == a0;
},
[&](const libzcash::UnifiedAddress& ua) {
const auto a0Meta = self->GetUFVKMetadataForReceiver(a0);
auto saplingReceiver = ua.GetSaplingReceiver();
if (saplingReceiver.has_value()) {
const auto uaMeta = self->GetUFVKMetadataForReceiver(saplingReceiver.value());
// if the Sapling address is derived from any UFVK corresponding to
// 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;
}
return false;
},
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
auto saplingKey = ufvk.GetSaplingKey();
if (saplingKey.has_value()) {
@ -2067,6 +2117,22 @@ SpendableInputs CWallet::FindSpendableInputs(
if (selectOrchard) {
// for Orchard, we select both the internal and external IVKs.
auto orchardIvks = std::visit(match {
[&](const libzcash::UnifiedAddress& selectorUA) -> std::vector<OrchardIncomingViewingKey> {
auto orchardReceiver = selectorUA.GetOrchardReceiver();
if (orchardReceiver.has_value()) {
auto meta = GetUFVKMetadataForReceiver(orchardReceiver.value());
if (meta.has_value()) {
auto ufvk = GetUnifiedFullViewingKey(meta.value().first);
if (ufvk.has_value()) {
auto fvk = ufvk->GetOrchardKey();
if (fvk.has_value()) {
return {fvk->ToIncomingViewingKey(), fvk->ToInternalIncomingViewingKey()};
}
}
}
}
return {};
},
[&](const libzcash::UnifiedFullViewingKey& ufvk) -> std::vector<OrchardIncomingViewingKey> {
auto fvk = ufvk.GetOrchardKey();
if (fvk.has_value()) {
@ -6585,30 +6651,6 @@ void CWallet::GetFilteredNotes(
}
}
std::optional<UFVKId> CWallet::FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const {
std::optional<libzcash::UFVKId> ufvkId;
for (const auto& receiver : addr) {
// skip unknown receivers
if (libzcash::HasKnownReceiverType(receiver)) {
auto ufvkPair = this->GetUFVKMetadataForReceiver(receiver);
if (ufvkPair.has_value()) {
if (ufvkId.has_value()) {
// Check that all the receivers belong to the same ufvk; if not,
// we cannot identify a unique UFVK for the address.
if (ufvkPair.value().first != ufvkId.value()) {
return std::nullopt;
}
} else {
ufvkId = ufvkPair.value().first;
}
} else {
return std::nullopt;
}
}
}
return ufvkId;
}
std::optional<libzcash::AccountId> CWallet::GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const {
auto addrMetaIt = mapUfvkAddressMetadata.find(ufvkId);
if (addrMetaIt != mapUfvkAddressMetadata.end()) {
@ -6618,10 +6660,6 @@ std::optional<libzcash::AccountId> CWallet::GetUnifiedAccountId(const libzcash::
}
}
std::optional<ZcashdUnifiedFullViewingKey> CWallet::FindUFVKByReceiver(const libzcash::Receiver& receiver) const {
return std::visit(UFVKForReceiver(*this), receiver);
}
std::optional<UnifiedAddress> CWallet::FindUnifiedAddressByReceiver(const Receiver& receiver) const {
return std::visit(UnifiedAddressForReceiver(*this), receiver);
}
@ -6655,7 +6693,7 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAdd
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &uaddr) const
{
return m_wallet->FindUnifiedFullViewingKey(uaddr).has_value();
return m_wallet->GetUFVKForAddress(uaddr).has_value();
}
// GetSourceForPaymentAddress
@ -6748,13 +6786,13 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
auto hdChain = m_wallet->GetMnemonicHDChain();
auto ufvkid = m_wallet->FindUnifiedFullViewingKey(uaddr);
auto ufvkid = m_wallet->GetUFVKMetadataForAddress(uaddr);
if (ufvkid.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
// the wallet's mnemonic seed.
for (const auto& [k, v] : m_wallet->mapUnifiedAccountKeys) {
if (v == ufvkid.value() && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) {
if (v == ufvkid.value().first && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) {
return PaymentAddressSource::MnemonicHDSeed;
}
}
@ -6809,9 +6847,7 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
auto ufvkid = m_wallet->FindUnifiedFullViewingKey(uaddr);
if (!ufvkid.has_value()) return std::nullopt;
auto zufvk = m_wallet->GetUnifiedFullViewingKey(ufvkid.value());
auto zufvk = m_wallet->GetUFVKForAddress(uaddr);
if (!zufvk.has_value()) return std::nullopt;
return zufvk.value().ToFullViewingKey();
}
@ -7061,6 +7097,9 @@ bool ZTXOSelector::SelectsTransparent() const {
[](const libzcash::SproutViewingKey& vk) { return false; },
[](const libzcash::SaplingPaymentAddress& addr) { return false; },
[](const libzcash::SaplingExtendedFullViewingKey& vk) { return false; },
[](const libzcash::UnifiedAddress& ua) {
return ua.GetP2PKHReceiver().has_value() || ua.GetP2SHReceiver().has_value();
},
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetTransparentKey().has_value(); },
[](const AccountZTXOPattern& acct) { return acct.IncludesP2PKH() || acct.IncludesP2SH(); }
}, this->pattern);
@ -7076,6 +7115,7 @@ bool ZTXOSelector::SelectsSapling() const {
return std::visit(match {
[](const libzcash::SaplingPaymentAddress& addr) { return true; },
[](const libzcash::SaplingExtendedSpendingKey& extfvk) { return true; },
[](const libzcash::UnifiedAddress& ua) { return ua.GetSaplingReceiver().has_value(); },
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetSaplingKey().has_value(); },
[](const AccountZTXOPattern& acct) { return acct.IncludesSapling(); },
[](const auto& addr) { return false; }
@ -7083,6 +7123,7 @@ bool ZTXOSelector::SelectsSapling() const {
}
bool ZTXOSelector::SelectsOrchard() const {
return std::visit(match {
[](const libzcash::UnifiedAddress& ua) { return ua.GetOrchardReceiver().has_value(); },
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetOrchardKey().has_value(); },
[](const AccountZTXOPattern& acct) { return acct.IncludesOrchard(); },
[](const auto& addr) { return false; }

View File

@ -778,6 +778,7 @@ typedef std::variant<
libzcash::SproutViewingKey,
libzcash::SaplingPaymentAddress,
libzcash::SaplingExtendedFullViewingKey,
libzcash::UnifiedAddress,
libzcash::UnifiedFullViewingKey,
AccountZTXOPattern> ZTXOPattern;
@ -805,17 +806,6 @@ public:
bool SelectsOrchard() const;
};
/**
* An enumeration of the fund pools for which a transaction may produce outputs.
* It is sorted in descending preference order, so that when iterating over a
* set of output pools the most-preferred pool is selected first.
*/
enum class OutputPool {
Orchard,
Sapling,
Transparent,
};
class SpendableInputs {
private:
bool limited = false;
@ -840,7 +830,7 @@ public:
bool LimitToAmount(
CAmount amount,
CAmount dustThreshold,
std::set<OutputPool> recipientPools);
std::set<libzcash::OutputPool> recipientPools);
/**
* Compute the total ZEC amount of spendable inputs.
@ -1335,7 +1325,8 @@ public:
*/
std::optional<ZTXOSelector> ZTXOSelectorForAddress(
const libzcash::PaymentAddress& addr,
bool requireSpendingKey) const;
bool requireSpendingKey,
bool allowAddressLinkability) const;
/**
* Returns the ZTXO selector for the specified viewing key, if that key
@ -1377,7 +1368,7 @@ public:
*/
std::optional<libzcash::RecipientAddress> GenerateChangeAddressForAccount(
libzcash::AccountId accountId,
std::set<OutputPool> changeOptions);
std::set<libzcash::OutputPool> changeOptions);
SpendableInputs FindSpendableInputs(
ZTXOSelector paymentSource,
@ -1595,11 +1586,16 @@ public:
//! failures in reconstructing the cache.
bool LoadCaches();
std::optional<libzcash::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> FindUFVKByReceiver(const libzcash::Receiver& receiver) const;
std::optional<libzcash::UnifiedAddress> FindUnifiedAddressByReceiver(const libzcash::Receiver& receiver) const;
/**
* Reconstructs a unified address by determining the UFVK that the receiver
* is associated with, combined with the set of receiver types that were
* associated with the diversifier index that the provided receiver
* corresponds to.
*/
std::optional<libzcash::UnifiedAddress> FindUnifiedAddressByReceiver(
const libzcash::Receiver& receiver) const;
/**
* Increment the next transaction order id

View File

@ -188,14 +188,15 @@ std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(co
return addr;
}
std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress() const {
if (orchardKey.has_value()) {
std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(
const std::set<OutputPool>& allowedPools) const {
if (orchardKey.has_value() && allowedPools.count(OutputPool::Orchard) > 0) {
return orchardKey.value().GetChangeAddress();
}
if (saplingKey.has_value()) {
if (saplingKey.has_value() && allowedPools.count(OutputPool::Sapling) > 0) {
return saplingKey.value().GetChangeAddress();
}
if (transparentKey.has_value()) {
if (transparentKey.has_value() && allowedPools.count(OutputPool::Transparent) > 0) {
auto changeAddr = transparentKey.value().GetChangeAddress(diversifier_index_t(0));
if (changeAddr.has_value()) {
return changeAddr.value();

View File

@ -27,6 +27,17 @@ enum class ReceiverType: uint32_t {
Orchard = 0x03
};
/**
* An enumeration of the fund pools for which a transaction may produce outputs.
* It is sorted in descending preference order, so that when iterating over a
* set of output pools the most-preferred pool is selected first.
*/
enum class OutputPool {
Orchard,
Sapling,
Transparent,
};
enum class UnifiedAddressGenerationError {
ShieldedReceiverNotFound,
ReceiverTypeNotAvailable,
@ -226,7 +237,7 @@ public:
* *any* shielded pool) in which case the change address returned will be
* associated with diversifier index 0.
*/
std::optional<RecipientAddress> GetChangeAddress() const;
std::optional<RecipientAddress> GetChangeAddress(const std::set<OutputPool>& allowedPools) const;
UnifiedFullViewingKey ToFullViewingKey() const;