Merge pull request #5692 from nuttycom/feature/wallet_orchard-limited_ua_inputs
Add UnifiedAddress variant to ZTXOSelector
This commit is contained in:
commit
b708b8ef60
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue