aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpc
diff options
context:
space:
mode:
authorAndrew Chow <achow101-github@achow101.com>2022-08-16 12:55:17 -0400
committerAndrew Chow <achow101-github@achow101.com>2022-08-16 13:08:05 -0400
commitc336f813b3cd1d222d4b37cce3a6fbd855c5f4ad (patch)
tree22b68943bdf1f1cf2b2c4865c1de3896acf9bb22 /src/wallet/rpc
parentcf39913e574857b8066795d33cb88ac5e45d05db (diff)
parenta6b0c1fcc06485ecd320727fa7534a51a20608c1 (diff)
downloadbitcoin-c336f813b3cd1d222d4b37cce3a6fbd855c5f4ad.tar.xz
Merge bitcoin/bitcoin#25504: RPC: allow to track coins by parent descriptors
a6b0c1fcc06485ecd320727fa7534a51a20608c1 doc: add releases notes for 25504 (listsinceblock updates) (Antoine Poinsot) 0fd2d144540b720626fc065a3cef5188831b5ee2 rpc: add an include_change parameter to listsinceblock (Antoine Poinsot) 55f98d087efd2609d808c082d5770306cc489409 rpc: output parent wallet descriptors for coins in listunspent (Antoine Poinsot) b724476158a7dfeef9edfda3f519dfd6f93202a8 rpc: output wallet descriptors for received entries in listsinceblock (Antoine Poinsot) 55a82eaf91d252a04a0cc8ad7d948d956c6cb24f wallet: allow to fetch the wallet descriptors for a given Script (Antoine Poinsot) Pull request description: Wallet descriptors are useful for applications using the Bitcoin Core wallet as a backend for tracking coins, as they allow to track coins for multiple descriptors in a single wallet. However there is no information currently given for such applications to link a coin with an imported descriptor, severely limiting the possibilities for such applications of using multiple descriptors in a single wallet. This PR outputs the matching imported descriptor(s) for a given received coin in `listsinceblock` (and friends). It comes from a need for an application i'm working on, but i think it's something any software using `bitcoind` to track multiple descriptors in a single wallet would have eventually. For instance i'm thinking about the BDK project. Currently, the way to achieve this is to import raw addresses with labels and to have your application be responsible for wallet things like the gap limit. I'll add this to the output of `listunspent` too if this gets a few Concept ACKs. ACKs for top commit: instagibbs: ACK https://github.com/bitcoin/bitcoin/pull/25504/commits/a6b0c1fcc06485ecd320727fa7534a51a20608c1 achow101: re-ACK a6b0c1fcc06485ecd320727fa7534a51a20608c1 Tree-SHA512: 7a5850e8de98b439ddede2cb72de0208944f8cda67272e8b8037678738d55b7a5272375be808b0f7d15def4904430e089dafdcc037436858ff3292c5f8b75e37
Diffstat (limited to 'src/wallet/rpc')
-rw-r--r--src/wallet/rpc/coins.cpp4
-rw-r--r--src/wallet/rpc/transactions.cpp23
-rw-r--r--src/wallet/rpc/util.cpp9
-rw-r--r--src/wallet/rpc/util.h4
4 files changed, 35 insertions, 5 deletions
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index d40d800f28..c01ca28290 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -543,6 +543,9 @@ RPCHelpMan listunspent()
{RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
{RPCResult::Type::BOOL, "reused", /*optional=*/true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
{RPCResult::Type::STR, "desc", /*optional=*/true, "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::ARR, "parent_descs", /*optional=*/false, "List of parent descriptors for the scriptPubKey of this coin.", {
+ {RPCResult::Type::STR, "desc", "The descriptor string."},
+ }},
{RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
"from outside keys and unconfirmed replacement transactions are considered unsafe\n"
"and are not eligible for spending by fundrawtransaction and sendtoaddress."},
@@ -722,6 +725,7 @@ RPCHelpMan listunspent()
entry.pushKV("desc", descriptor->ToString());
}
}
+ PushParentDescriptors(*pwallet, scriptPubKey, entry);
if (avoid_reuse) entry.pushKV("reused", reused);
entry.pushKV("safe", out.safe);
results.push_back(entry);
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 9c8003d6d7..a0769d3ecb 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -315,13 +315,16 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_label Optional label string to filter incoming transactions.
*/
template <class Vec>
-static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong, Vec& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong,
+ Vec& ret, const isminefilter& filter_ismine, const std::string* filter_label,
+ bool include_change = false)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
- CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine);
+ CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine, include_change);
bool involvesWatchonly = CachedTxIsFromMe(wallet, wtx, ISMINE_WATCH_ONLY);
@@ -367,6 +370,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
entry.pushKV("involvesWatchonly", true);
}
MaybePushAddress(entry, r.destination);
+ PushParentDescriptors(wallet, wtx.tx->vout.at(r.vout).scriptPubKey, entry);
if (wtx.IsCoinBase())
{
if (wallet.GetTxDepthInMainChain(wtx) < 1)
@@ -418,7 +422,11 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction, only present if not empty."},
{RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- "may be unknown for unconfirmed transactions not in the mempool."}};
+ "may be unknown for unconfirmed transactions not in the mempool."},
+ {RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'received'. List of parent descriptors for the scriptPubKey of this coin.", {
+ {RPCResult::Type::STR, "desc", "The descriptor string."},
+ }},
+ };
}
RPCHelpMan listtransactions()
@@ -543,6 +551,7 @@ RPCHelpMan listsinceblock()
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
"(not guaranteed to work on pruned nodes)"},
+ {"include_change", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also add entries for change outputs.\n"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -623,6 +632,7 @@ RPCHelpMan listsinceblock()
}
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
+ bool include_change = (!request.params[4].isNull() && request.params[4].get_bool());
int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
@@ -632,7 +642,7 @@ RPCHelpMan listsinceblock()
const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
- ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */, /*include_change=*/include_change);
}
}
@@ -649,7 +659,7 @@ RPCHelpMan listsinceblock()
if (it != wallet.mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */, /*include_change=*/include_change);
}
}
blockId = block.hashPrevBlock;
@@ -709,6 +719,9 @@ RPCHelpMan gettransaction()
"'send' category of transactions."},
{RPCResult::Type::BOOL, "abandoned", /*optional=*/true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
+ {RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'received'. List of parent descriptors for the scriptPubKey of this coin.", {
+ {RPCResult::Type::STR, "desc", "The descriptor string."},
+ }},
}},
}},
{RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp
index 4fcb393226..16305d4465 100644
--- a/src/wallet/rpc/util.cpp
+++ b/src/wallet/rpc/util.cpp
@@ -123,6 +123,15 @@ std::string LabelFromValue(const UniValue& value)
return label;
}
+void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
+{
+ UniValue parent_descs(UniValue::VARR);
+ for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
+ parent_descs.push_back(desc.descriptor->ToString());
+ }
+ entry.pushKV("parent_descs", parent_descs);
+}
+
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
{
if (!wallet) {
diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h
index 7b810eb06e..2ca28914e7 100644
--- a/src/wallet/rpc/util.h
+++ b/src/wallet/rpc/util.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_WALLET_RPC_UTIL_H
#define BITCOIN_WALLET_RPC_UTIL_H
+#include <script/script.h>
+
#include <any>
#include <memory>
#include <string>
@@ -39,6 +41,8 @@ const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wal
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param);
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet);
std::string LabelFromValue(const UniValue& value);
+//! Fetch parent descriptors of this scriptPubKey.
+void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry);
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error);
} // namespace wallet