Bitcoin Core 31.99.0
P2P Digital Currency
backup.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <chain.h>
6#include <clientversion.h>
7#include <core_io.h>
8#include <hash.h>
9#include <interfaces/chain.h>
10#include <key_io.h>
11#include <merkleblock.h>
12#include <node/types.h>
13#include <rpc/util.h>
14#include <script/descriptor.h>
15#include <script/script.h>
16#include <script/solver.h>
17#include <sync.h>
18#include <uint256.h>
19#include <util/bip32.h>
20#include <util/check.h>
21#include <util/fs.h>
22#include <util/time.h>
23#include <util/translation.h>
24#include <wallet/export.h>
25#include <wallet/rpc/util.h>
26#include <wallet/wallet.h>
27
28#include <cstdint>
29#include <fstream>
30#include <tuple>
31#include <string>
32
33#include <univalue.h>
34
35
36
38
39namespace wallet {
41{
42 return RPCMethod{
43 "importprunedfunds",
44 "Imports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
45 {
46 {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
47 {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
48 },
50 RPCExamples{""},
51 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
52{
53 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
54 if (!pwallet) return UniValue::VNULL;
55
57 if (!DecodeHexTx(tx, request.params[0].get_str())) {
58 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
59 }
60
61 CMerkleBlock merkleBlock;
62 SpanReader{ParseHexV(request.params[1], "proof")} >> merkleBlock;
63
64 //Search partial merkle tree in proof for our transaction and index in valid block
65 std::vector<Txid> vMatch;
66 std::vector<unsigned int> vIndex;
67 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
68 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
69 }
70
71 LOCK(pwallet->cs_wallet);
72 int height;
73 if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
74 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
75 }
76
77 std::vector<Txid>::const_iterator it;
78 if ((it = std::find(vMatch.begin(), vMatch.end(), tx.GetHash())) == vMatch.end()) {
79 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
80 }
81
82 unsigned int txnIndex = vIndex[it - vMatch.begin()];
83
85 if (pwallet->IsMine(*tx_ref)) {
86 pwallet->AddToWallet(std::move(tx_ref), TxStateConfirmed{merkleBlock.header.GetHash(), height, static_cast<int>(txnIndex)});
87 return UniValue::VNULL;
88 }
89
90 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
91},
92 };
93}
94
96{
97 return RPCMethod{
98 "removeprunedfunds",
99 "(DEPRECATED) This feature will be removed in the next major release. Start bitcoind with the `-deprecatedrpc=removeprunedfunds` option in order to use this.\n"
100 "Deletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
101 {
102 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
103 },
106 HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
107 "\nAs a JSON-RPC call\n"
108 + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
109 },
110 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
111{
112 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
113 if (!pwallet) return UniValue::VNULL;
114
115 if (!pwallet->chain().rpcEnableDeprecated("removeprunedfunds")) {
116 throw JSONRPCError(RPC_METHOD_DEPRECATED, "DEPRECATION WARNING: This feature will be removed in the next major release. Start bitcoind with the `-deprecatedrpc=removeprunedfunds` option in order to use this.");
117 }
118
119 LOCK(pwallet->cs_wallet);
120
121 Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
122 std::vector<Txid> vHash;
123 vHash.push_back(hash);
124 if (auto res = pwallet->RemoveTxs(vHash); !res) {
125 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
126 }
127
128 return UniValue::VNULL;
129},
130 };
131}
132
133static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
134{
135 if (data.exists("timestamp")) {
136 const UniValue& timestamp = data["timestamp"];
137 if (timestamp.isNum()) {
138 return timestamp.getInt<int64_t>();
139 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
140 return now;
141 }
142 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
143 }
144 throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
145}
146
147static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
148{
149 UniValue warnings(UniValue::VARR);
150 UniValue result(UniValue::VOBJ);
151
152 try {
153 if (!data.exists("desc")) {
154 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");
155 }
156
157 const std::string& descriptor = data["desc"].get_str();
158 const bool active = data.exists("active") ? data["active"].get_bool() : false;
159 const std::string label{LabelFromValue(data["label"])};
160
161 // Parse descriptor string
163 std::string error;
164 auto parsed_descs = Parse(descriptor, keys, error, /* require_checksum = */ true);
165 if (parsed_descs.empty()) {
167 }
168 std::optional<bool> internal;
169 if (data.exists("internal")) {
170 if (parsed_descs.size() > 1) {
171 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'");
172 }
173 internal = data["internal"].get_bool();
174 }
175
176 // Range check
177 std::optional<bool> is_ranged;
178 int64_t range_start = 0, range_end = 1, next_index = 0;
179 if (!parsed_descs.at(0)->IsRange() && data.exists("range")) {
180 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
181 } else if (parsed_descs.at(0)->IsRange()) {
182 if (data.exists("range")) {
183 auto range = ParseDescriptorRange(data["range"]);
184 range_start = range.first;
185 range_end = range.second + 1; // Specified range end is inclusive, but we need range end as exclusive
186 } else {
187 warnings.push_back("Range not given, using default keypool range");
188 range_start = 0;
189 range_end = wallet.m_keypool_size;
190 }
191 next_index = range_start;
192 is_ranged = true;
193
194 if (data.exists("next_index")) {
195 next_index = data["next_index"].getInt<int64_t>();
196 // bound checks
197 if (next_index < range_start || next_index >= range_end) {
198 throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");
199 }
200 }
201 }
202
203 // Active descriptors must be ranged
204 if (active && !parsed_descs.at(0)->IsRange()) {
205 throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");
206 }
207
208 // Multipath descriptors should not have a label
209 if (parsed_descs.size() > 1 && data.exists("label")) {
210 throw JSONRPCError(RPC_INVALID_PARAMETER, "Multipath descriptors should not have a label");
211 }
212
213 // Ranged descriptors should not have a label
214 if (is_ranged.has_value() && is_ranged.value() && data.exists("label")) {
215 throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
216 }
217
218 bool desc_internal = internal.has_value() && internal.value();
219 // Internal addresses should not have a label either
220 if (desc_internal && data.exists("label")) {
221 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
222 }
223
224 // Combo descriptor check
225 if (active && !parsed_descs.at(0)->IsSingleType()) {
226 throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
227 }
228
229 // If the wallet disabled private keys, abort if private keys exist
230 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
231 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
232 }
233
234 for (size_t j = 0; j < parsed_descs.size(); ++j) {
235 auto parsed_desc = std::move(parsed_descs[j]);
236 if (parsed_descs.size() == 2) {
237 desc_internal = j == 1;
238 } else if (parsed_descs.size() > 2) {
239 CHECK_NONFATAL(!desc_internal);
240 }
241 // Expand to check whether the descriptor can be derived at the first index.
242 FlatSigningProvider expand_keys;
243 std::vector<CScript> scripts;
244 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
245 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
246 }
247
248 for (const auto& w : parsed_desc->Warnings()) {
249 warnings.push_back(w);
250 }
251
252 // If private keys are enabled, check some things.
253 if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
254 if (keys.keys.empty()) {
255 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
256 }
257 if (!parsed_desc->HavePrivateKeys(keys)) {
258 warnings.push_back("Not all private keys provided. Some wallet functionality may return unexpected errors");
259 }
260 }
261
262 // If this is an unused(KEY) descriptor, check that the wallet doesn't already have other descriptors with this key
263 if (!parsed_desc->HasScripts()) {
264 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
265 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import unused() to wallet without private keys enabled");
266 }
267 // Unused descriptors must contain a single key.
268 // Earlier checks will have enforced that this key is either a private key when private keys are enabled,
269 // or that this key is a public key when private keys are disabled.
270 // If we can retrieve the corresponding private key from the wallet, then this key is already in the wallet
271 // and we should not import it.
272 std::set<CPubKey> pubkeys;
273 std::set<CExtPubKey> extpubs;
274 parsed_desc->GetPubKeys(pubkeys, extpubs);
275 std::transform(extpubs.begin(), extpubs.end(), std::inserter(pubkeys, pubkeys.begin()), [](const CExtPubKey& xpub) { return xpub.pubkey; });
276 CHECK_NONFATAL(pubkeys.size() == 1);
277 if (wallet.GetKey(pubkeys.begin()->GetID())) {
278 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import an unused() descriptor when its private key is already in the wallet");
279 }
280 }
281
282 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
283
284 // Add descriptor to the wallet
285 auto spk_manager_res = wallet.AddWalletDescriptor(w_desc, keys, label, desc_internal);
286
287 if (!spk_manager_res) {
288 throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s': %s", descriptor, util::ErrorString(spk_manager_res).original));
289 }
290
291 auto& spk_manager = spk_manager_res.value().get();
292
293 // Set descriptor as active if necessary
294 if (active) {
295 if (!w_desc.descriptor->GetOutputType()) {
296 warnings.push_back("Unknown output type, cannot set descriptor to active.");
297 } else {
298 wallet.AddActiveScriptPubKeyMan(spk_manager.GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
299 }
300 } else {
301 if (w_desc.descriptor->GetOutputType()) {
302 wallet.DeactivateScriptPubKeyMan(spk_manager.GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
303 }
304 }
305 }
306
307 result.pushKV("success", UniValue(true));
308 } catch (const UniValue& e) {
309 result.pushKV("success", UniValue(false));
310 result.pushKV("error", e);
311 }
312 PushWarnings(warnings, result);
313 return result;
314}
315
317{
318 return RPCMethod{
319 "importdescriptors",
320 "Import descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
321 "When importing descriptors with multipath key expressions, if the multipath specifier contains exactly two elements, the descriptor produced from the second element will be imported as an internal descriptor.\n"
322 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
323 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
324 "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
325 {
326 {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
327 {
329 {
330 {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
331 {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
332 {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
333 {"next_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "If a ranged descriptor is set to active, this specifies the next index to generate addresses from"},
334 {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
335 "Use the string \"now\" to substitute the current synced blockchain time.\n"
336 "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
337 "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
338 "of all descriptors being imported will be scanned as well as the mempool.",
339 RPCArgOptions{.type_str={"timestamp | \"now\"", "integer / string"}}
340 },
341 {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
342 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"},
343 },
344 },
345 },
347 },
348 RPCResult{
349 RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
350 {
351 {RPCResult::Type::OBJ, "", "",
352 {
353 {RPCResult::Type::BOOL, "success", ""},
354 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "",
355 {
356 {RPCResult::Type::STR, "", ""},
357 }},
358 {RPCResult::Type::OBJ, "error", /*optional=*/true, "",
359 {
360 {RPCResult::Type::NUM, "code", "JSONRPC error code"},
361 {RPCResult::Type::STR, "message", "JSONRPC error message"},
362 }},
363 }},
364 }
365 },
367 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
368 "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
369 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
370 },
371 [](const RPCMethod& self, const JSONRPCRequest& main_request) -> UniValue
372{
373 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
374 if (!pwallet) return UniValue::VNULL;
375 CWallet& wallet{*pwallet};
376
377 WalletRescanReserver reserver(*pwallet);
378 if (!reserver.reserve(/*with_passphrase=*/true)) {
379 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
380 }
381
382 // Make sure the results are valid at least up to the most recent block
383 // the user could have gotten from another RPC command prior to now
384 wallet.BlockUntilSyncedToCurrentChain();
385
386 // Ensure that the wallet is not locked for the remainder of this RPC, as
387 // the passphrase is used to top up the keypool.
388 LOCK(pwallet->m_relock_mutex);
389
390 const UniValue& requests = main_request.params[0];
391 const int64_t minimum_timestamp = 1;
392 int64_t now = 0;
393 int64_t lowest_timestamp = 0;
394 bool rescan = false;
395 UniValue response(UniValue::VARR);
396 {
397 LOCK(pwallet->cs_wallet);
398 EnsureWalletIsUnlocked(*pwallet);
399
400 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
401
402 // Get all timestamps and extract the lowest timestamp
403 for (const UniValue& request : requests.getValues()) {
404 // This throws an error if "timestamp" doesn't exist
405 const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
406 const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
407 response.push_back(result);
408
409 if (lowest_timestamp > timestamp ) {
410 lowest_timestamp = timestamp;
411 }
412
413 // If we know the chain tip, and at least one request was successful then allow rescan
414 if (!rescan && result["success"].get_bool()) {
415 rescan = true;
416 }
417 }
418 pwallet->ConnectScriptPubKeyManNotifiers();
419 pwallet->RefreshAllTXOs();
420 }
421
422 // Rescan the blockchain using the lowest timestamp
423 if (rescan) {
424 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver);
425 pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
426
427 if (pwallet->IsAbortingRescan()) {
428 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
429 }
430
431 if (scanned_time > lowest_timestamp) {
432 std::vector<UniValue> results = response.getValues();
433 response.clear();
434 response.setArray();
435
436 // Compose the response
437 for (unsigned int i = 0; i < requests.size(); ++i) {
438 const UniValue& request = requests.getValues().at(i);
439
440 // If the descriptor timestamp is within the successfully scanned
441 // range, or if the import result already has an error set, let
442 // the result stand unmodified. Otherwise replace the result
443 // with an error message.
444 if (scanned_time <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
445 response.push_back(results.at(i));
446 } else {
447 std::string error_msg{strprintf("Rescan failed for descriptor with timestamp %d. There "
448 "was an error reading a block from time %d, which is after or within %d seconds "
449 "of key creation, and could contain transactions pertaining to the desc. As a "
450 "result, transactions and coins using this desc may not appear in the wallet.",
451 GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)};
452 if (pwallet->chain().havePruned()) {
453 error_msg += strprintf(" This error could be caused by pruning or data corruption "
454 "(see bitcoind log for details) and could be dealt with by downloading and "
455 "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
456 } else if (pwallet->chain().hasAssumedValidChain()) {
457 error_msg += strprintf(" This error is likely caused by an in-progress assumeutxo "
458 "background sync. Check logs or getchainstates RPC for assumeutxo background "
459 "sync progress and try again later.");
460 } else {
461 error_msg += strprintf(" This error could potentially caused by data corruption. If "
462 "the issue persists you may want to reindex (see -reindex option).");
463 }
464
466 result.pushKV("success", UniValue(false));
467 result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, error_msg));
468 response.push_back(std::move(result));
469 }
470 }
471 }
472 }
473
474 return response;
475},
476 };
477}
478
480{
481 return RPCMethod{
482 "listdescriptors",
483 "List all descriptors present in a wallet.\n",
484 {
485 {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private descriptors."}
486 },
488 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
489 {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects (sorted by descriptor string representation)",
490 {
491 {RPCResult::Type::OBJ, "", "", {
492 {RPCResult::Type::STR, "desc", "Descriptor string representation"},
493 {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
494 {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
495 {RPCResult::Type::BOOL, "internal", /*optional=*/true, "True if this descriptor is used to generate change addresses. False if this descriptor is used to generate receiving addresses; defined only for active descriptors"},
496 {RPCResult::Type::ARR_FIXED, "range", /*optional=*/true, "Defined only for ranged descriptors", {
497 {RPCResult::Type::NUM, "", "Range start inclusive"},
498 {RPCResult::Type::NUM, "", "Range end inclusive"},
499 }},
500 {RPCResult::Type::NUM, "next", /*optional=*/true, "Same as next_index field. Kept for compatibility reason."},
501 {RPCResult::Type::NUM, "next_index", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
502 }},
503 }}
504 }},
506 HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
507 + HelpExampleCli("listdescriptors", "true") + HelpExampleRpc("listdescriptors", "true")
508 },
509 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
510{
511 const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
512 if (!wallet) return UniValue::VNULL;
513
514 const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
515 if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && priv) {
516 throw JSONRPCError(RPC_WALLET_ERROR, "Can't get private descriptor string for watch-only wallets");
517 }
518 if (priv) {
520 }
521
522 LOCK(wallet->cs_wallet);
524 if (!exported) {
525 throw JSONRPCError(RPC_WALLET_ERROR, exported.error());
526 }
527 std::vector<WalletDescInfo> wallet_descriptors = *exported;
528
529 std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](const auto& a, const auto& b) {
530 return a.descriptor < b.descriptor;
531 });
532
533 UniValue descriptors(UniValue::VARR);
534 for (const WalletDescInfo& info : wallet_descriptors) {
536 spk.pushKV("desc", info.descriptor);
537 spk.pushKV("timestamp", info.creation_time);
538 spk.pushKV("active", info.active);
539 if (info.internal.has_value()) {
540 spk.pushKV("internal", info.internal.value());
541 }
542 if (info.range.has_value()) {
544 range.push_back(info.range->first);
545 range.push_back(info.range->second - 1);
546 spk.pushKV("range", std::move(range));
547 spk.pushKV("next", info.next_index);
548 spk.pushKV("next_index", info.next_index);
549 }
550 descriptors.push_back(std::move(spk));
551 }
552
553 UniValue response(UniValue::VOBJ);
554 response.pushKV("wallet_name", wallet->GetName());
555 response.pushKV("descriptors", std::move(descriptors));
556
557 return response;
558},
559 };
560}
561
563{
564 return RPCMethod{
565 "backupwallet",
566 "Safely copies the current wallet file to the specified destination, which can either be a directory or a path with a filename.\n",
567 {
568 {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
569 },
572 HelpExampleCli("backupwallet", "\"backup.dat\"")
573 + HelpExampleRpc("backupwallet", "\"backup.dat\"")
574 },
575 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
576{
577 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
578 if (!pwallet) return UniValue::VNULL;
579
580 // Make sure the results are valid at least up to the most recent block
581 // the user could have gotten from another RPC command prior to now
582 pwallet->BlockUntilSyncedToCurrentChain();
583
584 LOCK(pwallet->cs_wallet);
585
586 std::string strDest = request.params[0].get_str();
587 if (!pwallet->BackupWallet(strDest)) {
588 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
589 }
590
591 return UniValue::VNULL;
592},
593 };
594}
595
596
598{
599 return RPCMethod{
600 "restorewallet",
601 "Restores and loads a wallet from backup.\n"
602 "\nThe rescan is significantly faster if block filters are available"
603 "\n(using startup option \"-blockfilterindex=1\").\n",
604 {
605 {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"},
606 {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."},
607 {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
608 },
609 RPCResult{
610 RPCResult::Type::OBJ, "", "",
611 {
612 {RPCResult::Type::STR, "name", "The wallet name if restored successfully."},
613 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to restoring and loading the wallet.",
614 {
615 {RPCResult::Type::STR, "", ""},
616 }},
617 }
618 },
620 HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"")
621 + HelpExampleRpc("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"")
622 + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}})
623 + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}})
624 },
625 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
626{
627
628 WalletContext& context = EnsureWalletContext(request.context);
629
630 auto backup_file = fs::u8path(request.params[1].get_str());
631
632 std::string wallet_name = request.params[0].get_str();
633
634 std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
635
636 DatabaseStatus status;
637 bilingual_str error;
638 std::vector<bilingual_str> warnings;
639
640 const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
641
642 HandleWalletError(wallet, status, error);
643
645 obj.pushKV("name", wallet->GetName());
646 PushWarnings(warnings, obj);
647
648 return obj;
649
650},
651 };
652}
653} // namespace wallet
constexpr int64_t TIMESTAMP_WINDOW
Timestamp window used as a grace period by code that compares external timestamps (such as timestamps...
Definition: chain.h:37
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:112
uint256 hashMerkleRoot
Definition: block.h:32
uint256 GetHash() const
Definition: block.cpp:14
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
Definition: merkleblock.h:127
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:130
CPartialMerkleTree txn
Definition: merkleblock.h:131
uint256 ExtractMatches(std::vector< Txid > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid's represented by this partial merkle tree and their respective indices with...
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:83
void push_back(UniValue val)
Definition: univalue.cpp:103
const std::string & get_str() const
@ VNULL
Definition: univalue.h:24
@ VOBJ
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
void setArray()
Definition: univalue.cpp:91
void clear()
Definition: univalue.cpp:17
size_t size() const
Definition: univalue.h:71
enum VType type() const
Definition: univalue.h:128
const std::vector< UniValue > & getValues() const
bool isStr() const
Definition: univalue.h:85
Int getInt() const
Definition: univalue.h:140
bool isNum() const
Definition: univalue.h:86
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
Helper for findBlock to selectively return pieces of block data.
Definition: chain.h:53
static transaction_identifier FromUint256(const uint256 &id)
The util::Expected class provides a standard way for low-level functions to return either error value...
Definition: expected.h:44
constexpr const E & error() const &noexcept LIFETIMEBOUND
Definition: expected.h:86
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:310
Descriptor with some wallet metadata.
Definition: walletutil.h:64
std::shared_ptr< Descriptor > descriptor
Definition: walletutil.h:66
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1100
bool reserve(bool with_passphrase=false)
Definition: wallet.h:1110
static UniValue Parse(std::string_view raw, ParamFormat format=ParamFormat::JSON)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition: client.cpp:402
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
bool DecodeHexTx(CMutableTransaction &tx, const std::string &hex_tx, bool try_no_witness, bool try_witness)
Definition: core_io.cpp:225
static path u8path(std::string_view utf8_str)
Definition: fs.h:82
@ MEMPOOL_NO_BROADCAST
Add the transaction to the mempool, but don't broadcast to anybody.
bilingual_str ErrorString(const Result< T > &result)
Definition: result.h:93
static UniValue ProcessDescriptorImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Definition: backup.cpp:147
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:62
void HandleWalletError(const std::shared_ptr< CWallet > &wallet, DatabaseStatus &status, bilingual_str &error)
Definition: util.cpp:124
RPCMethod importdescriptors()
Definition: backup.cpp:316
void EnsureWalletIsUnlocked(const CWallet &wallet)
Definition: util.cpp:85
WalletContext & EnsureWalletContext(const std::any &context)
Definition: util.cpp:92
RPCMethod removeprunedfunds()
Definition: backup.cpp:95
RPCMethod importprunedfunds()
Definition: backup.cpp:40
std::string LabelFromValue(const UniValue &value)
Definition: util.cpp:101
std::shared_ptr< CWallet > RestoreWallet(WalletContext &context, const fs::path &backup_file, const std::string &wallet_name, std::optional< bool > load_on_start, DatabaseStatus &status, bilingual_str &error, std::vector< bilingual_str > &warnings, bool load_after_restore, bool allow_unnamed)
Definition: wallet.cpp:457
RPCMethod listdescriptors()
Definition: backup.cpp:479
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
Definition: backup.cpp:133
RPCMethod backupwallet()
Definition: backup.cpp:562
util::Expected< std::vector< WalletDescInfo >, std::string > ExportDescriptors(const CWallet &wallet, bool export_private)
Export the descriptors from a wallet so that they can be imported elsewhere.
Definition: export.cpp:18
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:30
RPCMethod restorewallet()
Definition: backup.cpp:597
DatabaseStatus
Definition: db.h:186
is a home for public enum and struct type definitions that are used internally by node code,...
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:70
@ RPC_MISC_ERROR
General application defined errors.
Definition: protocol.h:63
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
Definition: protocol.h:64
@ RPC_METHOD_DEPRECATED
RPC method is deprecated.
Definition: protocol.h:74
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:67
@ RPC_WALLET_ERROR
Wallet errors.
Definition: protocol.h:95
@ RPC_DESERIALIZATION_ERROR
Error parsing or validating structure in raw format.
Definition: protocol.h:69
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:65
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
Definition: util.cpp:1323
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:183
std::string HelpExampleRpcNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:207
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)
Definition: util.cpp:130
void PushWarnings(const UniValue &warnings, UniValue &obj)
Push warning messages to an RPC "warnings" field as a JSON array of strings.
Definition: util.cpp:1393
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:201
const std::string UNIX_EPOCH_TIME
String used to describe UNIX epoch time in documentation, factored out to a constant for consistency.
Definition: util.cpp:43
std::string HelpExampleCliNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:188
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: util.cpp:117
A mutable version of CTransaction.
Definition: transaction.h:358
Txid GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:69
@ RANGE
Special type that is a NUM or [NUM,NUM].
@ STR_HEX
Special type that is a STR with only hex chars.
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
@ NO
Required arg.
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings....
Definition: util.h:172
std::string oneline_description
Should be empty unless it is supposed to override the auto-generated summary line.
Definition: util.h:171
@ ARR_FIXED
Special array that has a fixed number of entries.
Bilingual messages:
Definition: translation.h:24
State of transaction confirmed in a block.
Definition: transaction.h:32
WalletContext struct containing references to state shared between CWallet instances,...
Definition: context.h:36
#define LOCK(cs)
Definition: sync.h:268
std::vector< uint16_t > keys
Definition: dbwrapper.cpp:376
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
const char * uvTypeName(UniValue::VType t)
Definition: univalue.cpp:217