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