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