Add support for unified addresses to CWallet::ToZTXOSelector

This commit is contained in:
Kris Nuttycombe 2022-01-12 17:26:38 -07:00
parent f850e89449
commit e419899a29
5 changed files with 65 additions and 5 deletions

View File

@ -61,7 +61,10 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
std::visit(match {
[&](const AccountZTXOSelector& acct) {
isfromtaddr_ = (acct.GetAccountId() == ZCASH_LEGACY_ACCOUNT);
isfromtaddr_ =
acct.GetReceiverTypes().empty() ||
acct.GetReceiverTypes().count(ReceiverType::P2PKH) > 0 ||
acct.GetReceiverTypes().count(ReceiverType::P2SH) > 0;
},
[&](const PaymentAddress& addr) {
// We don't need to lock on the wallet as spending key related methods are thread-safe
@ -252,6 +255,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
// and send the extra to the recipient or the miner fee to avoid
// creating dust change, rather than prohibit them from sending
// entirely in this circumstance.
// (Daira disagrees, as this could leak information to the recipient)
throw JSONRPCError(
RPC_WALLET_INSUFFICIENT_FUNDS,
strprintf(

View File

@ -1359,9 +1359,13 @@ void CWallet::SyncMetaData(pair<typename TxSpendMap<T>::iterator, typename TxSpe
}
}
//
// Zcash transaction output selectors
//
std::optional<ZTXOSelector> CWallet::ToZTXOSelector(const libzcash::PaymentAddress& addr, bool requireSpendingKey) const {
auto self = this;
std::optional<ZTXOSelector> result;
std::optional<ZTXOSelector> result = std::nullopt;
std::visit(match {
[&](const CKeyID& addr) {
if (!requireSpendingKey || self->HaveKey(addr)) {
@ -1384,7 +1388,21 @@ std::optional<ZTXOSelector> CWallet::ToZTXOSelector(const libzcash::PaymentAddre
}
},
[&](const libzcash::UnifiedAddress& ua) {
// TODO: Find the unified account corresponding to this UA
auto ufvkId = this->FindUnifiedFullViewingKey(ua);
if (ufvkId.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 addrMetaIt = mapUfvkAddressMetadata.find(ufvkId.value());
if (addrMetaIt != mapUfvkAddressMetadata.end()) {
auto accountId = addrMetaIt->second.GetAccountId();
if (accountId.has_value()) {
result = AccountZTXOSelector(accountId.value(), ua.GetKnownReceiverTypes());
}
}
}
}
}, addr);
return result;
@ -5700,8 +5718,31 @@ 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<UnifiedAddress> CWallet::GetUnifiedForReceiver(const Receiver& receiver) {
std::optional<UnifiedAddress> CWallet::GetUnifiedForReceiver(const Receiver& receiver) const {
return std::visit(LookupUnifiedAddress(*this), receiver);
}

View File

@ -846,6 +846,10 @@ public:
}
}
std::optional<libzcash::AccountId> GetAccountId() const {
return accountId;
}
bool SetAccountId(libzcash::AccountId accountIdIn) {
if (accountId.has_value()) {
return (accountIdIn == accountId.value());
@ -1358,7 +1362,8 @@ public:
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver);
std::optional<libzcash::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver) const;
/**
* Increment the next transaction order id

View File

@ -73,6 +73,14 @@ std::optional<SaplingPaymentAddress> UnifiedAddress::GetSaplingReceiver() const
return std::nullopt;
}
bool HasKnownReceiverType(const Receiver& receiver) {
return std::visit(match {
[](const SaplingPaymentAddress& addr) { return true; },
[](const CScriptID& addr) { return true; },
[](const CKeyID& addr) { return true; },
[](const UnknownReceiver& addr) { return false; }
}, receiver);
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const {
return std::make_pair("sprout", sk.address());

View File

@ -56,6 +56,8 @@ typedef std::variant<
CKeyID,
UnknownReceiver> Receiver;
bool HasKnownReceiverType(const Receiver& receiver);
struct ReceiverIterator {
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;