· 4 years ago · Jul 27, 2021, 09:20 PM
1// Copyright (c) 2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <amount.h>
7#include <core_io.h>
8#include <interfaces/chain.h>
9#include <key_io.h>
10#include <node/context.h>
11#include <outputtype.h>
12#include <policy/feerate.h>
13#include <policy/fees.h>
14#include <policy/policy.h>
15#include <policy/rbf.h>
16#include <rpc/rawtransaction_util.h>
17#include <rpc/server.h>
18#include <rpc/util.h>
19#include <script/descriptor.h>
20#include <script/sign.h>
21#include <util/bip32.h>
22#include <util/fees.h>
23#include <util/message.h> // For MessageSign()
24#include <util/moneystr.h>
25#include <util/string.h>
26#include <util/system.h>
27#include <util/translation.h>
28#include <util/url.h>
29#include <util/vector.h>
30#include <wallet/coincontrol.h>
31#include <wallet/context.h>
32#include <wallet/feebumper.h>
33#include <wallet/fees.h>
34#include <wallet/load.h>
35#include <wallet/rpcwallet.h>
36#include <wallet/wallet.h>
37#include <wallet/walletdb.h>
38#include <wallet/walletutil.h>
39
40#include <optional>
41#include <stdint.h>
42
43#include <univalue.h>
44
45
46using interfaces::FoundBlock;
47
48static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
49static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
50
51static inline bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
52 bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
53 bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
54
55 if (avoid_reuse && !can_avoid_reuse) {
56 throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
57 }
58
59 return avoid_reuse;
60}
61
62
63/** Used by RPC commands that have an include_watchonly parameter.
64 * We default to true for watchonly wallets if include_watchonly isn't
65 * explicitly set.
66 */
67static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
68{
69 if (include_watchonly.isNull()) {
70 // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
71 return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
72 }
73
74 // otherwise return whatever include_watchonly was set to
75 return include_watchonly.get_bool();
76}
77
78
79/** Checks if a CKey is in the given CWallet compressed or otherwise*/
80bool HaveKey(const SigningProvider& wallet, const CKey& key)
81{
82 CKey key2;
83 key2.Set(key.begin(), key.end(), !key.IsCompressed());
84 return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
85}
86
87bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
88{
89 if (URL_DECODE && request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
90 // wallet endpoint was used
91 wallet_name = URL_DECODE(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
92 return true;
93 }
94 return false;
95}
96
97std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
98{
99 CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
100 std::string wallet_name;
101 if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
102 std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
103 if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
104 return pwallet;
105 }
106
107 std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
108 if (wallets.size() == 1) {
109 return wallets[0];
110 }
111
112 if (wallets.empty()) {
113 throw JSONRPCError(
114 RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)");
115 }
116 throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
117 "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
118}
119
120void EnsureWalletIsUnlocked(const CWallet& wallet)
121{
122 if (wallet.IsLocked()) {
123 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
124 }
125}
126
127WalletContext& EnsureWalletContext(const std::any& context)
128{
129 auto wallet_context = util::AnyPtr<WalletContext>(context);
130 if (!wallet_context) {
131 throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
132 }
133 return *wallet_context;
134}
135
136// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
137LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
138{
139 LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
140 if (!spk_man && also_create) {
141 spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
142 }
143 if (!spk_man) {
144 throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
145 }
146 return *spk_man;
147}
148
149static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniValue& entry)
150{
151 int confirms = wtx.GetDepthInMainChain();
152 entry.pushKV("confirmations", confirms);
153 if (wtx.IsCoinBase())
154 entry.pushKV("generated", true);
155 if (confirms > 0)
156 {
157 entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
158 entry.pushKV("blockheight", wtx.m_confirm.block_height);
159 entry.pushKV("blockindex", wtx.m_confirm.nIndex);
160 int64_t block_time;
161 CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time)));
162 entry.pushKV("blocktime", block_time);
163 } else {
164 entry.pushKV("trusted", wtx.IsTrusted());
165 }
166 uint256 hash = wtx.GetHash();
167 entry.pushKV("txid", hash.GetHex());
168 UniValue conflicts(UniValue::VARR);
169 for (const uint256& conflict : wtx.GetConflicts())
170 conflicts.push_back(conflict.GetHex());
171 entry.pushKV("walletconflicts", conflicts);
172 entry.pushKV("time", wtx.GetTxTime());
173 entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
174
175 // Add opt-in RBF status
176 std::string rbfStatus = "no";
177 if (confirms <= 0) {
178 RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
179 if (rbfState == RBFTransactionState::UNKNOWN)
180 rbfStatus = "unknown";
181 else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
182 rbfStatus = "yes";
183 }
184 entry.pushKV("bip125-replaceable", rbfStatus);
185
186 for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
187 entry.pushKV(item.first, item.second);
188}
189
190static std::string LabelFromValue(const UniValue& value)
191{
192 std::string label = value.get_str();
193 if (label == "*")
194 throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
195 return label;
196}
197
198/**
199 * Update coin control with fee estimation based on the given parameters
200 *
201 * @param[in] wallet Wallet reference
202 * @param[in,out] cc Coin control to be updated
203 * @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
204 * @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
205 * @param[in] fee_rate UniValue real; fee rate in sat/vB;
206 * if present, both conf_target and estimate_mode must either be null, or "unset"
207 * @param[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
208 * verify only that fee_rate is greater than 0
209 * @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
210 */
211static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
212{
213 if (!fee_rate.isNull()) {
214 if (!conf_target.isNull()) {
215 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
216 }
217 if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
218 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
219 }
220 // Fee rates in sat/vB cannot represent more than 3 significant digits.
221 cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /* decimals */ 3)};
222 if (override_min_fee) cc.fOverrideFeeRate = true;
223 // Default RBF to true for explicit fee_rate, if unset.
224 if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true;
225 return;
226 }
227 if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
228 throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
229 }
230 if (!conf_target.isNull()) {
231 cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks());
232 }
233}
234
235static RPCHelpMan getnewaddress()
236{
237 return RPCHelpMan{"getnewaddress",
238 "\nReturns a new Bitcoin address for receiving payments.\n"
239 "If 'label' is specified, it is added to the address book \n"
240 "so payments received with the address will be associated with 'label'.\n",
241 {
242 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
243 {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
244 },
245 RPCResult{
246 RPCResult::Type::STR, "address", "The new bitcoin address"
247 },
248 RPCExamples{
249 HelpExampleCli("getnewaddress", "")
250 + HelpExampleRpc("getnewaddress", "")
251 },
252 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
253{
254 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
255 if (!pwallet) return NullUniValue;
256
257 LOCK(pwallet->cs_wallet);
258
259 if (!pwallet->CanGetAddresses()) {
260 throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
261 }
262
263 // Parse the label first so we don't generate a key if there's an error
264 std::string label;
265 if (!request.params[0].isNull())
266 label = LabelFromValue(request.params[0]);
267
268 OutputType output_type = pwallet->m_default_address_type;
269 if (!request.params[1].isNull()) {
270 if (!ParseOutputType(request.params[1].get_str(), output_type)) {
271 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
272 }
273 if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
274 throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
275 }
276 }
277
278 CTxDestination dest;
279 std::string error;
280 if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
281 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
282 }
283
284 return EncodeDestination(dest);
285},
286 };
287}
288
289static RPCHelpMan getrawchangeaddress()
290{
291 return RPCHelpMan{"getrawchangeaddress",
292 "\nReturns a new Bitcoin address, for receiving change.\n"
293 "This is for use with raw transactions, NOT normal use.\n",
294 {
295 {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
296 },
297 RPCResult{
298 RPCResult::Type::STR, "address", "The address"
299 },
300 RPCExamples{
301 HelpExampleCli("getrawchangeaddress", "")
302 + HelpExampleRpc("getrawchangeaddress", "")
303 },
304 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
305{
306 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
307 if (!pwallet) return NullUniValue;
308
309 LOCK(pwallet->cs_wallet);
310
311 if (!pwallet->CanGetAddresses(true)) {
312 throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
313 }
314
315 OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
316 if (!request.params[0].isNull()) {
317 if (!ParseOutputType(request.params[0].get_str(), output_type)) {
318 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
319 }
320 if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
321 throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
322 }
323 }
324
325 CTxDestination dest;
326 std::string error;
327 if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
328 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
329 }
330 return EncodeDestination(dest);
331},
332 };
333}
334
335
336static RPCHelpMan setlabel()
337{
338 return RPCHelpMan{"setlabel",
339 "\nSets the label associated with the given address.\n",
340 {
341 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
342 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
343 },
344 RPCResult{RPCResult::Type::NONE, "", ""},
345 RPCExamples{
346 HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
347 + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
348 },
349 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
350{
351 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
352 if (!pwallet) return NullUniValue;
353
354 LOCK(pwallet->cs_wallet);
355
356 CTxDestination dest = DecodeDestination(request.params[0].get_str());
357 if (!IsValidDestination(dest)) {
358 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
359 }
360
361 std::string label = LabelFromValue(request.params[1]);
362
363 if (pwallet->IsMine(dest)) {
364 pwallet->SetAddressBook(dest, label, "receive");
365 } else {
366 pwallet->SetAddressBook(dest, label, "send");
367 }
368
369 return NullUniValue;
370},
371 };
372}
373
374void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
375 std::set<CTxDestination> destinations;
376 int i = 0;
377 for (const std::string& address: address_amounts.getKeys()) {
378 CTxDestination dest = DecodeDestination(address);
379 if (!IsValidDestination(dest)) {
380 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + address);
381 }
382
383 if (destinations.count(dest)) {
384 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
385 }
386 destinations.insert(dest);
387
388 CScript script_pub_key = GetScriptForDestination(dest);
389 CAmount amount = AmountFromValue(address_amounts[i++]);
390
391 bool subtract_fee = false;
392 for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
393 const UniValue& addr = subtract_fee_outputs[idx];
394 if (addr.get_str() == address) {
395 subtract_fee = true;
396 }
397 }
398
399 CRecipient recipient = {script_pub_key, amount, subtract_fee};
400 recipients.push_back(recipient);
401 }
402}
403
404UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
405{
406 EnsureWalletIsUnlocked(wallet);
407
408 // This function is only used by sendtoaddress and sendmany.
409 // This should always try to sign, if we don't have private keys, don't try to do anything here.
410 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
411 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
412 }
413
414 // Shuffle recipient list
415 std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
416
417 // Send
418 CAmount nFeeRequired = 0;
419 int nChangePosRet = -1;
420 bilingual_str error;
421 CTransactionRef tx;
422 FeeCalculation fee_calc_out;
423 const bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
424 if (!fCreated) {
425 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
426 }
427 wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
428 if (verbose) {
429 UniValue entry(UniValue::VOBJ);
430 entry.pushKV("txid", tx->GetHash().GetHex());
431 entry.pushKV("fee_reason", StringForFeeReason(fee_calc_out.reason));
432 return entry;
433 }
434 return tx->GetHash().GetHex();
435}
436
437static RPCHelpMan sendtoaddress()
438{
439 return RPCHelpMan{"sendtoaddress",
440 "\nSend an amount to a given address." +
441 HELP_REQUIRING_PASSPHRASE,
442 {
443 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to send to."},
444 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
445 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
446 "This is not part of the transaction, just kept in your wallet."},
447 {"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
448 "to which you're sending the transaction. This is not part of the \n"
449 "transaction, just kept in your wallet."},
450 {"subtractfeefromamount", RPCArg::Type::BOOL, RPCArg::Default{false}, "The fee will be deducted from the amount being sent.\n"
451 "The recipient will receive less bitcoins than you enter in the amount field."},
452 {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
453 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
454 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
455 " \"" + FeeModes("\"\n\"") + "\""},
456 {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
457 "dirty if they have previously been used in a transaction. If true, this also activates avoidpartialspends, grouping outputs by their addresses."},
458 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
459 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
460 },
461 {
462 RPCResult{"if verbose is not set or set to false",
463 RPCResult::Type::STR_HEX, "txid", "The transaction id."
464 },
465 RPCResult{"if verbose is set to true",
466 RPCResult::Type::OBJ, "", "",
467 {
468 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
469 {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
470 },
471 },
472 },
473 RPCExamples{
474 "\nSend 0.1 BTC\n"
475 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") +
476 "\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n"
477 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false true 6 economical") +
478 "\nSend 0.1 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n"
479 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true null \"unset\" null 1.1") +
480 "\nSend 0.2 BTC with a confirmation target of 6 blocks in economical fee estimate mode using named arguments\n"
481 + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") +
482 "\nSend 0.5 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
483 + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25")
484 + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25 subtractfeefromamount=false replaceable=true avoid_reuse=true comment=\"2 pizzas\" comment_to=\"jeremy\" verbose=true")
485 },
486 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
487{
488 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
489 if (!pwallet) return NullUniValue;
490
491 // Make sure the results are valid at least up to the most recent block
492 // the user could have gotten from another RPC command prior to now
493 pwallet->BlockUntilSyncedToCurrentChain();
494
495 LOCK(pwallet->cs_wallet);
496
497 // Wallet comments
498 mapValue_t mapValue;
499 if (!request.params[2].isNull() && !request.params[2].get_str().empty())
500 mapValue["comment"] = request.params[2].get_str();
501 if (!request.params[3].isNull() && !request.params[3].get_str().empty())
502 mapValue["to"] = request.params[3].get_str();
503
504 bool fSubtractFeeFromAmount = false;
505 if (!request.params[4].isNull()) {
506 fSubtractFeeFromAmount = request.params[4].get_bool();
507 }
508
509 CCoinControl coin_control;
510 if (!request.params[5].isNull()) {
511 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
512 }
513
514 coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[8]);
515 // We also enable partial spend avoidance if reuse avoidance is set.
516 coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
517
518 SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false);
519
520 EnsureWalletIsUnlocked(*pwallet);
521
522 UniValue address_amounts(UniValue::VOBJ);
523 const std::string address = request.params[0].get_str();
524 address_amounts.pushKV(address, request.params[1]);
525 UniValue subtractFeeFromAmount(UniValue::VARR);
526 if (fSubtractFeeFromAmount) {
527 subtractFeeFromAmount.push_back(address);
528 }
529
530 std::vector<CRecipient> recipients;
531 ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
532 const bool verbose{request.params[10].isNull() ? false : request.params[10].get_bool()};
533
534 return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
535},
536 };
537}
538
539static RPCHelpMan walletestimatefee()
540{
541 return RPCHelpMan{"walletestimatefee",
542 "\nEstimates the fee per kilobyte needed for a transaction to begin from this wallet\n"
543 "confirmation within conf_target blocks if possible and return the number of blocks\n"
544 "for which the estimate is valid.\n",
545 {
546 {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks (1 - 1008)"},
547 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"conservative"}, "The fee estimate mode.\n"
548 " Whether to return a more conservative estimate which also satisfies\n"
549 " a longer history. A conservative estimate potentially returns a\n"
550 " higher feerate and is more likely to be sufficient for the desired\n"
551 " target, but is not as responsive to short term drops in the\n"
552 " prevailing fee market. Must be one of (case insensitive):\n"
553 "\"" + FeeModes("\"\n\"") + "\""},
554 },
555 RPCResult{
556 RPCResult::Type::OBJ, "", "",
557 {
558 {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},
559 {RPCResult::Type::STR, "reason", /* optional */ true, "mention the reason for the estimated fee"},
560 {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)",
561 {
562 {RPCResult::Type::STR, "", "error"},
563 }},
564 {RPCResult::Type::NUM, "blocks", "block number where estimate was found (only when reason is estimatesmartfee)\n"
565 "The request target will be clamped between 2 and the highest target\n"
566 "fee estimation is able to return based on how long it has been running.\n"
567 "An error is returned if not enough transactions and blocks\n"
568 "have been observed to make an estimate for any number of blocks."},
569 }},
570 RPCExamples{
571 HelpExampleCli("walletestimatefee", "6")
572 },
573 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
574
575{
576 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
577 if (!pwallet) return NullUniValue;
578
579 // Make sure the results are valid at least up to the most recent block
580 // the user could have gotten from another RPC command prior to now
581 pwallet->BlockUntilSyncedToCurrentChain();
582
583 LOCK(pwallet->cs_wallet);
584
585 // Wallet comments
586
587 CCoinControl coin_control;
588
589 //dummy variable temp representing NULL fee_rate
590 UniValue temp(UniValue::VNULL);
591
592 SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[0], /* estimate_mode */ request.params[1], /* fee_rate */temp , /* override_min_fee */ false);
593
594 UniValue result(UniValue::VOBJ);
595 UniValue errors(UniValue::VARR);
596 UniValue reas(UniValue::VSTR);
597 FeeCalculation feeCalc;
598
599 CFeeRate effective_feerate = GetMinimumFeeRate(*pwallet, coin_control, &feeCalc);
600
601 if (effective_feerate != CFeeRate(0)) {
602 result.pushKV("feerate", ValueFromAmount(effective_feerate.GetFeePerK()));
603 //when reason is estimatesmartfee return block number where estimate was found
604 if(feeCalc.reason!=FeeReason::MEMPOOL_MIN&&
605 feeCalc.reason!=FeeReason::REQUIRED&&
606 feeCalc.reason!=FeeReason::FALLBACK&&
607 feeCalc.reason!=FeeReason::PAYTXFEE)
608 {
609 result.pushKV("reason", "estimatesmartfee" );
610 result.pushKV("blocks", feeCalc.returnedTarget);
611 }
612 else
613 {
614 result.pushKV("reason",StringForFeeReason(feeCalc.reason) );
615 }
616 } else {
617 errors.push_back("Insufficient data or no feerate found");
618 result.pushKV("errors", errors);
619 }
620
621 return result;
622},
623 };
624}
625
626static RPCHelpMan listaddressgroupings()
627{
628 return RPCHelpMan{"listaddressgroupings",
629 "\nLists groups of addresses which have had their common ownership\n"
630 "made public by common use as inputs or as the resulting change\n"
631 "in past transactions\n",
632 {},
633 RPCResult{
634 RPCResult::Type::ARR, "", "",
635 {
636 {RPCResult::Type::ARR, "", "",
637 {
638 {RPCResult::Type::ARR_FIXED, "", "",
639 {
640 {RPCResult::Type::STR, "address", "The bitcoin address"},
641 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
642 {RPCResult::Type::STR, "label", /* optional */ true, "The label"},
643 }},
644 }},
645 }
646 },
647 RPCExamples{
648 HelpExampleCli("listaddressgroupings", "")
649 + HelpExampleRpc("listaddressgroupings", "")
650 },
651 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
652{
653 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
654 if (!pwallet) return NullUniValue;
655
656 // Make sure the results are valid at least up to the most recent block
657 // the user could have gotten from another RPC command prior to now
658 pwallet->BlockUntilSyncedToCurrentChain();
659
660 LOCK(pwallet->cs_wallet);
661
662 UniValue jsonGroupings(UniValue::VARR);
663 std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
664 for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
665 UniValue jsonGrouping(UniValue::VARR);
666 for (const CTxDestination& address : grouping)
667 {
668 UniValue addressInfo(UniValue::VARR);
669 addressInfo.push_back(EncodeDestination(address));
670 addressInfo.push_back(ValueFromAmount(balances[address]));
671 {
672 const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
673 if (address_book_entry) {
674 addressInfo.push_back(address_book_entry->GetLabel());
675 }
676 }
677 jsonGrouping.push_back(addressInfo);
678 }
679 jsonGroupings.push_back(jsonGrouping);
680 }
681 return jsonGroupings;
682},
683 };
684}
685
686static RPCHelpMan signmessage()
687{
688 return RPCHelpMan{"signmessage",
689 "\nSign a message with the private key of an address" +
690 HELP_REQUIRING_PASSPHRASE,
691 {
692 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the private key."},
693 {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
694 },
695 RPCResult{
696 RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
697 },
698 RPCExamples{
699 "\nUnlock the wallet for 30 seconds\n"
700 + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
701 "\nCreate the signature\n"
702 + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
703 "\nVerify the signature\n"
704 + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
705 "\nAs a JSON-RPC call\n"
706 + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
707 },
708 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
709{
710 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
711 if (!pwallet) return NullUniValue;
712
713 LOCK(pwallet->cs_wallet);
714
715 EnsureWalletIsUnlocked(*pwallet);
716
717 std::string strAddress = request.params[0].get_str();
718 std::string strMessage = request.params[1].get_str();
719
720 CTxDestination dest = DecodeDestination(strAddress);
721 if (!IsValidDestination(dest)) {
722 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
723 }
724
725 const PKHash* pkhash = std::get_if<PKHash>(&dest);
726 if (!pkhash) {
727 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
728 }
729
730 std::string signature;
731 SigningResult err = pwallet->SignMessage(strMessage, *pkhash, signature);
732 if (err == SigningResult::SIGNING_FAILED) {
733 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, SigningResultString(err));
734 } else if (err != SigningResult::OK){
735 throw JSONRPCError(RPC_WALLET_ERROR, SigningResultString(err));
736 }
737
738 return signature;
739},
740 };
741}
742
743static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
744{
745 std::set<CTxDestination> address_set;
746
747 if (by_label) {
748 // Get the set of addresses assigned to label
749 std::string label = LabelFromValue(params[0]);
750 address_set = wallet.GetLabelAddresses(label);
751 } else {
752 // Get the address
753 CTxDestination dest = DecodeDestination(params[0].get_str());
754 if (!IsValidDestination(dest)) {
755 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
756 }
757 CScript script_pub_key = GetScriptForDestination(dest);
758 if (!wallet.IsMine(script_pub_key)) {
759 throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
760 }
761 address_set.insert(dest);
762 }
763
764 // Minimum confirmations
765 int min_depth = 1;
766 if (!params[1].isNull())
767 min_depth = params[1].get_int();
768
769 // Tally
770 CAmount amount = 0;
771 for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
772 const CWalletTx& wtx = wtx_pair.second;
773 if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wtx.GetDepthInMainChain() < min_depth) {
774 continue;
775 }
776
777 for (const CTxOut& txout : wtx.tx->vout) {
778 CTxDestination address;
779 if (ExtractDestination(txout.scriptPubKey, address) && wallet.IsMine(address) && address_set.count(address)) {
780 amount += txout.nValue;
781 }
782 }
783 }
784
785 return amount;
786}
787
788
789static RPCHelpMan getreceivedbyaddress()
790{
791 return RPCHelpMan{"getreceivedbyaddress",
792 "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
793 {
794 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
795 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
796 },
797 RPCResult{
798 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
799 },
800 RPCExamples{
801 "\nThe amount from transactions with at least 1 confirmation\n"
802 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
803 "\nThe amount including unconfirmed transactions, zero confirmations\n"
804 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
805 "\nThe amount with at least 6 confirmations\n"
806 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
807 "\nAs a JSON-RPC call\n"
808 + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
809 },
810 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
811{
812 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
813 if (!pwallet) return NullUniValue;
814
815 // Make sure the results are valid at least up to the most recent block
816 // the user could have gotten from another RPC command prior to now
817 pwallet->BlockUntilSyncedToCurrentChain();
818
819 LOCK(pwallet->cs_wallet);
820
821 return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
822},
823 };
824}
825
826
827static RPCHelpMan getreceivedbylabel()
828{
829 return RPCHelpMan{"getreceivedbylabel",
830 "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
831 {
832 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
833 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
834 },
835 RPCResult{
836 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
837 },
838 RPCExamples{
839 "\nAmount received by the default label with at least 1 confirmation\n"
840 + HelpExampleCli("getreceivedbylabel", "\"\"") +
841 "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
842 + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
843 "\nThe amount with at least 6 confirmations\n"
844 + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
845 "\nAs a JSON-RPC call\n"
846 + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
847 },
848 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
849{
850 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
851 if (!pwallet) return NullUniValue;
852
853 // Make sure the results are valid at least up to the most recent block
854 // the user could have gotten from another RPC command prior to now
855 pwallet->BlockUntilSyncedToCurrentChain();
856
857 LOCK(pwallet->cs_wallet);
858
859 return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
860},
861 };
862}
863
864
865static RPCHelpMan getbalance()
866{
867 return RPCHelpMan{"getbalance",
868 "\nReturns the total available balance.\n"
869 "The available balance is what the wallet considers currently spendable, and is\n"
870 "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
871 {
872 {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
873 {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
874 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
875 {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
876 },
877 RPCResult{
878 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
879 },
880 RPCExamples{
881 "\nThe total amount in the wallet with 0 or more confirmations\n"
882 + HelpExampleCli("getbalance", "") +
883 "\nThe total amount in the wallet with at least 6 confirmations\n"
884 + HelpExampleCli("getbalance", "\"*\" 6") +
885 "\nAs a JSON-RPC call\n"
886 + HelpExampleRpc("getbalance", "\"*\", 6")
887 },
888 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
889{
890 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
891 if (!pwallet) return NullUniValue;
892
893 // Make sure the results are valid at least up to the most recent block
894 // the user could have gotten from another RPC command prior to now
895 pwallet->BlockUntilSyncedToCurrentChain();
896
897 LOCK(pwallet->cs_wallet);
898
899 const UniValue& dummy_value = request.params[0];
900 if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
901 throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
902 }
903
904 int min_depth = 0;
905 if (!request.params[1].isNull()) {
906 min_depth = request.params[1].get_int();
907 }
908
909 bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
910
911 bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
912
913 const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
914
915 return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
916},
917 };
918}
919
920static RPCHelpMan getunconfirmedbalance()
921{
922 return RPCHelpMan{"getunconfirmedbalance",
923 "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
924 {},
925 RPCResult{RPCResult::Type::NUM, "", "The balance"},
926 RPCExamples{""},
927 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
928{
929 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
930 if (!pwallet) return NullUniValue;
931
932 // Make sure the results are valid at least up to the most recent block
933 // the user could have gotten from another RPC command prior to now
934 pwallet->BlockUntilSyncedToCurrentChain();
935
936 LOCK(pwallet->cs_wallet);
937
938 return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
939},
940 };
941}
942
943
944static RPCHelpMan sendmany()
945{
946 return RPCHelpMan{"sendmany",
947 "\nSend multiple times. Amounts are double-precision floating point numbers." +
948 HELP_REQUIRING_PASSPHRASE,
949 {
950 {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
951 {"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
952 {
953 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
954 },
955 },
956 {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
957 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
958 {"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
959 "The fee will be equally deducted from the amount of each selected address.\n"
960 "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
961 "If no addresses are specified here, the sender pays the fee.",
962 {
963 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
964 },
965 },
966 {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
967 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
968 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
969 " \"" + FeeModes("\"\n\"") + "\""},
970 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
971 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra infomration about the transaction."},
972 },
973 {
974 RPCResult{"if verbose is not set or set to false",
975 RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
976 "the number of addresses."
977 },
978 RPCResult{"if verbose is set to true",
979 RPCResult::Type::OBJ, "", "",
980 {
981 {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
982 "the number of addresses."},
983 {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
984 },
985 },
986 },
987 RPCExamples{
988 "\nSend two amounts to two different addresses:\n"
989 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\"") +
990 "\nSend two amounts to two different addresses setting the confirmation and comment:\n"
991 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 6 \"testing\"") +
992 "\nSend two amounts to two different addresses, subtract fee from amount:\n"
993 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 1 \"\" \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
994 "\nAs a JSON-RPC call\n"
995 + HelpExampleRpc("sendmany", "\"\", {\"" + EXAMPLE_ADDRESS[0] + "\":0.01,\"" + EXAMPLE_ADDRESS[1] + "\":0.02}, 6, \"testing\"")
996 },
997 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
998{
999 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1000 if (!pwallet) return NullUniValue;
1001
1002 // Make sure the results are valid at least up to the most recent block
1003 // the user could have gotten from another RPC command prior to now
1004 pwallet->BlockUntilSyncedToCurrentChain();
1005
1006 LOCK(pwallet->cs_wallet);
1007
1008 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
1009 throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
1010 }
1011 UniValue sendTo = request.params[1].get_obj();
1012
1013 mapValue_t mapValue;
1014 if (!request.params[3].isNull() && !request.params[3].get_str().empty())
1015 mapValue["comment"] = request.params[3].get_str();
1016
1017 UniValue subtractFeeFromAmount(UniValue::VARR);
1018 if (!request.params[4].isNull())
1019 subtractFeeFromAmount = request.params[4].get_array();
1020
1021 CCoinControl coin_control;
1022 if (!request.params[5].isNull()) {
1023 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
1024 }
1025
1026 SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[8], /* override_min_fee */ false);
1027
1028 std::vector<CRecipient> recipients;
1029 ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
1030 const bool verbose{request.params[9].isNull() ? false : request.params[9].get_bool()};
1031
1032 return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
1033},
1034 };
1035}
1036
1037
1038static RPCHelpMan addmultisigaddress()
1039{
1040 return RPCHelpMan{"addmultisigaddress",
1041 "\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
1042 "Each key is a Bitcoin address or hex-encoded public key.\n"
1043 "This functionality is only intended for use with non-watchonly addresses.\n"
1044 "See `importaddress` for watchonly p2sh address support.\n"
1045 "If 'label' is specified, assign address to that label.\n",
1046 {
1047 {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
1048 {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The bitcoin addresses or hex-encoded public keys",
1049 {
1050 {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address or hex-encoded public key"},
1051 },
1052 },
1053 {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
1054 {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
1055 },
1056 RPCResult{
1057 RPCResult::Type::OBJ, "", "",
1058 {
1059 {RPCResult::Type::STR, "address", "The value of the new multisig address"},
1060 {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
1061 {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
1062 }
1063 },
1064 RPCExamples{
1065 "\nAdd a multisig address from 2 addresses\n"
1066 + HelpExampleCli("addmultisigaddress", "2 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
1067 "\nAs a JSON-RPC call\n"
1068 + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
1069 },
1070 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1071{
1072 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1073 if (!pwallet) return NullUniValue;
1074
1075 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
1076
1077 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
1078
1079 std::string label;
1080 if (!request.params[2].isNull())
1081 label = LabelFromValue(request.params[2]);
1082
1083 int required = request.params[0].get_int();
1084
1085 // Get the public keys
1086 const UniValue& keys_or_addrs = request.params[1].get_array();
1087 std::vector<CPubKey> pubkeys;
1088 for (unsigned int i = 0; i < keys_or_addrs.size(); ++i) {
1089 if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
1090 pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
1091 } else {
1092 pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
1093 }
1094 }
1095
1096 OutputType output_type = pwallet->m_default_address_type;
1097 if (!request.params[3].isNull()) {
1098 if (!ParseOutputType(request.params[3].get_str(), output_type)) {
1099 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
1100 }
1101 if (output_type == OutputType::BECH32M) {
1102 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m multisig addresses cannot be created with legacy wallets");
1103 }
1104 }
1105
1106 // Construct using pay-to-script-hash:
1107 CScript inner;
1108 CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner);
1109 pwallet->SetAddressBook(dest, label, "send");
1110
1111 // Make the descriptor
1112 std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
1113
1114 UniValue result(UniValue::VOBJ);
1115 result.pushKV("address", EncodeDestination(dest));
1116 result.pushKV("redeemScript", HexStr(inner));
1117 result.pushKV("descriptor", descriptor->ToString());
1118 return result;
1119},
1120 };
1121}
1122
1123struct tallyitem
1124{
1125 CAmount nAmount{0};
1126 int nConf{std::numeric_limits<int>::max()};
1127 std::vector<uint256> txids;
1128 bool fIsWatchonly{false};
1129 tallyitem()
1130 {
1131 }
1132};
1133
1134static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1135{
1136 // Minimum confirmations
1137 int nMinDepth = 1;
1138 if (!params[0].isNull())
1139 nMinDepth = params[0].get_int();
1140
1141 // Whether to include empty labels
1142 bool fIncludeEmpty = false;
1143 if (!params[1].isNull())
1144 fIncludeEmpty = params[1].get_bool();
1145
1146 isminefilter filter = ISMINE_SPENDABLE;
1147
1148 if (ParseIncludeWatchonly(params[2], wallet)) {
1149 filter |= ISMINE_WATCH_ONLY;
1150 }
1151
1152 bool has_filtered_address = false;
1153 CTxDestination filtered_address = CNoDestination();
1154 if (!by_label && params.size() > 3) {
1155 if (!IsValidDestinationString(params[3].get_str())) {
1156 throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
1157 }
1158 filtered_address = DecodeDestination(params[3].get_str());
1159 has_filtered_address = true;
1160 }
1161
1162 // Tally
1163 std::map<CTxDestination, tallyitem> mapTally;
1164 for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
1165 const CWalletTx& wtx = pairWtx.second;
1166
1167 if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx)) {
1168 continue;
1169 }
1170
1171 int nDepth = wtx.GetDepthInMainChain();
1172 if (nDepth < nMinDepth)
1173 continue;
1174
1175 for (const CTxOut& txout : wtx.tx->vout)
1176 {
1177 CTxDestination address;
1178 if (!ExtractDestination(txout.scriptPubKey, address))
1179 continue;
1180
1181 if (has_filtered_address && !(filtered_address == address)) {
1182 continue;
1183 }
1184
1185 isminefilter mine = wallet.IsMine(address);
1186 if(!(mine & filter))
1187 continue;
1188
1189 tallyitem& item = mapTally[address];
1190 item.nAmount += txout.nValue;
1191 item.nConf = std::min(item.nConf, nDepth);
1192 item.txids.push_back(wtx.GetHash());
1193 if (mine & ISMINE_WATCH_ONLY)
1194 item.fIsWatchonly = true;
1195 }
1196 }
1197
1198 // Reply
1199 UniValue ret(UniValue::VARR);
1200 std::map<std::string, tallyitem> label_tally;
1201
1202 // Create m_address_book iterator
1203 // If we aren't filtering, go from begin() to end()
1204 auto start = wallet.m_address_book.begin();
1205 auto end = wallet.m_address_book.end();
1206 // If we are filtering, find() the applicable entry
1207 if (has_filtered_address) {
1208 start = wallet.m_address_book.find(filtered_address);
1209 if (start != end) {
1210 end = std::next(start);
1211 }
1212 }
1213
1214 for (auto item_it = start; item_it != end; ++item_it)
1215 {
1216 if (item_it->second.IsChange()) continue;
1217 const CTxDestination& address = item_it->first;
1218 const std::string& label = item_it->second.GetLabel();
1219 auto it = mapTally.find(address);
1220 if (it == mapTally.end() && !fIncludeEmpty)
1221 continue;
1222
1223 CAmount nAmount = 0;
1224 int nConf = std::numeric_limits<int>::max();
1225 bool fIsWatchonly = false;
1226 if (it != mapTally.end())
1227 {
1228 nAmount = (*it).second.nAmount;
1229 nConf = (*it).second.nConf;
1230 fIsWatchonly = (*it).second.fIsWatchonly;
1231 }
1232
1233 if (by_label)
1234 {
1235 tallyitem& _item = label_tally[label];
1236 _item.nAmount += nAmount;
1237 _item.nConf = std::min(_item.nConf, nConf);
1238 _item.fIsWatchonly = fIsWatchonly;
1239 }
1240 else
1241 {
1242 UniValue obj(UniValue::VOBJ);
1243 if(fIsWatchonly)
1244 obj.pushKV("involvesWatchonly", true);
1245 obj.pushKV("address", EncodeDestination(address));
1246 obj.pushKV("amount", ValueFromAmount(nAmount));
1247 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
1248 obj.pushKV("label", label);
1249 UniValue transactions(UniValue::VARR);
1250 if (it != mapTally.end())
1251 {
1252 for (const uint256& _item : (*it).second.txids)
1253 {
1254 transactions.push_back(_item.GetHex());
1255 }
1256 }
1257 obj.pushKV("txids", transactions);
1258 ret.push_back(obj);
1259 }
1260 }
1261
1262 if (by_label)
1263 {
1264 for (const auto& entry : label_tally)
1265 {
1266 CAmount nAmount = entry.second.nAmount;
1267 int nConf = entry.second.nConf;
1268 UniValue obj(UniValue::VOBJ);
1269 if (entry.second.fIsWatchonly)
1270 obj.pushKV("involvesWatchonly", true);
1271 obj.pushKV("amount", ValueFromAmount(nAmount));
1272 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
1273 obj.pushKV("label", entry.first);
1274 ret.push_back(obj);
1275 }
1276 }
1277
1278 return ret;
1279}
1280
1281static RPCHelpMan listreceivedbyaddress()
1282{
1283 return RPCHelpMan{"listreceivedbyaddress",
1284 "\nList balances by receiving address.\n",
1285 {
1286 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
1287 {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},
1288 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
1289 {"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
1290 },
1291 RPCResult{
1292 RPCResult::Type::ARR, "", "",
1293 {
1294 {RPCResult::Type::OBJ, "", "",
1295 {
1296 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
1297 {RPCResult::Type::STR, "address", "The receiving address"},
1298 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
1299 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
1300 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
1301 {RPCResult::Type::ARR, "txids", "",
1302 {
1303 {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},
1304 }},
1305 }},
1306 }
1307 },
1308 RPCExamples{
1309 HelpExampleCli("listreceivedbyaddress", "")
1310 + HelpExampleCli("listreceivedbyaddress", "6 true")
1311 + HelpExampleRpc("listreceivedbyaddress", "6, true, true")
1312 + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\"")
1313 },
1314 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1315{
1316 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1317 if (!pwallet) return NullUniValue;
1318
1319 // Make sure the results are valid at least up to the most recent block
1320 // the user could have gotten from another RPC command prior to now
1321 pwallet->BlockUntilSyncedToCurrentChain();
1322
1323 LOCK(pwallet->cs_wallet);
1324
1325 return ListReceived(*pwallet, request.params, false);
1326},
1327 };
1328}
1329
1330static RPCHelpMan listreceivedbylabel()
1331{
1332 return RPCHelpMan{"listreceivedbylabel",
1333 "\nList received transactions by label.\n",
1334 {
1335 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
1336 {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include labels that haven't received any payments."},
1337 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
1338 },
1339 RPCResult{
1340 RPCResult::Type::ARR, "", "",
1341 {
1342 {RPCResult::Type::OBJ, "", "",
1343 {
1344 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
1345 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
1346 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
1347 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
1348 }},
1349 }
1350 },
1351 RPCExamples{
1352 HelpExampleCli("listreceivedbylabel", "")
1353 + HelpExampleCli("listreceivedbylabel", "6 true")
1354 + HelpExampleRpc("listreceivedbylabel", "6, true, true")
1355 },
1356 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1357{
1358 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1359 if (!pwallet) return NullUniValue;
1360
1361 // Make sure the results are valid at least up to the most recent block
1362 // the user could have gotten from another RPC command prior to now
1363 pwallet->BlockUntilSyncedToCurrentChain();
1364
1365 LOCK(pwallet->cs_wallet);
1366
1367 return ListReceived(*pwallet, request.params, true);
1368},
1369 };
1370}
1371
1372static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
1373{
1374 if (IsValidDestination(dest)) {
1375 entry.pushKV("address", EncodeDestination(dest));
1376 }
1377}
1378
1379/**
1380 * List transactions based on the given criteria.
1381 *
1382 * @param wallet The wallet.
1383 * @param wtx The wallet transaction.
1384 * @param nMinDepth The minimum confirmation depth.
1385 * @param fLong Whether to include the JSON version of the transaction.
1386 * @param ret The UniValue into which the result is stored.
1387 * @param filter_ismine The "is mine" filter flags.
1388 * @param filter_label Optional label string to filter incoming transactions.
1389 */
1390static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1391{
1392 CAmount nFee;
1393 std::list<COutputEntry> listReceived;
1394 std::list<COutputEntry> listSent;
1395
1396 wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
1397
1398 bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
1399
1400 // Sent
1401 if (!filter_label)
1402 {
1403 for (const COutputEntry& s : listSent)
1404 {
1405 UniValue entry(UniValue::VOBJ);
1406 if (involvesWatchonly || (wallet.IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
1407 entry.pushKV("involvesWatchonly", true);
1408 }
1409 MaybePushAddress(entry, s.destination);
1410 entry.pushKV("category", "send");
1411 entry.pushKV("amount", ValueFromAmount(-s.amount));
1412 const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
1413 if (address_book_entry) {
1414 entry.pushKV("label", address_book_entry->GetLabel());
1415 }
1416 entry.pushKV("vout", s.vout);
1417 entry.pushKV("fee", ValueFromAmount(-nFee));
1418 if (fLong)
1419 WalletTxToJSON(wallet.chain(), wtx, entry);
1420 entry.pushKV("abandoned", wtx.isAbandoned());
1421 ret.push_back(entry);
1422 }
1423 }
1424
1425 // Received
1426 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
1427 for (const COutputEntry& r : listReceived)
1428 {
1429 std::string label;
1430 const auto* address_book_entry = wallet.FindAddressBookEntry(r.destination);
1431 if (address_book_entry) {
1432 label = address_book_entry->GetLabel();
1433 }
1434 if (filter_label && label != *filter_label) {
1435 continue;
1436 }
1437 UniValue entry(UniValue::VOBJ);
1438 if (involvesWatchonly || (wallet.IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
1439 entry.pushKV("involvesWatchonly", true);
1440 }
1441 MaybePushAddress(entry, r.destination);
1442 if (wtx.IsCoinBase())
1443 {
1444 if (wtx.GetDepthInMainChain() < 1)
1445 entry.pushKV("category", "orphan");
1446 else if (wtx.IsImmatureCoinBase())
1447 entry.pushKV("category", "immature");
1448 else
1449 entry.pushKV("category", "generate");
1450 }
1451 else
1452 {
1453 entry.pushKV("category", "receive");
1454 }
1455 entry.pushKV("amount", ValueFromAmount(r.amount));
1456 if (address_book_entry) {
1457 entry.pushKV("label", label);
1458 }
1459 entry.pushKV("vout", r.vout);
1460 if (fLong)
1461 WalletTxToJSON(wallet.chain(), wtx, entry);
1462 ret.push_back(entry);
1463 }
1464 }
1465}
1466
1467static const std::vector<RPCResult> TransactionDescriptionString()
1468{
1469 return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
1470 "transaction conflicted that many blocks ago."},
1471 {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
1472 {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
1473 {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
1474 {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
1475 {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
1476 {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
1477 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
1478 {RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
1479 {
1480 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
1481 }},
1482 {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
1483 {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
1484 {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
1485 {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
1486 "may be unknown for unconfirmed transactions not in the mempool"}};
1487}
1488
1489static RPCHelpMan listtransactions()
1490{
1491 return RPCHelpMan{"listtransactions",
1492 "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
1493 "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
1494 {
1495 {"label|dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n"
1496 "with the specified label, or \"*\" to disable filtering and return all transactions."},
1497 {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
1498 {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
1499 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
1500 },
1501 RPCResult{
1502 RPCResult::Type::ARR, "", "",
1503 {
1504 {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
1505 {
1506 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
1507 {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
1508 {RPCResult::Type::STR, "category", "The transaction category.\n"
1509 "\"send\" Transactions sent.\n"
1510 "\"receive\" Non-coinbase transactions received.\n"
1511 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
1512 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
1513 "\"orphan\" Orphaned coinbase transactions received."},
1514 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
1515 "for all other categories"},
1516 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
1517 {RPCResult::Type::NUM, "vout", "the vout value"},
1518 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
1519 "'send' category of transactions."},
1520 },
1521 TransactionDescriptionString()),
1522 {
1523 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
1524 "'send' category of transactions."},
1525 })},
1526 }
1527 },
1528 RPCExamples{
1529 "\nList the most recent 10 transactions in the systems\n"
1530 + HelpExampleCli("listtransactions", "") +
1531 "\nList transactions 100 to 120\n"
1532 + HelpExampleCli("listtransactions", "\"*\" 20 100") +
1533 "\nAs a JSON-RPC call\n"
1534 + HelpExampleRpc("listtransactions", "\"*\", 20, 100")
1535 },
1536 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1537{
1538 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1539 if (!pwallet) return NullUniValue;
1540
1541 // Make sure the results are valid at least up to the most recent block
1542 // the user could have gotten from another RPC command prior to now
1543 pwallet->BlockUntilSyncedToCurrentChain();
1544
1545 const std::string* filter_label = nullptr;
1546 if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
1547 filter_label = &request.params[0].get_str();
1548 if (filter_label->empty()) {
1549 throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
1550 }
1551 }
1552 int nCount = 10;
1553 if (!request.params[1].isNull())
1554 nCount = request.params[1].get_int();
1555 int nFrom = 0;
1556 if (!request.params[2].isNull())
1557 nFrom = request.params[2].get_int();
1558 isminefilter filter = ISMINE_SPENDABLE;
1559
1560 if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
1561 filter |= ISMINE_WATCH_ONLY;
1562 }
1563
1564 if (nCount < 0)
1565 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1566 if (nFrom < 0)
1567 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1568
1569 UniValue ret(UniValue::VARR);
1570
1571 {
1572 LOCK(pwallet->cs_wallet);
1573
1574 const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
1575
1576 // iterate backwards until we have nCount items to return:
1577 for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1578 {
1579 CWalletTx *const pwtx = (*it).second;
1580 ListTransactions(*pwallet, *pwtx, 0, true, ret, filter, filter_label);
1581 if ((int)ret.size() >= (nCount+nFrom)) break;
1582 }
1583 }
1584
1585 // ret is newest to oldest
1586
1587 if (nFrom > (int)ret.size())
1588 nFrom = ret.size();
1589 if ((nFrom + nCount) > (int)ret.size())
1590 nCount = ret.size() - nFrom;
1591
1592 const std::vector<UniValue>& txs = ret.getValues();
1593 UniValue result{UniValue::VARR};
1594 result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
1595 return result;
1596},
1597 };
1598}
1599
1600static RPCHelpMan listsinceblock()
1601{
1602 return RPCHelpMan{"listsinceblock",
1603 "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
1604 "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
1605 "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
1606 {
1607 {"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
1608 {"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
1609 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
1610 {"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
1611 "(not guaranteed to work on pruned nodes)"},
1612 },
1613 RPCResult{
1614 RPCResult::Type::OBJ, "", "",
1615 {
1616 {RPCResult::Type::ARR, "transactions", "",
1617 {
1618 {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
1619 {
1620 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
1621 {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
1622 {RPCResult::Type::STR, "category", "The transaction category.\n"
1623 "\"send\" Transactions sent.\n"
1624 "\"receive\" Non-coinbase transactions received.\n"
1625 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
1626 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
1627 "\"orphan\" Orphaned coinbase transactions received."},
1628 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
1629 "for all other categories"},
1630 {RPCResult::Type::NUM, "vout", "the vout value"},
1631 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
1632 "'send' category of transactions."},
1633 },
1634 TransactionDescriptionString()),
1635 {
1636 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
1637 "'send' category of transactions."},
1638 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
1639 {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
1640 })},
1641 }},
1642 {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
1643 "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
1644 , {{RPCResult::Type::ELISION, "", ""},}},
1645 {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
1646 }
1647 },
1648 RPCExamples{
1649 HelpExampleCli("listsinceblock", "")
1650 + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
1651 + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
1652 },
1653 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1654{
1655 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1656 if (!pwallet) return NullUniValue;
1657
1658 const CWallet& wallet = *pwallet;
1659 // Make sure the results are valid at least up to the most recent block
1660 // the user could have gotten from another RPC command prior to now
1661 wallet.BlockUntilSyncedToCurrentChain();
1662
1663 LOCK(wallet.cs_wallet);
1664
1665 std::optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
1666 std::optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
1667 int target_confirms = 1;
1668 isminefilter filter = ISMINE_SPENDABLE;
1669
1670 uint256 blockId;
1671 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
1672 blockId = ParseHashV(request.params[0], "blockhash");
1673 height = int{};
1674 altheight = int{};
1675 if (!wallet.chain().findCommonAncestor(blockId, wallet.GetLastBlockHash(), /* ancestor out */ FoundBlock().height(*height), /* blockId out */ FoundBlock().height(*altheight))) {
1676 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1677 }
1678 }
1679
1680 if (!request.params[1].isNull()) {
1681 target_confirms = request.params[1].get_int();
1682
1683 if (target_confirms < 1) {
1684 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1685 }
1686 }
1687
1688 if (ParseIncludeWatchonly(request.params[2], wallet)) {
1689 filter |= ISMINE_WATCH_ONLY;
1690 }
1691
1692 bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
1693
1694 int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
1695
1696 UniValue transactions(UniValue::VARR);
1697
1698 for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
1699 const CWalletTx& tx = pairWtx.second;
1700
1701 if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
1702 ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
1703 }
1704 }
1705
1706 // when a reorg'd block is requested, we also list any relevant transactions
1707 // in the blocks of the chain that was detached
1708 UniValue removed(UniValue::VARR);
1709 while (include_removed && altheight && *altheight > *height) {
1710 CBlock block;
1711 if (!wallet.chain().findBlock(blockId, FoundBlock().data(block)) || block.IsNull()) {
1712 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
1713 }
1714 for (const CTransactionRef& tx : block.vtx) {
1715 auto it = wallet.mapWallet.find(tx->GetHash());
1716 if (it != wallet.mapWallet.end()) {
1717 // We want all transactions regardless of confirmation count to appear here,
1718 // even negative confirmation ones, hence the big negative.
1719 ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
1720 }
1721 }
1722 blockId = block.hashPrevBlock;
1723 --*altheight;
1724 }
1725
1726 uint256 lastblock;
1727 target_confirms = std::min(target_confirms, wallet.GetLastBlockHeight() + 1);
1728 CHECK_NONFATAL(wallet.chain().findAncestorByHeight(wallet.GetLastBlockHash(), wallet.GetLastBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
1729
1730 UniValue ret(UniValue::VOBJ);
1731 ret.pushKV("transactions", transactions);
1732 if (include_removed) ret.pushKV("removed", removed);
1733 ret.pushKV("lastblock", lastblock.GetHex());
1734
1735 return ret;
1736},
1737 };
1738}
1739
1740static RPCHelpMan gettransaction()
1741{
1742 return RPCHelpMan{"gettransaction",
1743 "\nGet detailed information about in-wallet transaction <txid>\n",
1744 {
1745 {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
1746 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"},
1747 "Whether to include watch-only addresses in balance calculation and details[]"},
1748 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
1749 "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
1750 },
1751 RPCResult{
1752 RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
1753 {
1754 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
1755 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
1756 "'send' category of transactions."},
1757 },
1758 TransactionDescriptionString()),
1759 {
1760 {RPCResult::Type::ARR, "details", "",
1761 {
1762 {RPCResult::Type::OBJ, "", "",
1763 {
1764 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
1765 {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."},
1766 {RPCResult::Type::STR, "category", "The transaction category.\n"
1767 "\"send\" Transactions sent.\n"
1768 "\"receive\" Non-coinbase transactions received.\n"
1769 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
1770 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
1771 "\"orphan\" Orphaned coinbase transactions received."},
1772 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
1773 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
1774 {RPCResult::Type::NUM, "vout", "the vout value"},
1775 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
1776 "'send' category of transactions."},
1777 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
1778 "'send' category of transactions."},
1779 }},
1780 }},
1781 {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
1782 {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
1783 {
1784 {RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
1785 }},
1786 })
1787 },
1788 RPCExamples{
1789 HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1790 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
1791 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
1792 + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1793 },
1794 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1795{
1796 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1797 if (!pwallet) return NullUniValue;
1798
1799 // Make sure the results are valid at least up to the most recent block
1800 // the user could have gotten from another RPC command prior to now
1801 pwallet->BlockUntilSyncedToCurrentChain();
1802
1803 LOCK(pwallet->cs_wallet);
1804
1805 uint256 hash(ParseHashV(request.params[0], "txid"));
1806
1807 isminefilter filter = ISMINE_SPENDABLE;
1808
1809 if (ParseIncludeWatchonly(request.params[1], *pwallet)) {
1810 filter |= ISMINE_WATCH_ONLY;
1811 }
1812
1813 bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
1814
1815 UniValue entry(UniValue::VOBJ);
1816 auto it = pwallet->mapWallet.find(hash);
1817 if (it == pwallet->mapWallet.end()) {
1818 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
1819 }
1820 const CWalletTx& wtx = it->second;
1821
1822 CAmount nCredit = wtx.GetCredit(filter);
1823 CAmount nDebit = wtx.GetDebit(filter);
1824 CAmount nNet = nCredit - nDebit;
1825 CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
1826
1827 entry.pushKV("amount", ValueFromAmount(nNet - nFee));
1828 if (wtx.IsFromMe(filter))
1829 entry.pushKV("fee", ValueFromAmount(nFee));
1830
1831 WalletTxToJSON(pwallet->chain(), wtx, entry);
1832
1833 UniValue details(UniValue::VARR);
1834 ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
1835 entry.pushKV("details", details);
1836
1837 std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
1838 entry.pushKV("hex", strHex);
1839
1840 if (verbose) {
1841 UniValue decoded(UniValue::VOBJ);
1842 TxToUniv(*wtx.tx, uint256(), pwallet->chain().rpcEnableDeprecated("addresses"), decoded, false);
1843 entry.pushKV("decoded", decoded);
1844 }
1845
1846 return entry;
1847},
1848 };
1849}
1850
1851static RPCHelpMan abandontransaction()
1852{
1853 return RPCHelpMan{"abandontransaction",
1854 "\nMark in-wallet transaction <txid> as abandoned\n"
1855 "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
1856 "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
1857 "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
1858 "It has no effect on transactions which are already abandoned.\n",
1859 {
1860 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
1861 },
1862 RPCResult{RPCResult::Type::NONE, "", ""},
1863 RPCExamples{
1864 HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1865 + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1866 },
1867 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1868{
1869 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1870 if (!pwallet) return NullUniValue;
1871
1872 // Make sure the results are valid at least up to the most recent block
1873 // the user could have gotten from another RPC command prior to now
1874 pwallet->BlockUntilSyncedToCurrentChain();
1875
1876 LOCK(pwallet->cs_wallet);
1877
1878 uint256 hash(ParseHashV(request.params[0], "txid"));
1879
1880 if (!pwallet->mapWallet.count(hash)) {
1881 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
1882 }
1883 if (!pwallet->AbandonTransaction(hash)) {
1884 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
1885 }
1886
1887 return NullUniValue;
1888},
1889 };
1890}
1891
1892
1893static RPCHelpMan backupwallet()
1894{
1895 return RPCHelpMan{"backupwallet",
1896 "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
1897 {
1898 {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
1899 },
1900 RPCResult{RPCResult::Type::NONE, "", ""},
1901 RPCExamples{
1902 HelpExampleCli("backupwallet", "\"backup.dat\"")
1903 + HelpExampleRpc("backupwallet", "\"backup.dat\"")
1904 },
1905 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1906{
1907 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1908 if (!pwallet) return NullUniValue;
1909
1910 // Make sure the results are valid at least up to the most recent block
1911 // the user could have gotten from another RPC command prior to now
1912 pwallet->BlockUntilSyncedToCurrentChain();
1913
1914 LOCK(pwallet->cs_wallet);
1915
1916 std::string strDest = request.params[0].get_str();
1917 if (!pwallet->BackupWallet(strDest)) {
1918 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1919 }
1920
1921 return NullUniValue;
1922},
1923 };
1924}
1925
1926
1927static RPCHelpMan keypoolrefill()
1928{
1929 return RPCHelpMan{"keypoolrefill",
1930 "\nFills the keypool."+
1931 HELP_REQUIRING_PASSPHRASE,
1932 {
1933 {"newsize", RPCArg::Type::NUM, RPCArg::Default{100}, "The new keypool size"},
1934 },
1935 RPCResult{RPCResult::Type::NONE, "", ""},
1936 RPCExamples{
1937 HelpExampleCli("keypoolrefill", "")
1938 + HelpExampleRpc("keypoolrefill", "")
1939 },
1940 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1941{
1942 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1943 if (!pwallet) return NullUniValue;
1944
1945 if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1946 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
1947 }
1948
1949 LOCK(pwallet->cs_wallet);
1950
1951 // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
1952 unsigned int kpSize = 0;
1953 if (!request.params[0].isNull()) {
1954 if (request.params[0].get_int() < 0)
1955 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
1956 kpSize = (unsigned int)request.params[0].get_int();
1957 }
1958
1959 EnsureWalletIsUnlocked(*pwallet);
1960 pwallet->TopUpKeyPool(kpSize);
1961
1962 if (pwallet->GetKeyPoolSize() < kpSize) {
1963 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1964 }
1965
1966 return NullUniValue;
1967},
1968 };
1969}
1970
1971
1972static RPCHelpMan walletpassphrase()
1973{
1974 return RPCHelpMan{"walletpassphrase",
1975 "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
1976 "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
1977 "\nNote:\n"
1978 "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
1979 "time that overrides the old one.\n",
1980 {
1981 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
1982 {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
1983 },
1984 RPCResult{RPCResult::Type::NONE, "", ""},
1985 RPCExamples{
1986 "\nUnlock the wallet for 60 seconds\n"
1987 + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
1988 "\nLock the wallet again (before 60 seconds)\n"
1989 + HelpExampleCli("walletlock", "") +
1990 "\nAs a JSON-RPC call\n"
1991 + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
1992 },
1993 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1994{
1995 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1996 if (!wallet) return NullUniValue;
1997 CWallet* const pwallet = wallet.get();
1998
1999 int64_t nSleepTime;
2000 int64_t relock_time;
2001 // Prevent concurrent calls to walletpassphrase with the same wallet.
2002 LOCK(pwallet->m_unlock_mutex);
2003 {
2004 LOCK(pwallet->cs_wallet);
2005
2006 if (!pwallet->IsCrypted()) {
2007 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
2008 }
2009
2010 // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
2011 SecureString strWalletPass;
2012 strWalletPass.reserve(100);
2013 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
2014 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
2015 strWalletPass = request.params[0].get_str().c_str();
2016
2017 // Get the timeout
2018 nSleepTime = request.params[1].get_int64();
2019 // Timeout cannot be negative, otherwise it will relock immediately
2020 if (nSleepTime < 0) {
2021 throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
2022 }
2023 // Clamp timeout
2024 constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
2025 if (nSleepTime > MAX_SLEEP_TIME) {
2026 nSleepTime = MAX_SLEEP_TIME;
2027 }
2028
2029 if (strWalletPass.empty()) {
2030 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
2031 }
2032
2033 if (!pwallet->Unlock(strWalletPass)) {
2034 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
2035 }
2036
2037 pwallet->TopUpKeyPool();
2038
2039 pwallet->nRelockTime = GetTime() + nSleepTime;
2040 relock_time = pwallet->nRelockTime;
2041 }
2042
2043 // rpcRunLater must be called without cs_wallet held otherwise a deadlock
2044 // can occur. The deadlock would happen when RPCRunLater removes the
2045 // previous timer (and waits for the callback to finish if already running)
2046 // and the callback locks cs_wallet.
2047 AssertLockNotHeld(wallet->cs_wallet);
2048 // Keep a weak pointer to the wallet so that it is possible to unload the
2049 // wallet before the following callback is called. If a valid shared pointer
2050 // is acquired in the callback then the wallet is still loaded.
2051 std::weak_ptr<CWallet> weak_wallet = wallet;
2052 pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
2053 if (auto shared_wallet = weak_wallet.lock()) {
2054 LOCK(shared_wallet->cs_wallet);
2055 // Skip if this is not the most recent rpcRunLater callback.
2056 if (shared_wallet->nRelockTime != relock_time) return;
2057 shared_wallet->Lock();
2058 shared_wallet->nRelockTime = 0;
2059 }
2060 }, nSleepTime);
2061
2062 return NullUniValue;
2063},
2064 };
2065}
2066
2067
2068static RPCHelpMan walletpassphrasechange()
2069{
2070 return RPCHelpMan{"walletpassphrasechange",
2071 "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
2072 {
2073 {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
2074 {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
2075 },
2076 RPCResult{RPCResult::Type::NONE, "", ""},
2077 RPCExamples{
2078 HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
2079 + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
2080 },
2081 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2082{
2083 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2084 if (!pwallet) return NullUniValue;
2085
2086 LOCK(pwallet->cs_wallet);
2087
2088 if (!pwallet->IsCrypted()) {
2089 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
2090 }
2091
2092 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
2093 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
2094 SecureString strOldWalletPass;
2095 strOldWalletPass.reserve(100);
2096 strOldWalletPass = request.params[0].get_str().c_str();
2097
2098 SecureString strNewWalletPass;
2099 strNewWalletPass.reserve(100);
2100 strNewWalletPass = request.params[1].get_str().c_str();
2101
2102 if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
2103 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
2104 }
2105
2106 if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
2107 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
2108 }
2109
2110 return NullUniValue;
2111},
2112 };
2113}
2114
2115
2116static RPCHelpMan walletlock()
2117{
2118 return RPCHelpMan{"walletlock",
2119 "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
2120 "After calling this method, you will need to call walletpassphrase again\n"
2121 "before being able to call any methods which require the wallet to be unlocked.\n",
2122 {},
2123 RPCResult{RPCResult::Type::NONE, "", ""},
2124 RPCExamples{
2125 "\nSet the passphrase for 2 minutes to perform a transaction\n"
2126 + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
2127 "\nPerform a send (requires passphrase set)\n"
2128 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
2129 "\nClear the passphrase since we are done before 2 minutes is up\n"
2130 + HelpExampleCli("walletlock", "") +
2131 "\nAs a JSON-RPC call\n"
2132 + HelpExampleRpc("walletlock", "")
2133 },
2134 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2135{
2136 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2137 if (!pwallet) return NullUniValue;
2138
2139 LOCK(pwallet->cs_wallet);
2140
2141 if (!pwallet->IsCrypted()) {
2142 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
2143 }
2144
2145 pwallet->Lock();
2146 pwallet->nRelockTime = 0;
2147
2148 return NullUniValue;
2149},
2150 };
2151}
2152
2153
2154static RPCHelpMan encryptwallet()
2155{
2156 return RPCHelpMan{"encryptwallet",
2157 "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
2158 "After this, any calls that interact with private keys such as sending or signing \n"
2159 "will require the passphrase to be set prior the making these calls.\n"
2160 "Use the walletpassphrase call for this, and then walletlock call.\n"
2161 "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
2162 {
2163 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
2164 },
2165 RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
2166 RPCExamples{
2167 "\nEncrypt your wallet\n"
2168 + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
2169 "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
2170 + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
2171 "\nNow we can do something like sign\n"
2172 + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
2173 "\nNow lock the wallet again by removing the passphrase\n"
2174 + HelpExampleCli("walletlock", "") +
2175 "\nAs a JSON-RPC call\n"
2176 + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
2177 },
2178 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2179{
2180 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2181 if (!pwallet) return NullUniValue;
2182
2183 LOCK(pwallet->cs_wallet);
2184
2185 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
2186 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
2187 }
2188
2189 if (pwallet->IsCrypted()) {
2190 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
2191 }
2192
2193 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
2194 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
2195 SecureString strWalletPass;
2196 strWalletPass.reserve(100);
2197 strWalletPass = request.params[0].get_str().c_str();
2198
2199 if (strWalletPass.empty()) {
2200 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
2201 }
2202
2203 if (!pwallet->EncryptWallet(strWalletPass)) {
2204 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
2205 }
2206
2207 return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
2208},
2209 };
2210}
2211
2212static RPCHelpMan lockunspent()
2213{
2214 return RPCHelpMan{"lockunspent",
2215 "\nUpdates list of temporarily unspendable outputs.\n"
2216 "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
2217 "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
2218 "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
2219 "Manually selected coins are automatically unlocked.\n"
2220 "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
2221 "is always cleared (by virtue of process exit) when a node stops or fails.\n"
2222 "Also see the listunspent call\n",
2223 {
2224 {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
2225 {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
2226 {
2227 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
2228 {
2229 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
2230 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
2231 },
2232 },
2233 },
2234 },
2235 },
2236 RPCResult{
2237 RPCResult::Type::BOOL, "", "Whether the command was successful or not"
2238 },
2239 RPCExamples{
2240 "\nList the unspent transactions\n"
2241 + HelpExampleCli("listunspent", "") +
2242 "\nLock an unspent transaction\n"
2243 + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
2244 "\nList the locked transactions\n"
2245 + HelpExampleCli("listlockunspent", "") +
2246 "\nUnlock the transaction again\n"
2247 + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
2248 "\nAs a JSON-RPC call\n"
2249 + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
2250 },
2251 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2252{
2253 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2254 if (!pwallet) return NullUniValue;
2255
2256 // Make sure the results are valid at least up to the most recent block
2257 // the user could have gotten from another RPC command prior to now
2258 pwallet->BlockUntilSyncedToCurrentChain();
2259
2260 LOCK(pwallet->cs_wallet);
2261
2262 RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
2263
2264 bool fUnlock = request.params[0].get_bool();
2265
2266 if (request.params[1].isNull()) {
2267 if (fUnlock)
2268 pwallet->UnlockAllCoins();
2269 return true;
2270 }
2271
2272 RPCTypeCheckArgument(request.params[1], UniValue::VARR);
2273
2274 const UniValue& output_params = request.params[1];
2275
2276 // Create and validate the COutPoints first.
2277
2278 std::vector<COutPoint> outputs;
2279 outputs.reserve(output_params.size());
2280
2281 for (unsigned int idx = 0; idx < output_params.size(); idx++) {
2282 const UniValue& o = output_params[idx].get_obj();
2283
2284 RPCTypeCheckObj(o,
2285 {
2286 {"txid", UniValueType(UniValue::VSTR)},
2287 {"vout", UniValueType(UniValue::VNUM)},
2288 });
2289
2290 const uint256 txid(ParseHashO(o, "txid"));
2291 const int nOutput = find_value(o, "vout").get_int();
2292 if (nOutput < 0) {
2293 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
2294 }
2295
2296 const COutPoint outpt(txid, nOutput);
2297
2298 const auto it = pwallet->mapWallet.find(outpt.hash);
2299 if (it == pwallet->mapWallet.end()) {
2300 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
2301 }
2302
2303 const CWalletTx& trans = it->second;
2304
2305 if (outpt.n >= trans.tx->vout.size()) {
2306 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
2307 }
2308
2309 if (pwallet->IsSpent(outpt.hash, outpt.n)) {
2310 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
2311 }
2312
2313 const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
2314
2315 if (fUnlock && !is_locked) {
2316 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
2317 }
2318
2319 if (!fUnlock && is_locked) {
2320 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
2321 }
2322
2323 outputs.push_back(outpt);
2324 }
2325
2326 // Atomically set (un)locked status for the outputs.
2327 for (const COutPoint& outpt : outputs) {
2328 if (fUnlock) pwallet->UnlockCoin(outpt);
2329 else pwallet->LockCoin(outpt);
2330 }
2331
2332 return true;
2333},
2334 };
2335}
2336
2337static RPCHelpMan listlockunspent()
2338{
2339 return RPCHelpMan{"listlockunspent",
2340 "\nReturns list of temporarily unspendable outputs.\n"
2341 "See the lockunspent call to lock and unlock transactions for spending.\n",
2342 {},
2343 RPCResult{
2344 RPCResult::Type::ARR, "", "",
2345 {
2346 {RPCResult::Type::OBJ, "", "",
2347 {
2348 {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
2349 {RPCResult::Type::NUM, "vout", "The vout value"},
2350 }},
2351 }
2352 },
2353 RPCExamples{
2354 "\nList the unspent transactions\n"
2355 + HelpExampleCli("listunspent", "") +
2356 "\nLock an unspent transaction\n"
2357 + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
2358 "\nList the locked transactions\n"
2359 + HelpExampleCli("listlockunspent", "") +
2360 "\nUnlock the transaction again\n"
2361 + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
2362 "\nAs a JSON-RPC call\n"
2363 + HelpExampleRpc("listlockunspent", "")
2364 },
2365 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2366{
2367 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2368 if (!pwallet) return NullUniValue;
2369
2370 LOCK(pwallet->cs_wallet);
2371
2372 std::vector<COutPoint> vOutpts;
2373 pwallet->ListLockedCoins(vOutpts);
2374
2375 UniValue ret(UniValue::VARR);
2376
2377 for (const COutPoint& outpt : vOutpts) {
2378 UniValue o(UniValue::VOBJ);
2379
2380 o.pushKV("txid", outpt.hash.GetHex());
2381 o.pushKV("vout", (int)outpt.n);
2382 ret.push_back(o);
2383 }
2384
2385 return ret;
2386},
2387 };
2388}
2389
2390static RPCHelpMan settxfee()
2391{
2392 return RPCHelpMan{"settxfee",
2393 "\nSet the transaction fee rate in " + CURRENCY_UNIT + "/kvB for this wallet. Overrides the global -paytxfee command line parameter.\n"
2394 "Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
2395 {
2396 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_UNIT + "/kvB"},
2397 },
2398 RPCResult{
2399 RPCResult::Type::BOOL, "", "Returns true if successful"
2400 },
2401 RPCExamples{
2402 HelpExampleCli("settxfee", "0.00001")
2403 + HelpExampleRpc("settxfee", "0.00001")
2404 },
2405 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2406{
2407 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2408 if (!pwallet) return NullUniValue;
2409
2410 LOCK(pwallet->cs_wallet);
2411
2412 CAmount nAmount = AmountFromValue(request.params[0]);
2413 CFeeRate tx_fee_rate(nAmount, 1000);
2414 CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000);
2415 if (tx_fee_rate == CFeeRate(0)) {
2416 // automatic selection
2417 } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
2418 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
2419 } else if (tx_fee_rate < pwallet->m_min_fee) {
2420 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
2421 } else if (tx_fee_rate > max_tx_fee_rate) {
2422 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be more than wallet max tx fee (%s)", max_tx_fee_rate.ToString()));
2423 }
2424
2425 pwallet->m_pay_tx_fee = tx_fee_rate;
2426 return true;
2427},
2428 };
2429}
2430
2431static RPCHelpMan getbalances()
2432{
2433 return RPCHelpMan{
2434 "getbalances",
2435 "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
2436 {},
2437 RPCResult{
2438 RPCResult::Type::OBJ, "", "",
2439 {
2440 {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
2441 {
2442 {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
2443 {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
2444 {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
2445 {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
2446 }},
2447 {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
2448 {
2449 {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
2450 {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
2451 {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
2452 }},
2453 }
2454 },
2455 RPCExamples{
2456 HelpExampleCli("getbalances", "") +
2457 HelpExampleRpc("getbalances", "")},
2458 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2459{
2460 std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
2461 if (!rpc_wallet) return NullUniValue;
2462 CWallet& wallet = *rpc_wallet;
2463
2464 // Make sure the results are valid at least up to the most recent block
2465 // the user could have gotten from another RPC command prior to now
2466 wallet.BlockUntilSyncedToCurrentChain();
2467
2468 LOCK(wallet.cs_wallet);
2469
2470 const auto bal = wallet.GetBalance();
2471 UniValue balances{UniValue::VOBJ};
2472 {
2473 UniValue balances_mine{UniValue::VOBJ};
2474 balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
2475 balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
2476 balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
2477 if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
2478 // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
2479 // the total balance, and then subtract bal to get the reused address balance.
2480 const auto full_bal = wallet.GetBalance(0, false);
2481 balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
2482 }
2483 balances.pushKV("mine", balances_mine);
2484 }
2485 auto spk_man = wallet.GetLegacyScriptPubKeyMan();
2486 if (spk_man && spk_man->HaveWatchOnly()) {
2487 UniValue balances_watchonly{UniValue::VOBJ};
2488 balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
2489 balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
2490 balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
2491 balances.pushKV("watchonly", balances_watchonly);
2492 }
2493 return balances;
2494},
2495 };
2496}
2497
2498static RPCHelpMan getwalletinfo()
2499{
2500 return RPCHelpMan{"getwalletinfo",
2501 "Returns an object containing various wallet state info.\n",
2502 {},
2503 RPCResult{
2504 RPCResult::Type::OBJ, "", "",
2505 {
2506 {
2507 {RPCResult::Type::STR, "walletname", "the wallet name"},
2508 {RPCResult::Type::NUM, "walletversion", "the wallet version"},
2509 {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
2510 {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
2511 {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
2512 {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
2513 {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
2514 {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
2515 {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
2516 {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
2517 {RPCResult::Type::NUM_TIME, "unlocked_until", /* optional */ true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
2518 {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
2519 {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
2520 {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
2521 {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
2522 {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
2523 {
2524 {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
2525 {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
2526 }},
2527 {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for scriptPubKey management"},
2528 }},
2529 },
2530 RPCExamples{
2531 HelpExampleCli("getwalletinfo", "")
2532 + HelpExampleRpc("getwalletinfo", "")
2533 },
2534 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2535{
2536 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2537 if (!pwallet) return NullUniValue;
2538
2539 // Make sure the results are valid at least up to the most recent block
2540 // the user could have gotten from another RPC command prior to now
2541 pwallet->BlockUntilSyncedToCurrentChain();
2542
2543 LOCK(pwallet->cs_wallet);
2544
2545 UniValue obj(UniValue::VOBJ);
2546
2547 size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
2548 const auto bal = pwallet->GetBalance();
2549 int64_t kp_oldest = pwallet->GetOldestKeyPoolTime();
2550 obj.pushKV("walletname", pwallet->GetName());
2551 obj.pushKV("walletversion", pwallet->GetVersion());
2552 obj.pushKV("format", pwallet->GetDatabase().Format());
2553 obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
2554 obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
2555 obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
2556 obj.pushKV("txcount", (int)pwallet->mapWallet.size());
2557 if (kp_oldest > 0) {
2558 obj.pushKV("keypoololdest", kp_oldest);
2559 }
2560 obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
2561
2562 LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
2563 if (spk_man) {
2564 CKeyID seed_id = spk_man->GetHDChain().seed_id;
2565 if (!seed_id.IsNull()) {
2566 obj.pushKV("hdseedid", seed_id.GetHex());
2567 }
2568 }
2569
2570 if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
2571 obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
2572 }
2573 if (pwallet->IsCrypted()) {
2574 obj.pushKV("unlocked_until", pwallet->nRelockTime);
2575 }
2576 obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
2577 obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
2578 obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
2579 if (pwallet->IsScanning()) {
2580 UniValue scanning(UniValue::VOBJ);
2581 scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
2582 scanning.pushKV("progress", pwallet->ScanningProgress());
2583 obj.pushKV("scanning", scanning);
2584 } else {
2585 obj.pushKV("scanning", false);
2586 }
2587 obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
2588 return obj;
2589},
2590 };
2591}
2592
2593static RPCHelpMan listwalletdir()
2594{
2595 return RPCHelpMan{"listwalletdir",
2596 "Returns a list of wallets in the wallet directory.\n",
2597 {},
2598 RPCResult{
2599 RPCResult::Type::OBJ, "", "",
2600 {
2601 {RPCResult::Type::ARR, "wallets", "",
2602 {
2603 {RPCResult::Type::OBJ, "", "",
2604 {
2605 {RPCResult::Type::STR, "name", "The wallet name"},
2606 }},
2607 }},
2608 }
2609 },
2610 RPCExamples{
2611 HelpExampleCli("listwalletdir", "")
2612 + HelpExampleRpc("listwalletdir", "")
2613 },
2614 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2615{
2616 UniValue wallets(UniValue::VARR);
2617 for (const auto& path : ListDatabases(GetWalletDir())) {
2618 UniValue wallet(UniValue::VOBJ);
2619 wallet.pushKV("name", path.string());
2620 wallets.push_back(wallet);
2621 }
2622
2623 UniValue result(UniValue::VOBJ);
2624 result.pushKV("wallets", wallets);
2625 return result;
2626},
2627 };
2628}
2629
2630static RPCHelpMan listwallets()
2631{
2632 return RPCHelpMan{"listwallets",
2633 "Returns a list of currently loaded wallets.\n"
2634 "For full information on the wallet, use \"getwalletinfo\"\n",
2635 {},
2636 RPCResult{
2637 RPCResult::Type::ARR, "", "",
2638 {
2639 {RPCResult::Type::STR, "walletname", "the wallet name"},
2640 }
2641 },
2642 RPCExamples{
2643 HelpExampleCli("listwallets", "")
2644 + HelpExampleRpc("listwallets", "")
2645 },
2646 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2647{
2648 UniValue obj(UniValue::VARR);
2649
2650 for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
2651 LOCK(wallet->cs_wallet);
2652 obj.push_back(wallet->GetName());
2653 }
2654
2655 return obj;
2656},
2657 };
2658}
2659
2660static RPCHelpMan loadwallet()
2661{
2662 return RPCHelpMan{"loadwallet",
2663 "\nLoads a wallet from a wallet file or directory."
2664 "\nNote that all wallet command-line options used when starting bitcoind will be"
2665 "\napplied to the new wallet (eg -rescan, etc).\n",
2666 {
2667 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
2668 {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
2669 },
2670 RPCResult{
2671 RPCResult::Type::OBJ, "", "",
2672 {
2673 {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
2674 {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
2675 }
2676 },
2677 RPCExamples{
2678 HelpExampleCli("loadwallet", "\"test.dat\"")
2679 + HelpExampleRpc("loadwallet", "\"test.dat\"")
2680 },
2681 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2682{
2683 WalletContext& context = EnsureWalletContext(request.context);
2684 const std::string name(request.params[0].get_str());
2685
2686 DatabaseOptions options;
2687 DatabaseStatus status;
2688 options.require_existing = true;
2689 bilingual_str error;
2690 std::vector<bilingual_str> warnings;
2691 std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
2692 std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
2693 if (!wallet) {
2694 // Map bad format to not found, since bad format is returned when the
2695 // wallet directory exists, but doesn't contain a data file.
2696 RPCErrorCode code = RPC_WALLET_ERROR;
2697 switch (status) {
2698 case DatabaseStatus::FAILED_NOT_FOUND:
2699 case DatabaseStatus::FAILED_BAD_FORMAT:
2700 code = RPC_WALLET_NOT_FOUND;
2701 break;
2702 case DatabaseStatus::FAILED_ALREADY_LOADED:
2703 code = RPC_WALLET_ALREADY_LOADED;
2704 break;
2705 default: // RPC_WALLET_ERROR is returned for all other cases.
2706 break;
2707 }
2708 throw JSONRPCError(code, error.original);
2709 }
2710
2711 UniValue obj(UniValue::VOBJ);
2712 obj.pushKV("name", wallet->GetName());
2713 obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
2714
2715 return obj;
2716},
2717 };
2718}
2719
2720static RPCHelpMan setwalletflag()
2721{
2722 std::string flags = "";
2723 for (auto& it : WALLET_FLAG_MAP)
2724 if (it.second & MUTABLE_WALLET_FLAGS)
2725 flags += (flags == "" ? "" : ", ") + it.first;
2726
2727 return RPCHelpMan{"setwalletflag",
2728 "\nChange the state of the given wallet flag for a wallet.\n",
2729 {
2730 {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
2731 {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
2732 },
2733 RPCResult{
2734 RPCResult::Type::OBJ, "", "",
2735 {
2736 {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
2737 {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
2738 {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"},
2739 }
2740 },
2741 RPCExamples{
2742 HelpExampleCli("setwalletflag", "avoid_reuse")
2743 + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
2744 },
2745 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2746{
2747 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2748 if (!pwallet) return NullUniValue;
2749
2750 std::string flag_str = request.params[0].get_str();
2751 bool value = request.params[1].isNull() || request.params[1].get_bool();
2752
2753 if (!WALLET_FLAG_MAP.count(flag_str)) {
2754 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
2755 }
2756
2757 auto flag = WALLET_FLAG_MAP.at(flag_str);
2758
2759 if (!(flag & MUTABLE_WALLET_FLAGS)) {
2760 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
2761 }
2762
2763 UniValue res(UniValue::VOBJ);
2764
2765 if (pwallet->IsWalletFlagSet(flag) == value) {
2766 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
2767 }
2768
2769 res.pushKV("flag_name", flag_str);
2770 res.pushKV("flag_state", value);
2771
2772 if (value) {
2773 pwallet->SetWalletFlag(flag);
2774 } else {
2775 pwallet->UnsetWalletFlag(flag);
2776 }
2777
2778 if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
2779 res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
2780 }
2781
2782 return res;
2783},
2784 };
2785}
2786
2787static RPCHelpMan createwallet()
2788{
2789 return RPCHelpMan{
2790 "createwallet",
2791 "\nCreates and loads a new wallet.\n",
2792 {
2793 {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
2794 {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
2795 {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
2796 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
2797 {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
2798 {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
2799 {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
2800 {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
2801 },
2802 RPCResult{
2803 RPCResult::Type::OBJ, "", "",
2804 {
2805 {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
2806 {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
2807 }
2808 },
2809 RPCExamples{
2810 HelpExampleCli("createwallet", "\"testwallet\"")
2811 + HelpExampleRpc("createwallet", "\"testwallet\"")
2812 + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
2813 + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
2814 },
2815 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2816{
2817 WalletContext& context = EnsureWalletContext(request.context);
2818 uint64_t flags = 0;
2819 if (!request.params[1].isNull() && request.params[1].get_bool()) {
2820 flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
2821 }
2822
2823 if (!request.params[2].isNull() && request.params[2].get_bool()) {
2824 flags |= WALLET_FLAG_BLANK_WALLET;
2825 }
2826 SecureString passphrase;
2827 passphrase.reserve(100);
2828 std::vector<bilingual_str> warnings;
2829 if (!request.params[3].isNull()) {
2830 passphrase = request.params[3].get_str().c_str();
2831 if (passphrase.empty()) {
2832 // Empty string means unencrypted
2833 warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
2834 }
2835 }
2836
2837 if (!request.params[4].isNull() && request.params[4].get_bool()) {
2838 flags |= WALLET_FLAG_AVOID_REUSE;
2839 }
2840 if (!request.params[5].isNull() && request.params[5].get_bool()) {
2841#ifndef USE_SQLITE
2842 throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
2843#endif
2844 flags |= WALLET_FLAG_DESCRIPTORS;
2845 warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
2846 }
2847 if (!request.params[7].isNull() && request.params[7].get_bool()) {
2848#ifdef ENABLE_EXTERNAL_SIGNER
2849 flags |= WALLET_FLAG_EXTERNAL_SIGNER;
2850#else
2851 throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
2852#endif
2853 }
2854
2855#ifndef USE_BDB
2856 if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
2857 throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without bdb support (required for legacy wallets)");
2858 }
2859#endif
2860
2861 DatabaseOptions options;
2862 DatabaseStatus status;
2863 options.require_create = true;
2864 options.create_flags = flags;
2865 options.create_passphrase = passphrase;
2866 bilingual_str error;
2867 std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
2868 std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
2869 if (!wallet) {
2870 RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
2871 throw JSONRPCError(code, error.original);
2872 }
2873
2874 UniValue obj(UniValue::VOBJ);
2875 obj.pushKV("name", wallet->GetName());
2876 obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
2877
2878 return obj;
2879},
2880 };
2881}
2882
2883static RPCHelpMan unloadwallet()
2884{
2885 return RPCHelpMan{"unloadwallet",
2886 "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
2887 "Specifying the wallet name on a wallet endpoint is invalid.",
2888 {
2889 {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
2890 {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
2891 },
2892 RPCResult{RPCResult::Type::OBJ, "", "", {
2893 {RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
2894 }},
2895 RPCExamples{
2896 HelpExampleCli("unloadwallet", "wallet_name")
2897 + HelpExampleRpc("unloadwallet", "wallet_name")
2898 },
2899 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2900{
2901 std::string wallet_name;
2902 if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
2903 if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
2904 throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
2905 }
2906 } else {
2907 wallet_name = request.params[0].get_str();
2908 }
2909
2910 std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
2911 if (!wallet) {
2912 throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
2913 }
2914
2915 // Release the "main" shared pointer and prevent further notifications.
2916 // Note that any attempt to load the same wallet would fail until the wallet
2917 // is destroyed (see CheckUniqueFileid).
2918 std::vector<bilingual_str> warnings;
2919 std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
2920 if (!RemoveWallet(wallet, load_on_start, warnings)) {
2921 throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
2922 }
2923
2924 UnloadWallet(std::move(wallet));
2925
2926 UniValue result(UniValue::VOBJ);
2927 result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
2928 return result;
2929},
2930 };
2931}
2932
2933static RPCHelpMan listunspent()
2934{
2935 return RPCHelpMan{
2936 "listunspent",
2937 "\nReturns array of unspent transaction outputs\n"
2938 "with between minconf and maxconf (inclusive) confirmations.\n"
2939 "Optionally filter to only include txouts paid to specified addresses.\n",
2940 {
2941 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
2942 {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
2943 {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The bitcoin addresses to filter",
2944 {
2945 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
2946 },
2947 },
2948 {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
2949 "See description of \"safe\" attribute below."},
2950 {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
2951 {
2952 {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
2953 {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
2954 {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
2955 {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
2956 },
2957 "query_options"},
2958 },
2959 RPCResult{
2960 RPCResult::Type::ARR, "", "",
2961 {
2962 {RPCResult::Type::OBJ, "", "",
2963 {
2964 {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
2965 {RPCResult::Type::NUM, "vout", "the vout value"},
2966 {RPCResult::Type::STR, "address", "the bitcoin address"},
2967 {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
2968 {RPCResult::Type::STR, "scriptPubKey", "the script key"},
2969 {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
2970 {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
2971 {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
2972 {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
2973 {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
2974 {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
2975 {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
2976 {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
2977 {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
2978 "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
2979 "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
2980 }},
2981 }
2982 },
2983 RPCExamples{
2984 HelpExampleCli("listunspent", "")
2985 + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
2986 + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
2987 + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
2988 + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
2989 },
2990 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2991{
2992 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
2993 if (!pwallet) return NullUniValue;
2994
2995 int nMinDepth = 1;
2996 if (!request.params[0].isNull()) {
2997 RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
2998 nMinDepth = request.params[0].get_int();
2999 }
3000
3001 int nMaxDepth = 9999999;
3002 if (!request.params[1].isNull()) {
3003 RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
3004 nMaxDepth = request.params[1].get_int();
3005 }
3006
3007 std::set<CTxDestination> destinations;
3008 if (!request.params[2].isNull()) {
3009 RPCTypeCheckArgument(request.params[2], UniValue::VARR);
3010 UniValue inputs = request.params[2].get_array();
3011 for (unsigned int idx = 0; idx < inputs.size(); idx++) {
3012 const UniValue& input = inputs[idx];
3013 CTxDestination dest = DecodeDestination(input.get_str());
3014 if (!IsValidDestination(dest)) {
3015 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
3016 }
3017 if (!destinations.insert(dest).second) {
3018 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
3019 }
3020 }
3021 }
3022
3023 bool include_unsafe = true;
3024 if (!request.params[3].isNull()) {
3025 RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
3026 include_unsafe = request.params[3].get_bool();
3027 }
3028
3029 CAmount nMinimumAmount = 0;
3030 CAmount nMaximumAmount = MAX_MONEY;
3031 CAmount nMinimumSumAmount = MAX_MONEY;
3032 uint64_t nMaximumCount = 0;
3033
3034 if (!request.params[4].isNull()) {
3035 const UniValue& options = request.params[4].get_obj();
3036
3037 RPCTypeCheckObj(options,
3038 {
3039 {"minimumAmount", UniValueType()},
3040 {"maximumAmount", UniValueType()},
3041 {"minimumSumAmount", UniValueType()},
3042 {"maximumCount", UniValueType(UniValue::VNUM)},
3043 },
3044 true, true);
3045
3046 if (options.exists("minimumAmount"))
3047 nMinimumAmount = AmountFromValue(options["minimumAmount"]);
3048
3049 if (options.exists("maximumAmount"))
3050 nMaximumAmount = AmountFromValue(options["maximumAmount"]);
3051
3052 if (options.exists("minimumSumAmount"))
3053 nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
3054
3055 if (options.exists("maximumCount"))
3056 nMaximumCount = options["maximumCount"].get_int64();
3057 }
3058
3059 // Make sure the results are valid at least up to the most recent block
3060 // the user could have gotten from another RPC command prior to now
3061 pwallet->BlockUntilSyncedToCurrentChain();
3062
3063 UniValue results(UniValue::VARR);
3064 std::vector<COutput> vecOutputs;
3065 {
3066 CCoinControl cctl;
3067 cctl.m_avoid_address_reuse = false;
3068 cctl.m_min_depth = nMinDepth;
3069 cctl.m_max_depth = nMaxDepth;
3070 cctl.m_include_unsafe_inputs = include_unsafe;
3071 LOCK(pwallet->cs_wallet);
3072 pwallet->AvailableCoins(vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
3073 }
3074
3075 LOCK(pwallet->cs_wallet);
3076
3077 const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
3078
3079 for (const COutput& out : vecOutputs) {
3080 CTxDestination address;
3081 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
3082 bool fValidAddress = ExtractDestination(scriptPubKey, address);
3083 bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
3084
3085 if (destinations.size() && (!fValidAddress || !destinations.count(address)))
3086 continue;
3087
3088 UniValue entry(UniValue::VOBJ);
3089 entry.pushKV("txid", out.tx->GetHash().GetHex());
3090 entry.pushKV("vout", out.i);
3091
3092 if (fValidAddress) {
3093 entry.pushKV("address", EncodeDestination(address));
3094
3095 const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
3096 if (address_book_entry) {
3097 entry.pushKV("label", address_book_entry->GetLabel());
3098 }
3099
3100 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
3101 if (provider) {
3102 if (scriptPubKey.IsPayToScriptHash()) {
3103 const CScriptID& hash = CScriptID(std::get<ScriptHash>(address));
3104 CScript redeemScript;
3105 if (provider->GetCScript(hash, redeemScript)) {
3106 entry.pushKV("redeemScript", HexStr(redeemScript));
3107 // Now check if the redeemScript is actually a P2WSH script
3108 CTxDestination witness_destination;
3109 if (redeemScript.IsPayToWitnessScriptHash()) {
3110 bool extracted = ExtractDestination(redeemScript, witness_destination);
3111 CHECK_NONFATAL(extracted);
3112 // Also return the witness script
3113 const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
3114 CScriptID id;
3115 CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
3116 CScript witnessScript;
3117 if (provider->GetCScript(id, witnessScript)) {
3118 entry.pushKV("witnessScript", HexStr(witnessScript));
3119 }
3120 }
3121 }
3122 } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
3123 const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address);
3124 CScriptID id;
3125 CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
3126 CScript witnessScript;
3127 if (provider->GetCScript(id, witnessScript)) {
3128 entry.pushKV("witnessScript", HexStr(witnessScript));
3129 }
3130 }
3131 }
3132 }
3133
3134 entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
3135 entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
3136 entry.pushKV("confirmations", out.nDepth);
3137 entry.pushKV("spendable", out.fSpendable);
3138 entry.pushKV("solvable", out.fSolvable);
3139 if (out.fSolvable) {
3140 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
3141 if (provider) {
3142 auto descriptor = InferDescriptor(scriptPubKey, *provider);
3143 entry.pushKV("desc", descriptor->ToString());
3144 }
3145 }
3146 if (avoid_reuse) entry.pushKV("reused", reused);
3147 entry.pushKV("safe", out.fSafe);
3148 results.push_back(entry);
3149 }
3150
3151 return results;
3152},
3153 };
3154}
3155
3156void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
3157{
3158 // Make sure the results are valid at least up to the most recent block
3159 // the user could have gotten from another RPC command prior to now
3160 wallet.BlockUntilSyncedToCurrentChain();
3161
3162 change_position = -1;
3163 bool lockUnspents = false;
3164 UniValue subtractFeeFromOutputs;
3165 std::set<int> setSubtractFeeFromOutputs;
3166
3167 if (!options.isNull()) {
3168 if (options.type() == UniValue::VBOOL) {
3169 // backward compatibility bool only fallback
3170 coinControl.fAllowWatchOnly = options.get_bool();
3171 }
3172 else {
3173 RPCTypeCheckArgument(options, UniValue::VOBJ);
3174 RPCTypeCheckObj(options,
3175 {
3176 {"add_inputs", UniValueType(UniValue::VBOOL)},
3177 {"include_unsafe", UniValueType(UniValue::VBOOL)},
3178 {"add_to_wallet", UniValueType(UniValue::VBOOL)},
3179 {"changeAddress", UniValueType(UniValue::VSTR)},
3180 {"change_address", UniValueType(UniValue::VSTR)},
3181 {"changePosition", UniValueType(UniValue::VNUM)},
3182 {"change_position", UniValueType(UniValue::VNUM)},
3183 {"change_type", UniValueType(UniValue::VSTR)},
3184 {"includeWatching", UniValueType(UniValue::VBOOL)},
3185 {"include_watching", UniValueType(UniValue::VBOOL)},
3186 {"inputs", UniValueType(UniValue::VARR)},
3187 {"lockUnspents", UniValueType(UniValue::VBOOL)},
3188 {"lock_unspents", UniValueType(UniValue::VBOOL)},
3189 {"locktime", UniValueType(UniValue::VNUM)},
3190 {"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
3191 {"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
3192 {"psbt", UniValueType(UniValue::VBOOL)},
3193 {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
3194 {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
3195 {"replaceable", UniValueType(UniValue::VBOOL)},
3196 {"conf_target", UniValueType(UniValue::VNUM)},
3197 {"estimate_mode", UniValueType(UniValue::VSTR)},
3198 },
3199 true, true);
3200
3201 if (options.exists("add_inputs") ) {
3202 coinControl.m_add_inputs = options["add_inputs"].get_bool();
3203 }
3204
3205 if (options.exists("changeAddress") || options.exists("change_address")) {
3206 const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
3207 CTxDestination dest = DecodeDestination(change_address_str);
3208
3209 if (!IsValidDestination(dest)) {
3210 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid bitcoin address");
3211 }
3212
3213 coinControl.destChange = dest;
3214 }
3215
3216 if (options.exists("changePosition") || options.exists("change_position")) {
3217 change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).get_int();
3218 }
3219
3220 if (options.exists("change_type")) {
3221 if (options.exists("changeAddress") || options.exists("change_address")) {
3222 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
3223 }
3224 OutputType out_type;
3225 if (!ParseOutputType(options["change_type"].get_str(), out_type)) {
3226 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
3227 }
3228 coinControl.m_change_type.emplace(out_type);
3229 }
3230
3231 const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
3232 coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
3233
3234 if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
3235 lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
3236 }
3237
3238 if (options.exists("include_unsafe")) {
3239 coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
3240 }
3241
3242 if (options.exists("feeRate")) {
3243 if (options.exists("fee_rate")) {
3244 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)");
3245 }
3246 if (options.exists("conf_target")) {
3247 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
3248 }
3249 if (options.exists("estimate_mode")) {
3250 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
3251 }
3252 coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
3253 coinControl.fOverrideFeeRate = true;
3254 }
3255
3256 if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
3257 subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
3258
3259 if (options.exists("replaceable")) {
3260 coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
3261 }
3262 SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
3263 }
3264 } else {
3265 // if options is null and not a bool
3266 coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
3267 }
3268
3269 if (tx.vout.size() == 0)
3270 throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
3271
3272 if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
3273 throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
3274
3275 for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
3276 int pos = subtractFeeFromOutputs[idx].get_int();
3277 if (setSubtractFeeFromOutputs.count(pos))
3278 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
3279 if (pos < 0)
3280 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
3281 if (pos >= int(tx.vout.size()))
3282 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
3283 setSubtractFeeFromOutputs.insert(pos);
3284 }
3285
3286 bilingual_str error;
3287
3288 if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
3289 throw JSONRPCError(RPC_WALLET_ERROR, error.original);
3290 }
3291}
3292
3293static RPCHelpMan fundrawtransaction()
3294{
3295 return RPCHelpMan{"fundrawtransaction",
3296 "\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
3297 "It will add at most one change output to the outputs.\n"
3298 "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
3299 "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
3300 "The inputs added will not be signed, use signrawtransactionwithkey\n"
3301 " or signrawtransactionwithwallet for that.\n"
3302 "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
3303 "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
3304 "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
3305 "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
3306 "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
3307 {
3308 {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
3309 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
3310 {
3311 {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
3312 {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
3313 "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
3314 "If that happens, you will need to fund the transaction with different inputs and republish it."},
3315 {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
3316 {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
3317 {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
3318 {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
3319 "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
3320 "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
3321 {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
3322 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
3323 {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
3324 {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The integers.\n"
3325 "The fee will be equally deducted from the amount of each specified output.\n"
3326 "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
3327 "If no outputs are specified here, the sender pays the fee.",
3328 {
3329 {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
3330 },
3331 },
3332 {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
3333 "Allows this transaction to be replaced by a transaction with higher fees"},
3334 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
3335 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
3336 " \"" + FeeModes("\"\n\"") + "\""},
3337 },
3338 "options"},
3339 {"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
3340 "If iswitness is not present, heuristic tests will be used in decoding.\n"
3341 "If true, only witness deserialization will be tried.\n"
3342 "If false, only non-witness deserialization will be tried.\n"
3343 "This boolean should reflect whether the transaction has inputs\n"
3344 "(e.g. fully valid, or on-chain transactions), if known by the caller."
3345 },
3346 },
3347 RPCResult{
3348 RPCResult::Type::OBJ, "", "",
3349 {
3350 {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
3351 {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
3352 {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
3353 }
3354 },
3355 RPCExamples{
3356 "\nCreate a transaction with no inputs\n"
3357 + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
3358 "\nAdd sufficient unsigned inputs to meet the output value\n"
3359 + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
3360 "\nSign the transaction\n"
3361 + HelpExampleCli("signrawtransactionwithwallet", "\"fundedtransactionhex\"") +
3362 "\nSend the transaction\n"
3363 + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
3364 },
3365 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
3366{
3367 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
3368 if (!pwallet) return NullUniValue;
3369
3370 RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
3371
3372 // parse hex string from parameter
3373 CMutableTransaction tx;
3374 bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
3375 bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
3376 if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
3377 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
3378 }
3379
3380 CAmount fee;
3381 int change_position;
3382 CCoinControl coin_control;
3383 // Automatically select (additional) coins. Can be overridden by options.add_inputs.
3384 coin_control.m_add_inputs = true;
3385 FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
3386
3387 UniValue result(UniValue::VOBJ);
3388 result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
3389 result.pushKV("fee", ValueFromAmount(fee));
3390 result.pushKV("changepos", change_position);
3391
3392 return result;
3393},
3394 };
3395}
3396
3397RPCHelpMan signrawtransactionwithwallet()
3398{
3399 return RPCHelpMan{"signrawtransactionwithwallet",
3400 "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
3401 "The second optional argument (may be null) is an array of previous transaction outputs that\n"
3402 "this transaction depends on but may not yet be in the block chain." +
3403 HELP_REQUIRING_PASSPHRASE,
3404 {
3405 {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
3406 {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
3407 {
3408 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
3409 {
3410 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
3411 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
3412 {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
3413 {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
3414 {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
3415 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
3416 },
3417 },
3418 },
3419 },
3420 {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of\n"
3421 " \"DEFAULT\"\n"
3422 " \"ALL\"\n"
3423 " \"NONE\"\n"
3424 " \"SINGLE\"\n"
3425 " \"ALL|ANYONECANPAY\"\n"
3426 " \"NONE|ANYONECANPAY\"\n"
3427 " \"SINGLE|ANYONECANPAY\""},
3428 },
3429 RPCResult{
3430 RPCResult::Type::OBJ, "", "",
3431 {
3432 {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
3433 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
3434 {RPCResult::Type::ARR, "errors", /* optional */ true, "Script verification errors (if there are any)",
3435 {
3436 {RPCResult::Type::OBJ, "", "",
3437 {
3438 {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
3439 {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
3440 {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
3441 {RPCResult::Type::NUM, "sequence", "Script sequence number"},
3442 {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
3443 }},
3444 }},
3445 }
3446 },
3447 RPCExamples{
3448 HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
3449 + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
3450 },
3451 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
3452{
3453 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
3454 if (!pwallet) return NullUniValue;
3455
3456 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
3457
3458 CMutableTransaction mtx;
3459 if (!DecodeHexTx(mtx, request.params[0].get_str())) {
3460 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
3461 }
3462
3463 // Sign the transaction
3464 LOCK(pwallet->cs_wallet);
3465 EnsureWalletIsUnlocked(*pwallet);
3466
3467 // Fetch previous transactions (inputs):
3468 std::map<COutPoint, Coin> coins;
3469 for (const CTxIn& txin : mtx.vin) {
3470 coins[txin.prevout]; // Create empty map entry keyed by prevout.
3471 }
3472 pwallet->chain().findCoins(coins);
3473
3474 // Parse the prevtxs array
3475 ParsePrevouts(request.params[1], nullptr, coins);
3476
3477 int nHashType = ParseSighashString(request.params[2]);
3478
3479 // Script verification errors
3480 std::map<int, std::string> input_errors;
3481
3482 bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
3483 UniValue result(UniValue::VOBJ);
3484 SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
3485 return result;
3486},
3487 };
3488}
3489
3490static RPCHelpMan bumpfee_helper(std::string method_name)
3491{
3492 const bool want_psbt = method_name == "psbtbumpfee";
3493 const std::string incremental_fee{CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE).ToString(FeeEstimateMode::SAT_VB)};
3494
3495 return RPCHelpMan{method_name,
3496 "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
3497 + std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
3498 "An opt-in RBF transaction with the given txid must be in the wallet.\n"
3499 "The command will pay the additional fee by reducing change outputs or adding inputs when necessary.\n"
3500 "It may add a new change output if one does not already exist.\n"
3501 "All inputs in the original transaction will be included in the replacement transaction.\n"
3502 "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
3503 "By default, the new fee will be calculated automatically using the estimatesmartfee RPC.\n"
3504 "The user can specify a confirmation target for estimatesmartfee.\n"
3505 "Alternatively, the user can specify a fee rate in " + CURRENCY_ATOM + "/vB for the new transaction.\n"
3506 "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
3507 "returned by getnetworkinfo) to enter the node's mempool.\n"
3508 "* WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB. *\n",
3509 {
3510 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
3511 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
3512 {
3513 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks\n"},
3514 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"},
3515 "\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n"
3516 "Must be at least " + incremental_fee + " higher than the current transaction fee rate.\n"
3517 "WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB.\n"},
3518 {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether the new transaction should still be\n"
3519 "marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
3520 "be left unchanged from the original. If false, any input sequence numbers in the\n"
3521 "original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
3522 "so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
3523 "still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
3524 "are replaceable).\n"},
3525 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
3526 "\"" + FeeModes("\"\n\"") + "\""},
3527 },
3528 "options"},
3529 },
3530 RPCResult{
3531 RPCResult::Type::OBJ, "", "", Cat(
3532 want_psbt ?
3533 std::vector<RPCResult>{{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."}} :
3534 std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction."}},
3535 {
3536 {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
3537 {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
3538 {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
3539 {
3540 {RPCResult::Type::STR, "", ""},
3541 }},
3542 })
3543 },
3544 RPCExamples{
3545 "\nBump the fee, get the new transaction\'s " + std::string(want_psbt ? "psbt" : "txid") + "\n" +
3546 HelpExampleCli(method_name, "<txid>")
3547 },
3548 [want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
3549{
3550 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
3551 if (!pwallet) return NullUniValue;
3552
3553 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
3554 throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
3555 }
3556
3557 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
3558 uint256 hash(ParseHashV(request.params[0], "txid"));
3559
3560 CCoinControl coin_control;
3561 coin_control.fAllowWatchOnly = pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
3562 // optional parameters
3563 coin_control.m_signal_bip125_rbf = true;
3564
3565 if (!request.params[1].isNull()) {
3566 UniValue options = request.params[1];
3567 RPCTypeCheckObj(options,
3568 {
3569 {"confTarget", UniValueType(UniValue::VNUM)},
3570 {"conf_target", UniValueType(UniValue::VNUM)},
3571 {"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
3572 {"replaceable", UniValueType(UniValue::VBOOL)},
3573 {"estimate_mode", UniValueType(UniValue::VSTR)},
3574 },
3575 true, true);
3576
3577 if (options.exists("confTarget") && options.exists("conf_target")) {
3578 throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and conf_target options should not both be set. Use conf_target (confTarget is deprecated).");
3579 }
3580
3581 auto conf_target = options.exists("confTarget") ? options["confTarget"] : options["conf_target"];
3582
3583 if (options.exists("replaceable")) {
3584 coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
3585 }
3586 SetFeeEstimateMode(*pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false);
3587 }
3588
3589 // Make sure the results are valid at least up to the most recent block
3590 // the user could have gotten from another RPC command prior to now
3591 pwallet->BlockUntilSyncedToCurrentChain();
3592
3593 LOCK(pwallet->cs_wallet);
3594
3595 EnsureWalletIsUnlocked(*pwallet);
3596
3597
3598 std::vector<bilingual_str> errors;
3599 CAmount old_fee;
3600 CAmount new_fee;
3601 CMutableTransaction mtx;
3602 feebumper::Result res;
3603 // Targeting feerate bump.
3604 res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
3605 if (res != feebumper::Result::OK) {
3606 switch(res) {
3607 case feebumper::Result::INVALID_ADDRESS_OR_KEY:
3608 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0].original);
3609 break;
3610 case feebumper::Result::INVALID_REQUEST:
3611 throw JSONRPCError(RPC_INVALID_REQUEST, errors[0].original);
3612 break;
3613 case feebumper::Result::INVALID_PARAMETER:
3614 throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0].original);
3615 break;
3616 case feebumper::Result::WALLET_ERROR:
3617 throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
3618 break;
3619 default:
3620 throw JSONRPCError(RPC_MISC_ERROR, errors[0].original);
3621 break;
3622 }
3623 }
3624
3625 UniValue result(UniValue::VOBJ);
3626
3627 // For bumpfee, return the new transaction id.
3628 // For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction.
3629 if (!want_psbt) {
3630 if (!feebumper::SignTransaction(*pwallet, mtx)) {
3631 throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
3632 }
3633
3634 uint256 txid;
3635 if (feebumper::CommitTransaction(*pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
3636 throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
3637 }
3638
3639 result.pushKV("txid", txid.GetHex());
3640 } else {
3641 PartiallySignedTransaction psbtx(mtx);
3642 bool complete = false;
3643 const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false /* sign */, true /* bip32derivs */);
3644 CHECK_NONFATAL(err == TransactionError::OK);
3645 CHECK_NONFATAL(!complete);
3646 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
3647 ssTx << psbtx;
3648 result.pushKV("psbt", EncodeBase64(ssTx.str()));
3649 }
3650
3651 result.pushKV("origfee", ValueFromAmount(old_fee));
3652 result.pushKV("fee", ValueFromAmount(new_fee));
3653 UniValue result_errors(UniValue::VARR);
3654 for (const bilingual_str& error : errors) {
3655 result_errors.push_back(error.original);
3656 }
3657 result.pushKV("errors", result_errors);
3658
3659 return result;
3660},
3661 };
3662}
3663
3664static RPCHelpMan bumpfee() { return bumpfee_helper("bumpfee"); }
3665static RPCHelpMan psbtbumpfee() { return bumpfee_helper("psbtbumpfee"); }
3666
3667static RPCHelpMan rescanblockchain()
3668{
3669 return RPCHelpMan{"rescanblockchain",
3670 "\nRescan the local blockchain for wallet related transactions.\n"
3671 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
3672 {
3673 {"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "block height where the rescan should start"},
3674 {"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
3675 },
3676 RPCResult{
3677 RPCResult::Type::OBJ, "", "",
3678 {
3679 {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"},
3680 {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."},
3681 }
3682 },
3683 RPCExamples{
3684 HelpExampleCli("rescanblockchain", "100000 120000")
3685 + HelpExampleRpc("rescanblockchain", "100000, 120000")
3686 },
3687 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
3688{
3689 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
3690 if (!pwallet) return NullUniValue;
3691
3692 WalletRescanReserver reserver(*pwallet);
3693 if (!reserver.reserve()) {
3694 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
3695 }
3696
3697 int start_height = 0;
3698 std::optional<int> stop_height;
3699 uint256 start_block;
3700 {
3701 LOCK(pwallet->cs_wallet);
3702 int tip_height = pwallet->GetLastBlockHeight();
3703
3704 if (!request.params[0].isNull()) {
3705 start_height = request.params[0].get_int();
3706 if (start_height < 0 || start_height > tip_height) {
3707 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
3708 }
3709 }
3710
3711 if (!request.params[1].isNull()) {
3712 stop_height = request.params[1].get_int();
3713 if (*stop_height < 0 || *stop_height > tip_height) {
3714 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
3715 } else if (*stop_height < start_height) {
3716 throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
3717 }
3718 }
3719
3720 // We can't rescan beyond non-pruned blocks, stop and throw an error
3721 if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(), start_height, stop_height)) {
3722 throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
3723 }
3724
3725 CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), start_height, FoundBlock().hash(start_block)));
3726 }
3727
3728 CWallet::ScanResult result =
3729 pwallet->ScanForWalletTransactions(start_block, start_height, stop_height, reserver, true /* fUpdate */);
3730 switch (result.status) {
3731 case CWallet::ScanResult::SUCCESS:
3732 break;
3733 case CWallet::ScanResult::FAILURE:
3734 throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
3735 case CWallet::ScanResult::USER_ABORT:
3736 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
3737 // no default case, so the compiler can warn about missing cases
3738 }
3739 UniValue response(UniValue::VOBJ);
3740 response.pushKV("start_height", start_height);
3741 response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
3742 return response;
3743},
3744 };
3745}
3746
3747class DescribeWalletAddressVisitor
3748{
3749public:
3750 const SigningProvider * const provider;
3751
3752 void ProcessSubScript(const CScript& subscript, UniValue& obj) const
3753 {
3754 // Always present: script type and redeemscript
3755 std::vector<std::vector<unsigned char>> solutions_data;
3756 TxoutType which_type = Solver(subscript, solutions_data);
3757 obj.pushKV("script", GetTxnOutputType(which_type));
3758 obj.pushKV("hex", HexStr(subscript));
3759
3760 CTxDestination embedded;
3761 if (ExtractDestination(subscript, embedded)) {
3762 // Only when the script corresponds to an address.
3763 UniValue subobj(UniValue::VOBJ);
3764 UniValue detail = DescribeAddress(embedded);
3765 subobj.pushKVs(detail);
3766 UniValue wallet_detail = std::visit(*this, embedded);
3767 subobj.pushKVs(wallet_detail);
3768 subobj.pushKV("address", EncodeDestination(embedded));
3769 subobj.pushKV("scriptPubKey", HexStr(subscript));
3770 // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
3771 if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
3772 obj.pushKV("embedded", std::move(subobj));
3773 } else if (which_type == TxoutType::MULTISIG) {
3774 // Also report some information on multisig scripts (which do not have a corresponding address).
3775 // TODO: abstract out the common functionality between this logic and ExtractDestinations.
3776 obj.pushKV("sigsrequired", solutions_data[0][0]);
3777 UniValue pubkeys(UniValue::VARR);
3778 for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
3779 CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
3780 pubkeys.push_back(HexStr(key));
3781 }
3782 obj.pushKV("pubkeys", std::move(pubkeys));
3783 }
3784 }
3785
3786 explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {}
3787
3788 UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
3789
3790 UniValue operator()(const PKHash& pkhash) const
3791 {
3792 CKeyID keyID{ToKeyID(pkhash)};
3793 UniValue obj(UniValue::VOBJ);
3794 CPubKey vchPubKey;
3795 if (provider && provider->GetPubKey(keyID, vchPubKey)) {
3796 obj.pushKV("pubkey", HexStr(vchPubKey));
3797 obj.pushKV("iscompressed", vchPubKey.IsCompressed());
3798 }
3799 return obj;
3800 }
3801
3802 UniValue operator()(const ScriptHash& scripthash) const
3803 {
3804 CScriptID scriptID(scripthash);
3805 UniValue obj(UniValue::VOBJ);
3806 CScript subscript;
3807 if (provider && provider->GetCScript(scriptID, subscript)) {
3808 ProcessSubScript(subscript, obj);
3809 }
3810 return obj;
3811 }
3812
3813 UniValue operator()(const WitnessV0KeyHash& id) const
3814 {
3815 UniValue obj(UniValue::VOBJ);
3816 CPubKey pubkey;
3817 if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
3818 obj.pushKV("pubkey", HexStr(pubkey));
3819 }
3820 return obj;
3821 }
3822
3823 UniValue operator()(const WitnessV0ScriptHash& id) const
3824 {
3825 UniValue obj(UniValue::VOBJ);
3826 CScript subscript;
3827 CRIPEMD160 hasher;
3828 uint160 hash;
3829 hasher.Write(id.begin(), 32).Finalize(hash.begin());
3830 if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
3831 ProcessSubScript(subscript, obj);
3832 }
3833 return obj;
3834 }
3835
3836 UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
3837 UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
3838};
3839
3840static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
3841{
3842 UniValue ret(UniValue::VOBJ);
3843 UniValue detail = DescribeAddress(dest);
3844 CScript script = GetScriptForDestination(dest);
3845 std::unique_ptr<SigningProvider> provider = nullptr;
3846 provider = wallet.GetSolvingProvider(script);
3847 ret.pushKVs(detail);
3848 ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
3849 return ret;
3850}
3851
3852/** Convert CAddressBookData to JSON record. */
3853static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool verbose)
3854{
3855 UniValue ret(UniValue::VOBJ);
3856 if (verbose) {
3857 ret.pushKV("name", data.GetLabel());
3858 }
3859 ret.pushKV("purpose", data.purpose);
3860 return ret;
3861}
3862
3863RPCHelpMan getaddressinfo()
3864{
3865 return RPCHelpMan{"getaddressinfo",
3866 "\nReturn information about the given bitcoin address.\n"
3867 "Some of the information will only be present if the address is in the active wallet.\n",
3868 {
3869 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
3870 },
3871 RPCResult{
3872 RPCResult::Type::OBJ, "", "",
3873 {
3874 {RPCResult::Type::STR, "address", "The bitcoin address validated."},
3875 {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
3876 {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
3877 {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
3878 {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
3879 {RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."},
3880 {RPCResult::Type::STR, "parent_desc", /* optional */ true, "The descriptor used to derive this address if this is a descriptor wallet"},
3881 {RPCResult::Type::BOOL, "isscript", "If the key is a script."},
3882 {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
3883 {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
3884 {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
3885 {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
3886 {RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
3887 "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
3888 "witness_v0_scripthash, witness_unknown."},
3889 {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
3890 {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
3891 {
3892 {RPCResult::Type::STR, "pubkey", ""},
3893 }},
3894 {RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."},
3895 {RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
3896 {RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
3897 {
3898 {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
3899 "and relation to the wallet (ismine, iswatchonly)."},
3900 }},
3901 {RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
3902 {RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
3903 {RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
3904 {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
3905 {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /* optional */ true, "The fingerprint of the master key."},
3906 {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
3907 "as an array to keep the API stable if multiple labels are enabled in the future.",
3908 {
3909 {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
3910 }},
3911 }
3912 },
3913 RPCExamples{
3914 HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
3915 HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
3916 },
3917 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
3918{
3919 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
3920 if (!pwallet) return NullUniValue;
3921
3922 LOCK(pwallet->cs_wallet);
3923
3924 std::string error_msg;
3925 CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
3926
3927 // Make sure the destination is valid
3928 if (!IsValidDestination(dest)) {
3929 // Set generic error message in case 'DecodeDestination' didn't set it
3930 if (error_msg.empty()) error_msg = "Invalid address";
3931
3932 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
3933 }
3934
3935 UniValue ret(UniValue::VOBJ);
3936
3937 std::string currentAddress = EncodeDestination(dest);
3938 ret.pushKV("address", currentAddress);
3939
3940 CScript scriptPubKey = GetScriptForDestination(dest);
3941 ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
3942
3943 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
3944
3945 isminetype mine = pwallet->IsMine(dest);
3946 ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
3947
3948 if (provider) {
3949 auto inferred = InferDescriptor(scriptPubKey, *provider);
3950 bool solvable = inferred->IsSolvable() || IsSolvable(*provider, scriptPubKey);
3951 ret.pushKV("solvable", solvable);
3952 if (solvable) {
3953 ret.pushKV("desc", inferred->ToString());
3954 }
3955 } else {
3956 ret.pushKV("solvable", false);
3957 }
3958
3959
3960 DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey));
3961 if (desc_spk_man) {
3962 std::string desc_str;
3963 if (desc_spk_man->GetDescriptorString(desc_str)) {
3964 ret.pushKV("parent_desc", desc_str);
3965 }
3966 }
3967
3968 ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
3969
3970 UniValue detail = DescribeWalletAddress(*pwallet, dest);
3971 ret.pushKVs(detail);
3972
3973 ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
3974
3975 ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
3976 if (spk_man) {
3977 if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
3978 ret.pushKV("timestamp", meta->nCreateTime);
3979 if (meta->has_key_origin) {
3980 ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
3981 ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
3982 ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
3983 }
3984 }
3985 }
3986
3987 // Return a `labels` array containing the label associated with the address,
3988 // equivalent to the `label` field above. Currently only one label can be
3989 // associated with an address, but we return an array so the API remains
3990 // stable if we allow multiple labels to be associated with an address in
3991 // the future.
3992 UniValue labels(UniValue::VARR);
3993 const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
3994 if (address_book_entry) {
3995 labels.push_back(address_book_entry->GetLabel());
3996 }
3997 ret.pushKV("labels", std::move(labels));
3998
3999 return ret;
4000},
4001 };
4002}
4003
4004static RPCHelpMan getaddressesbylabel()
4005{
4006 return RPCHelpMan{"getaddressesbylabel",
4007 "\nReturns the list of addresses assigned the specified label.\n",
4008 {
4009 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
4010 },
4011 RPCResult{
4012 RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
4013 {
4014 {RPCResult::Type::OBJ, "address", "json object with information about address",
4015 {
4016 {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
4017 }},
4018 }
4019 },
4020 RPCExamples{
4021 HelpExampleCli("getaddressesbylabel", "\"tabby\"")
4022 + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
4023 },
4024 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4025{
4026 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4027 if (!pwallet) return NullUniValue;
4028
4029 LOCK(pwallet->cs_wallet);
4030
4031 std::string label = LabelFromValue(request.params[0]);
4032
4033 // Find all addresses that have the given label
4034 UniValue ret(UniValue::VOBJ);
4035 std::set<std::string> addresses;
4036 for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->m_address_book) {
4037 if (item.second.IsChange()) continue;
4038 if (item.second.GetLabel() == label) {
4039 std::string address = EncodeDestination(item.first);
4040 // CWallet::m_address_book is not expected to contain duplicate
4041 // address strings, but build a separate set as a precaution just in
4042 // case it does.
4043 bool unique = addresses.emplace(address).second;
4044 CHECK_NONFATAL(unique);
4045 // UniValue::pushKV checks if the key exists in O(N)
4046 // and since duplicate addresses are unexpected (checked with
4047 // std::set in O(log(N))), UniValue::__pushKV is used instead,
4048 // which currently is O(1).
4049 ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
4050 }
4051 }
4052
4053 if (ret.empty()) {
4054 throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
4055 }
4056
4057 return ret;
4058},
4059 };
4060}
4061
4062static RPCHelpMan listlabels()
4063{
4064 return RPCHelpMan{"listlabels",
4065 "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
4066 {
4067 {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
4068 },
4069 RPCResult{
4070 RPCResult::Type::ARR, "", "",
4071 {
4072 {RPCResult::Type::STR, "label", "Label name"},
4073 }
4074 },
4075 RPCExamples{
4076 "\nList all labels\n"
4077 + HelpExampleCli("listlabels", "") +
4078 "\nList labels that have receiving addresses\n"
4079 + HelpExampleCli("listlabels", "receive") +
4080 "\nList labels that have sending addresses\n"
4081 + HelpExampleCli("listlabels", "send") +
4082 "\nAs a JSON-RPC call\n"
4083 + HelpExampleRpc("listlabels", "receive")
4084 },
4085 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4086{
4087 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4088 if (!pwallet) return NullUniValue;
4089
4090 LOCK(pwallet->cs_wallet);
4091
4092 std::string purpose;
4093 if (!request.params[0].isNull()) {
4094 purpose = request.params[0].get_str();
4095 }
4096
4097 // Add to a set to sort by label name, then insert into Univalue array
4098 std::set<std::string> label_set;
4099 for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->m_address_book) {
4100 if (entry.second.IsChange()) continue;
4101 if (purpose.empty() || entry.second.purpose == purpose) {
4102 label_set.insert(entry.second.GetLabel());
4103 }
4104 }
4105
4106 UniValue ret(UniValue::VARR);
4107 for (const std::string& name : label_set) {
4108 ret.push_back(name);
4109 }
4110
4111 return ret;
4112},
4113 };
4114}
4115
4116static RPCHelpMan send()
4117{
4118 return RPCHelpMan{"send",
4119 "\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
4120 "\nSend a transaction.\n",
4121 {
4122 {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
4123 "That is, each address can only appear once and there can only be one 'data' object.\n"
4124 "For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
4125 {
4126 {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
4127 {
4128 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
4129 },
4130 },
4131 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
4132 {
4133 {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
4134 },
4135 },
4136 },
4137 },
4138 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
4139 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
4140 " \"" + FeeModes("\"\n\"") + "\""},
4141 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
4142 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
4143 {
4144 {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
4145 {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
4146 "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
4147 "If that happens, you will need to fund the transaction with different inputs and republish it."},
4148 {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
4149 {"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
4150 {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
4151 {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
4152 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
4153 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
4154 " \"" + FeeModes("\"\n\"") + "\""},
4155 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
4156 {"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
4157 "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
4158 "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
4159 {"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
4160 {
4161 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
4162 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
4163 {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
4164 },
4165 },
4166 {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
4167 {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
4168 {"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
4169 {"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
4170 "The fee will be equally deducted from the amount of each specified output.\n"
4171 "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
4172 "If no outputs are specified here, the sender pays the fee.",
4173 {
4174 {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
4175 },
4176 },
4177 {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
4178 "Allows this transaction to be replaced by a transaction with higher fees"},
4179 },
4180 "options"},
4181 },
4182 RPCResult{
4183 RPCResult::Type::OBJ, "", "",
4184 {
4185 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
4186 {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
4187 {RPCResult::Type::STR_HEX, "hex", "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
4188 {RPCResult::Type::STR, "psbt", "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
4189 }
4190 },
4191 RPCExamples{""
4192 "\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode\n"
4193 + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 6 economical\n") +
4194 "Send 0.2 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB using positional arguments\n"
4195 + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" 1.1\n") +
4196 "Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using the options argument\n"
4197 + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" null '{\"fee_rate\": 1}'\n") +
4198 "Send 0.3 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
4199 + HelpExampleCli("-named send", "outputs='{\"" + EXAMPLE_ADDRESS[0] + "\": 0.3}' fee_rate=25\n") +
4200 "Create a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
4201 + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
4202 },
4203 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4204 {
4205 RPCTypeCheck(request.params, {
4206 UniValueType(), // outputs (ARR or OBJ, checked later)
4207 UniValue::VNUM, // conf_target
4208 UniValue::VSTR, // estimate_mode
4209 UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
4210 UniValue::VOBJ, // options
4211 }, true
4212 );
4213
4214 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4215 if (!pwallet) return NullUniValue;
4216
4217 UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
4218 if (options.exists("conf_target") || options.exists("estimate_mode")) {
4219 if (!request.params[1].isNull() || !request.params[2].isNull()) {
4220 throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both");
4221 }
4222 } else {
4223 options.pushKV("conf_target", request.params[1]);
4224 options.pushKV("estimate_mode", request.params[2]);
4225 }
4226 if (options.exists("fee_rate")) {
4227 if (!request.params[3].isNull()) {
4228 throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass the fee_rate either as an argument, or in the options object, but not both");
4229 }
4230 } else {
4231 options.pushKV("fee_rate", request.params[3]);
4232 }
4233 if (!options["conf_target"].isNull() && (options["estimate_mode"].isNull() || (options["estimate_mode"].get_str() == "unset"))) {
4234 throw JSONRPCError(RPC_INVALID_PARAMETER, "Specify estimate_mode");
4235 }
4236 if (options.exists("feeRate")) {
4237 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use fee_rate (" + CURRENCY_ATOM + "/vB) instead of feeRate");
4238 }
4239 if (options.exists("changeAddress")) {
4240 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address");
4241 }
4242 if (options.exists("changePosition")) {
4243 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position");
4244 }
4245 if (options.exists("includeWatching")) {
4246 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching");
4247 }
4248 if (options.exists("lockUnspents")) {
4249 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents");
4250 }
4251 if (options.exists("subtractFeeFromOutputs")) {
4252 throw JSONRPCError(RPC_INVALID_PARAMETER, "Use subtract_fee_from_outputs");
4253 }
4254
4255 const bool psbt_opt_in = options.exists("psbt") && options["psbt"].get_bool();
4256
4257 CAmount fee;
4258 int change_position;
4259 bool rbf = pwallet->m_signal_rbf;
4260 if (options.exists("replaceable")) {
4261 rbf = options["replaceable"].get_bool();
4262 }
4263 CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
4264 CCoinControl coin_control;
4265 // Automatically select coins, unless at least one is manually selected. Can
4266 // be overridden by options.add_inputs.
4267 coin_control.m_add_inputs = rawTx.vin.size() == 0;
4268 FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
4269
4270 bool add_to_wallet = true;
4271 if (options.exists("add_to_wallet")) {
4272 add_to_wallet = options["add_to_wallet"].get_bool();
4273 }
4274
4275 // Make a blank psbt
4276 PartiallySignedTransaction psbtx(rawTx);
4277
4278 // First fill transaction with our data without signing,
4279 // so external signers are not asked sign more than once.
4280 bool complete;
4281 pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false, true);
4282 const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, true, false);
4283 if (err != TransactionError::OK) {
4284 throw JSONRPCTransactionError(err);
4285 }
4286
4287 CMutableTransaction mtx;
4288 complete = FinalizeAndExtractPSBT(psbtx, mtx);
4289
4290 UniValue result(UniValue::VOBJ);
4291
4292 if (psbt_opt_in || !complete || !add_to_wallet) {
4293 // Serialize the PSBT
4294 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
4295 ssTx << psbtx;
4296 result.pushKV("psbt", EncodeBase64(ssTx.str()));
4297 }
4298
4299 if (complete) {
4300 std::string err_string;
4301 std::string hex = EncodeHexTx(CTransaction(mtx));
4302 CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
4303 result.pushKV("txid", tx->GetHash().GetHex());
4304 if (add_to_wallet && !psbt_opt_in) {
4305 pwallet->CommitTransaction(tx, {}, {} /* orderForm */);
4306 } else {
4307 result.pushKV("hex", hex);
4308 }
4309 }
4310 result.pushKV("complete", complete);
4311
4312 return result;
4313 }
4314 };
4315}
4316
4317static RPCHelpMan sethdseed()
4318{
4319 return RPCHelpMan{"sethdseed",
4320 "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
4321 "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
4322 "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
4323 HELP_REQUIRING_PASSPHRASE,
4324 {
4325 {"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
4326 "If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
4327 "If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
4328 "keypool will be used until it has been depleted."},
4329 {"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
4330 "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
4331 },
4332 RPCResult{RPCResult::Type::NONE, "", ""},
4333 RPCExamples{
4334 HelpExampleCli("sethdseed", "")
4335 + HelpExampleCli("sethdseed", "false")
4336 + HelpExampleCli("sethdseed", "true \"wifkey\"")
4337 + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
4338 },
4339 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4340{
4341 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4342 if (!pwallet) return NullUniValue;
4343
4344 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
4345
4346 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
4347 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
4348 }
4349
4350 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
4351
4352 // Do not do anything to non-HD wallets
4353 if (!pwallet->CanSupportFeature(FEATURE_HD)) {
4354 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
4355 }
4356
4357 EnsureWalletIsUnlocked(*pwallet);
4358
4359 bool flush_key_pool = true;
4360 if (!request.params[0].isNull()) {
4361 flush_key_pool = request.params[0].get_bool();
4362 }
4363
4364 CPubKey master_pub_key;
4365 if (request.params[1].isNull()) {
4366 master_pub_key = spk_man.GenerateNewSeed();
4367 } else {
4368 CKey key = DecodeSecret(request.params[1].get_str());
4369 if (!key.IsValid()) {
4370 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
4371 }
4372
4373 if (HaveKey(spk_man, key)) {
4374 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
4375 }
4376
4377 master_pub_key = spk_man.DeriveNewSeed(key);
4378 }
4379
4380 spk_man.SetHDSeed(master_pub_key);
4381 if (flush_key_pool) spk_man.NewKeyPool();
4382
4383 return NullUniValue;
4384},
4385 };
4386}
4387
4388static RPCHelpMan walletprocesspsbt()
4389{
4390 return RPCHelpMan{"walletprocesspsbt",
4391 "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
4392 "that we can sign for." +
4393 HELP_REQUIRING_PASSPHRASE,
4394 {
4395 {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
4396 {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
4397 {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
4398 " \"DEFAULT\"\n"
4399 " \"ALL\"\n"
4400 " \"NONE\"\n"
4401 " \"SINGLE\"\n"
4402 " \"ALL|ANYONECANPAY\"\n"
4403 " \"NONE|ANYONECANPAY\"\n"
4404 " \"SINGLE|ANYONECANPAY\""},
4405 {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
4406 },
4407 RPCResult{
4408 RPCResult::Type::OBJ, "", "",
4409 {
4410 {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
4411 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
4412 }
4413 },
4414 RPCExamples{
4415 HelpExampleCli("walletprocesspsbt", "\"psbt\"")
4416 },
4417 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4418{
4419 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4420 if (!pwallet) return NullUniValue;
4421
4422 const CWallet& wallet{*pwallet};
4423 // Make sure the results are valid at least up to the most recent block
4424 // the user could have gotten from another RPC command prior to now
4425 wallet.BlockUntilSyncedToCurrentChain();
4426
4427 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
4428
4429 // Unserialize the transaction
4430 PartiallySignedTransaction psbtx;
4431 std::string error;
4432 if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
4433 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
4434 }
4435
4436 // Get the sighash type
4437 int nHashType = ParseSighashString(request.params[2]);
4438
4439 // Fill transaction with our data and also sign
4440 bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
4441 bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
4442 bool complete = true;
4443 const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
4444 if (err != TransactionError::OK) {
4445 throw JSONRPCTransactionError(err);
4446 }
4447
4448 UniValue result(UniValue::VOBJ);
4449 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
4450 ssTx << psbtx;
4451 result.pushKV("psbt", EncodeBase64(ssTx.str()));
4452 result.pushKV("complete", complete);
4453
4454 return result;
4455},
4456 };
4457}
4458
4459static RPCHelpMan walletcreatefundedpsbt()
4460{
4461 return RPCHelpMan{"walletcreatefundedpsbt",
4462 "\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
4463 "Implements the Creator and Updater roles.\n",
4464 {
4465 {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
4466 {
4467 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
4468 {
4469 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
4470 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
4471 {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"},
4472 },
4473 },
4474 },
4475 },
4476 {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
4477 "That is, each address can only appear once and there can only be one 'data' object.\n"
4478 "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
4479 "accepted as second parameter.",
4480 {
4481 {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
4482 {
4483 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
4484 },
4485 },
4486 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
4487 {
4488 {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
4489 },
4490 },
4491 },
4492 },
4493 {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
4494 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
4495 {
4496 {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
4497 {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
4498 "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
4499 "If that happens, you will need to fund the transaction with different inputs and republish it."},
4500 {"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
4501 {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
4502 {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
4503 {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
4504 {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
4505 {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
4506 {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
4507 {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The outputs to subtract the fee from.\n"
4508 "The fee will be equally deducted from the amount of each specified output.\n"
4509 "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
4510 "If no outputs are specified here, the sender pays the fee.",
4511 {
4512 {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
4513 },
4514 },
4515 {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
4516 "Allows this transaction to be replaced by a transaction with higher fees"},
4517 {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
4518 {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
4519 " \"" + FeeModes("\"\n\"") + "\""},
4520 },
4521 "options"},
4522 {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
4523 },
4524 RPCResult{
4525 RPCResult::Type::OBJ, "", "",
4526 {
4527 {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
4528 {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
4529 {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
4530 }
4531 },
4532 RPCExamples{
4533 "\nCreate a transaction with no inputs\n"
4534 + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
4535 },
4536 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4537{
4538 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4539 if (!pwallet) return NullUniValue;
4540
4541 CWallet& wallet{*pwallet};
4542 // Make sure the results are valid at least up to the most recent block
4543 // the user could have gotten from another RPC command prior to now
4544 wallet.BlockUntilSyncedToCurrentChain();
4545
4546 RPCTypeCheck(request.params, {
4547 UniValue::VARR,
4548 UniValueType(), // ARR or OBJ, checked later
4549 UniValue::VNUM,
4550 UniValue::VOBJ,
4551 UniValue::VBOOL
4552 }, true
4553 );
4554
4555 CAmount fee;
4556 int change_position;
4557 bool rbf{wallet.m_signal_rbf};
4558 const UniValue &replaceable_arg = request.params[3]["replaceable"];
4559 if (!replaceable_arg.isNull()) {
4560 RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
4561 rbf = replaceable_arg.isTrue();
4562 }
4563 CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
4564 CCoinControl coin_control;
4565 // Automatically select coins, unless at least one is manually selected. Can
4566 // be overridden by options.add_inputs.
4567 coin_control.m_add_inputs = rawTx.vin.size() == 0;
4568 FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
4569
4570 // Make a blank psbt
4571 PartiallySignedTransaction psbtx(rawTx);
4572
4573 // Fill transaction with out data but don't sign
4574 bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
4575 bool complete = true;
4576 const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
4577 if (err != TransactionError::OK) {
4578 throw JSONRPCTransactionError(err);
4579 }
4580
4581 // Serialize the PSBT
4582 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
4583 ssTx << psbtx;
4584
4585 UniValue result(UniValue::VOBJ);
4586 result.pushKV("psbt", EncodeBase64(ssTx.str()));
4587 result.pushKV("fee", ValueFromAmount(fee));
4588 result.pushKV("changepos", change_position);
4589 return result;
4590},
4591 };
4592}
4593
4594static RPCHelpMan upgradewallet()
4595{
4596 return RPCHelpMan{"upgradewallet",
4597 "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
4598 "New keys may be generated and a new wallet backup will need to be made.",
4599 {
4600 {"version", RPCArg::Type::NUM, RPCArg::Default{FEATURE_LATEST}, "The version number to upgrade to. Default is the latest wallet version."}
4601 },
4602 RPCResult{
4603 RPCResult::Type::OBJ, "", "",
4604 {
4605 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
4606 {RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"},
4607 {RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"},
4608 {RPCResult::Type::STR, "result", /* optional */ true, "Description of result, if no error"},
4609 {RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"}
4610 },
4611 },
4612 RPCExamples{
4613 HelpExampleCli("upgradewallet", "169900")
4614 + HelpExampleRpc("upgradewallet", "169900")
4615 },
4616 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4617{
4618 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
4619 if (!pwallet) return NullUniValue;
4620
4621 RPCTypeCheck(request.params, {UniValue::VNUM}, true);
4622
4623 EnsureWalletIsUnlocked(*pwallet);
4624
4625 int version = 0;
4626 if (!request.params[0].isNull()) {
4627 version = request.params[0].get_int();
4628 }
4629 bilingual_str error;
4630 const int previous_version{pwallet->GetVersion()};
4631 const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)};
4632 const int current_version{pwallet->GetVersion()};
4633 std::string result;
4634
4635 if (wallet_upgraded) {
4636 if (previous_version == current_version) {
4637 result = "Already at latest version. Wallet version unchanged.";
4638 } else {
4639 result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version);
4640 }
4641 }
4642
4643 UniValue obj(UniValue::VOBJ);
4644 obj.pushKV("wallet_name", pwallet->GetName());
4645 obj.pushKV("previous_version", previous_version);
4646 obj.pushKV("current_version", current_version);
4647 if (!result.empty()) {
4648 obj.pushKV("result", result);
4649 } else {
4650 CHECK_NONFATAL(!error.empty());
4651 obj.pushKV("error", error.original);
4652 }
4653 return obj;
4654},
4655 };
4656}
4657
4658#ifdef ENABLE_EXTERNAL_SIGNER
4659static RPCHelpMan walletdisplayaddress()
4660{
4661 return RPCHelpMan{"walletdisplayaddress",
4662 "Display address on an external signer for verification.",
4663 {
4664 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"},
4665 },
4666 RPCResult{
4667 RPCResult::Type::OBJ,"","",
4668 {
4669 {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
4670 }
4671 },
4672 RPCExamples{""},
4673 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
4674 {
4675 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4676 if (!wallet) return NullUniValue;
4677 CWallet* const pwallet = wallet.get();
4678
4679 LOCK(pwallet->cs_wallet);
4680
4681 CTxDestination dest = DecodeDestination(request.params[0].get_str());
4682
4683 // Make sure the destination is valid
4684 if (!IsValidDestination(dest)) {
4685 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
4686 }
4687
4688 if (!pwallet->DisplayAddress(dest)) {
4689 throw JSONRPCError(RPC_MISC_ERROR, "Failed to display address");
4690 }
4691
4692 UniValue result(UniValue::VOBJ);
4693 result.pushKV("address", request.params[0].get_str());
4694 return result;
4695 }
4696 };
4697}
4698#endif // ENABLE_EXTERNAL_SIGNER
4699
4700RPCHelpMan abortrescan();
4701RPCHelpMan dumpprivkey();
4702RPCHelpMan importprivkey();
4703RPCHelpMan importaddress();
4704RPCHelpMan importpubkey();
4705RPCHelpMan dumpwallet();
4706RPCHelpMan importwallet();
4707RPCHelpMan importprunedfunds();
4708RPCHelpMan removeprunedfunds();
4709RPCHelpMan importmulti();
4710RPCHelpMan importdescriptors();
4711RPCHelpMan listdescriptors();
4712
4713Span<const CRPCCommand> GetWalletRPCCommands()
4714{
4715// clang-format off
4716static const CRPCCommand commands[] =
4717{ // category actor (function)
4718 // ------------------ ------------------------
4719 { "rawtransactions", &fundrawtransaction, },
4720 { "wallet", &abandontransaction, },
4721 { "wallet", &abortrescan, },
4722 { "wallet", &addmultisigaddress, },
4723 { "wallet", &backupwallet, },
4724 { "wallet", &bumpfee, },
4725 { "wallet", &psbtbumpfee, },
4726 { "wallet", &createwallet, },
4727 { "wallet", &dumpprivkey, },
4728 { "wallet", &dumpwallet, },
4729 { "wallet", &encryptwallet, },
4730 { "wallet", &getaddressesbylabel, },
4731 { "wallet", &getaddressinfo, },
4732 { "wallet", &getbalance, },
4733 { "wallet", &getnewaddress, },
4734 { "wallet", &getrawchangeaddress, },
4735 { "wallet", &getreceivedbyaddress, },
4736 { "wallet", &getreceivedbylabel, },
4737 { "wallet", &gettransaction, },
4738 { "wallet", &getunconfirmedbalance, },
4739 { "wallet", &getbalances, },
4740 { "wallet", &getwalletinfo, },
4741 { "wallet", &importaddress, },
4742 { "wallet", &importdescriptors, },
4743 { "wallet", &importmulti, },
4744 { "wallet", &importprivkey, },
4745 { "wallet", &importprunedfunds, },
4746 { "wallet", &importpubkey, },
4747 { "wallet", &importwallet, },
4748 { "wallet", &keypoolrefill, },
4749 { "wallet", &listaddressgroupings, },
4750 { "wallet", &listdescriptors, },
4751 { "wallet", &listlabels, },
4752 { "wallet", &listlockunspent, },
4753 { "wallet", &listreceivedbyaddress, },
4754 { "wallet", &listreceivedbylabel, },
4755 { "wallet", &listsinceblock, },
4756 { "wallet", &listtransactions, },
4757 { "wallet", &listunspent, },
4758 { "wallet", &listwalletdir, },
4759 { "wallet", &listwallets, },
4760 { "wallet", &loadwallet, },
4761 { "wallet", &lockunspent, },
4762 { "wallet", &removeprunedfunds, },
4763 { "wallet", &rescanblockchain, },
4764 { "wallet", &send, },
4765 { "wallet", &sendmany, },
4766 { "wallet", &sendtoaddress, },
4767 { "wallet", &sethdseed, },
4768 { "wallet", &setlabel, },
4769 { "wallet", &settxfee, },
4770 { "wallet", &setwalletflag, },
4771 { "wallet", &signmessage, },
4772 { "wallet", &signrawtransactionwithwallet, },
4773 { "wallet", &unloadwallet, },
4774 { "wallet", &upgradewallet, },
4775 { "wallet", &walletcreatefundedpsbt, },
4776 { "wallet", &walletestimatefee, },
4777#ifdef ENABLE_EXTERNAL_SIGNER
4778 { "wallet", &walletdisplayaddress, },
4779#endif // ENABLE_EXTERNAL_SIGNER
4780 { "wallet", &walletlock, },
4781 { "wallet", &walletpassphrase, },
4782 { "wallet", &walletpassphrasechange, },
4783 { "wallet", &walletprocesspsbt, },
4784};
4785// clang-format on
4786 return MakeSpan(commands);
4787}
4788