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