From 0850f8aa4fbd278b3dc5f205ed86a9f6b9233729 Mon Sep 17 00:00:00 2001 From: Eunovo Date: Wed, 18 Sep 2024 18:23:23 +0000 Subject: [PATCH] wallet/rpc: Add silent payment labels support Update the following rpc functions to ignore silent payment destinations in the wallet address book - getaddressbylabel - getreceivedbylabel -listreceivedbylabel --- src/wallet/interfaces.cpp | 2 +- src/wallet/rpc/addresses.cpp | 31 +++++++++++-------------- src/wallet/rpc/coins.cpp | 2 +- src/wallet/rpc/transactions.cpp | 2 +- src/wallet/scriptpubkeyman.cpp | 8 ++++++- src/wallet/wallet.cpp | 38 ++++++++++++++++++++---------- src/wallet/wallet.h | 4 +++- src/wallet/walletutil.h | 41 +++++++++++++++++++++++++++++++++ 8 files changed, 94 insertions(+), 34 deletions(-) diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 9fab1b2ee44a30..a058dc6825cf72 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -224,7 +224,7 @@ class WalletImpl : public Wallet isminetype is_mine = m_wallet->IsMine(dest); // In very old wallets, address purpose may not be recorded so we derive it from IsMine result.emplace_back(dest, is_mine, purpose.value_or(is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND), label); - }); + }, std::nullopt); return result; } std::vector getAddressReceiveRequests() override { diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 429b21349700b2..d601da98bedf1d 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -721,23 +721,20 @@ RPCHelpMan getaddressesbylabel() UniValue ret(UniValue::VOBJ); std::set addresses; pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, bool _is_change, const std::optional& _purpose) { - if (_is_change) return; - if (_label == label) { - std::string address = EncodeDestination(_dest); - // CWallet::m_address_book is not expected to contain duplicate - // address strings, but build a separate set as a precaution just in - // case it does. - bool unique = addresses.emplace(address).second; - CHECK_NONFATAL(unique); - // UniValue::pushKV checks if the key exists in O(N) - // and since duplicate addresses are unexpected (checked with - // std::set in O(log(N))), UniValue::pushKVEnd is used instead, - // which currently is O(1). - UniValue value(UniValue::VOBJ); - value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown"); - ret.pushKVEnd(address, std::move(value)); - } - }); + std::string address = EncodeDestination(_dest); + // CWallet::m_address_book is not expected to contain duplicate + // address strings, but build a separate set as a precaution just in + // case it does. + bool unique = addresses.emplace(address).second; + CHECK_NONFATAL(unique); + // UniValue::pushKV checks if the key exists in O(N) + // and since duplicate addresses are unexpected (checked with + // std::set in O(log(N))), UniValue::pushKVEnd is used instead, + // which currently is O(1). + UniValue value(UniValue::VOBJ); + value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown"); + ret.pushKVEnd(address, std::move(value)); + }, CWallet::AddrBookFilter{.m_op_label = label, .m_ignore_output_types = std::vector{OutputType::SILENT_PAYMENT}, .ignore_change = true}); if (ret.empty()) { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label)); diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index 2cf94a57223044..cd62711db25f15 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -23,7 +23,7 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b std::vector addresses; if (by_label) { // Get the set of addresses assigned to label - addresses = wallet.ListAddrBookAddresses(CWallet::AddrBookFilter{LabelFromValue(params[0])}); + addresses = wallet.ListAddrBookAddresses(CWallet::AddrBookFilter{ .m_op_label = LabelFromValue(params[0]), .m_ignore_output_types = std::vector{OutputType::SILENT_PAYMENT} }); if (addresses.empty()) throw JSONRPCError(RPC_WALLET_ERROR, "Label not found in wallet"); } else { // Get the address diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index 4c1b183fd6d4e6..d3f0e70d6618ee 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -182,7 +182,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons if (entry) func(*filtered_address, entry->GetLabel(), entry->IsChange(), entry->purpose); } else { // No filtered addr, walk-through the addressbook entry - wallet.ForEachAddrBookEntry(func); + wallet.ForEachAddrBookEntry(func, CWallet::AddrBookFilter{ .m_ignore_output_types = std::vector{OutputType::SILENT_PAYMENT} }); } if (by_label) { diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index f59642dd69d3e9..75a16fa286e1ac 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -3047,7 +3047,13 @@ std::optional SilentPaymentDescriptorScriptPubKeyMan::GetLabelle auto label = tweaks.value()[0].label; if (!label.has_value()) return std::nullopt; - V0SilentPaymentDestination sp_dest(sppubkey->scanKey.GetPubKey(), *label); + auto it = m_map_label_tweaks.find(*label); + if (it == m_map_label_tweaks.end()) return std::nullopt; + auto label_tweak = it->second; + CPubKey tweaked_pubkey(sppubkey->spendKey.begin(), sppubkey->spendKey.end()); + tweaked_pubkey.TweakAdd(label_tweak.data()); + + V0SilentPaymentDestination sp_dest(sppubkey->scanKey.GetPubKey(), tweaked_pubkey); return sp_dest; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a49cd25dc0470a..1b845bd6520d89 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1243,7 +1243,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const std::ma if (fExisted && !fUpdate) return false; auto sp_data = GetSilentPaymentsData(tx, spent_coins); - bool isMineSilentPayment = !tx.IsCoinBase() && IsMineSilentPayment(sp_data->first, sp_data->second); + bool isMineSilentPayment = !tx.IsCoinBase() && sp_data && IsMineSilentPayment(sp_data->first, sp_data->second); if (fExisted || IsMine(tx) || isMineSilentPayment || IsFromMe(tx)) { /* Check if any keys in the wallet keypool that were supposed to be unused @@ -2682,28 +2682,42 @@ void CWallet::MarkDestinationsDirty(const std::set& destinations } } -void CWallet::ForEachAddrBookEntry(const ListAddrBookFunc& func) const +void CWallet::ForEachAddrBookEntry(const ListAddrBookFunc& func, const std::optional& _filter) const { AssertLockHeld(cs_wallet); + AddrBookFilter filter = _filter ? *_filter : AddrBookFilter(); for (const std::pair& item : m_address_book) { const auto& entry = item.second; + // Filter by change + if (filter.ignore_change && entry.IsChange()) continue; + // Filter by label + if (filter.m_op_label && *filter.m_op_label != entry.GetLabel()) continue; + // Filter by output type + if (filter.m_ignore_output_types) { + auto output_type = std::visit(DestinationToOutputTypeVisitor{}, item.first); + if (!output_type) continue; + bool is_ignored = false; + for (auto ignored_type : *filter.m_ignore_output_types) { + if (*output_type == ignored_type) { + is_ignored = true; + continue; + } + } + if (is_ignored) continue; + } + + // All good func(item.first, entry.GetLabel(), entry.IsChange(), entry.purpose); } } -std::vector CWallet::ListAddrBookAddresses(const std::optional& _filter) const +std::vector CWallet::ListAddrBookAddresses(const std::optional& filter) const { AssertLockHeld(cs_wallet); std::vector result; - AddrBookFilter filter = _filter ? *_filter : AddrBookFilter(); - ForEachAddrBookEntry([&result, &filter](const CTxDestination& dest, const std::string& label, bool is_change, const std::optional& purpose) { - // Filter by change - if (filter.ignore_change && is_change) return; - // Filter by label - if (filter.m_op_label && *filter.m_op_label != label) return; - // All good + ForEachAddrBookEntry([&result](const CTxDestination& dest, const std::string& label, bool is_change, const std::optional& purpose) { result.emplace_back(dest); - }); + }, filter); return result; } @@ -2717,7 +2731,7 @@ std::set CWallet::ListAddrBookLabels(const std::optional m_op_label{std::nullopt}; + // Ignore these OutputTypes + std::optional> m_ignore_output_types{std::nullopt}; // Don't include change addresses by default bool ignore_change{true}; }; @@ -765,7 +767,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * Stops when the provided 'ListAddrBookFunc' returns false. */ using ListAddrBookFunc = std::function purpose)>; - void ForEachAddrBookEntry(const ListAddrBookFunc& func) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void ForEachAddrBookEntry(const ListAddrBookFunc& func, const std::optional& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Marks all outputs in each one of the destinations dirty, so their cache is diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 19cdd28487dcb8..47a3b730a40ade 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -5,11 +5,13 @@ #ifndef BITCOIN_WALLET_WALLETUTIL_H #define BITCOIN_WALLET_WALLETUTIL_H +#include #include #include #include #include