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