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