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