Bitcoin Core  31.0.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 
38 namespace wallet {
40 {
41  return RPCHelpMan{
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 RPCHelpMan& 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 RPCHelpMan{
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  },
103  RPCExamples{
104  HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
105  "\nAs a JSON-RPC call\n"
106  + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
107  },
108  [&](const RPCHelpMan& 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 
127 static 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 
141 static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
142 {
143  UniValue warnings(UniValue::VARR);
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
156  FlatSigningProvider keys;
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  WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
269 
270  // Add descriptor to the wallet
271  auto spk_manager_res = wallet.AddWalletDescriptor(w_desc, keys, label, desc_internal);
272 
273  if (!spk_manager_res) {
274  throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s': %s", descriptor, util::ErrorString(spk_manager_res).original));
275  }
276 
277  auto& spk_manager = spk_manager_res.value().get();
278 
279  // Set descriptor as active if necessary
280  if (active) {
281  if (!w_desc.descriptor->GetOutputType()) {
282  warnings.push_back("Unknown output type, cannot set descriptor to active.");
283  } else {
284  wallet.AddActiveScriptPubKeyMan(spk_manager.GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
285  }
286  } else {
287  if (w_desc.descriptor->GetOutputType()) {
288  wallet.DeactivateScriptPubKeyMan(spk_manager.GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
289  }
290  }
291  }
292 
293  result.pushKV("success", UniValue(true));
294  } catch (const UniValue& e) {
295  result.pushKV("success", UniValue(false));
296  result.pushKV("error", e);
297  }
298  PushWarnings(warnings, result);
299  return result;
300 }
301 
303 {
304  return RPCHelpMan{
305  "importdescriptors",
306  "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"
307  "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"
308  "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
309  "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
310  "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
311  {
312  {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
313  {
315  {
316  {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
317  {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
318  {"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"},
319  {"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"},
320  {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
321  "Use the string \"now\" to substitute the current synced blockchain time.\n"
322  "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
323  "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
324  "of all descriptors being imported will be scanned as well as the mempool.",
325  RPCArgOptions{.type_str={"timestamp | \"now\"", "integer / string"}}
326  },
327  {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
328  {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"},
329  },
330  },
331  },
332  RPCArgOptions{.oneline_description="requests"}},
333  },
334  RPCResult{
335  RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
336  {
337  {RPCResult::Type::OBJ, "", "",
338  {
339  {RPCResult::Type::BOOL, "success", ""},
340  {RPCResult::Type::ARR, "warnings", /*optional=*/true, "",
341  {
342  {RPCResult::Type::STR, "", ""},
343  }},
344  {RPCResult::Type::OBJ, "error", /*optional=*/true, "",
345  {
346  {RPCResult::Type::ELISION, "", "JSONRPC error"},
347  }},
348  }},
349  }
350  },
351  RPCExamples{
352  HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
353  "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
354  HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
355  },
356  [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
357 {
358  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
359  if (!pwallet) return UniValue::VNULL;
360  CWallet& wallet{*pwallet};
361 
362  // Make sure the results are valid at least up to the most recent block
363  // the user could have gotten from another RPC command prior to now
364  wallet.BlockUntilSyncedToCurrentChain();
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  pwallet->RefreshAllTXOs();
405  }
406 
407  // Rescan the blockchain using the lowest timestamp
408  if (rescan) {
409  int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, /*update=*/true);
410  pwallet->ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);
411 
412  if (pwallet->IsAbortingRescan()) {
413  throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
414  }
415 
416  if (scanned_time > lowest_timestamp) {
417  std::vector<UniValue> results = response.getValues();
418  response.clear();
419  response.setArray();
420 
421  // Compose the response
422  for (unsigned int i = 0; i < requests.size(); ++i) {
423  const UniValue& request = requests.getValues().at(i);
424 
425  // If the descriptor timestamp is within the successfully scanned
426  // range, or if the import result already has an error set, let
427  // the result stand unmodified. Otherwise replace the result
428  // with an error message.
429  if (scanned_time <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
430  response.push_back(results.at(i));
431  } else {
432  std::string error_msg{strprintf("Rescan failed for descriptor with timestamp %d. There "
433  "was an error reading a block from time %d, which is after or within %d seconds "
434  "of key creation, and could contain transactions pertaining to the desc. As a "
435  "result, transactions and coins using this desc may not appear in the wallet.",
436  GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)};
437  if (pwallet->chain().havePruned()) {
438  error_msg += strprintf(" This error could be caused by pruning or data corruption "
439  "(see bitcoind log for details) and could be dealt with by downloading and "
440  "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
441  } else if (pwallet->chain().hasAssumedValidChain()) {
442  error_msg += strprintf(" This error is likely caused by an in-progress assumeutxo "
443  "background sync. Check logs or getchainstates RPC for assumeutxo background "
444  "sync progress and try again later.");
445  } else {
446  error_msg += strprintf(" This error could potentially caused by data corruption. If "
447  "the issue persists you may want to reindex (see -reindex option).");
448  }
449 
451  result.pushKV("success", UniValue(false));
452  result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, error_msg));
453  response.push_back(std::move(result));
454  }
455  }
456  }
457  }
458 
459  return response;
460 },
461  };
462 }
463 
465 {
466  return RPCHelpMan{
467  "listdescriptors",
468  "List all descriptors present in a wallet.\n",
469  {
470  {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private descriptors."}
471  },
473  {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
474  {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects (sorted by descriptor string representation)",
475  {
476  {RPCResult::Type::OBJ, "", "", {
477  {RPCResult::Type::STR, "desc", "Descriptor string representation"},
478  {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
479  {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
480  {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"},
481  {RPCResult::Type::ARR_FIXED, "range", /*optional=*/true, "Defined only for ranged descriptors", {
482  {RPCResult::Type::NUM, "", "Range start inclusive"},
483  {RPCResult::Type::NUM, "", "Range end inclusive"},
484  }},
485  {RPCResult::Type::NUM, "next", /*optional=*/true, "Same as next_index field. Kept for compatibility reason."},
486  {RPCResult::Type::NUM, "next_index", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
487  }},
488  }}
489  }},
490  RPCExamples{
491  HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
492  + HelpExampleCli("listdescriptors", "true") + HelpExampleRpc("listdescriptors", "true")
493  },
494  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
495 {
496  const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
497  if (!wallet) return UniValue::VNULL;
498 
499  const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
500  if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && priv) {
501  throw JSONRPCError(RPC_WALLET_ERROR, "Can't get private descriptor string for watch-only wallets");
502  }
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  CHECK_NONFATAL(desc_spk_man->GetDescriptorString(descriptor, priv));
530  const bool is_range = wallet_descriptor.descriptor->IsRange();
531  wallet_descriptors.push_back({
532  descriptor,
533  wallet_descriptor.creation_time,
534  active_spk_mans.contains(desc_spk_man),
535  wallet->IsInternalScriptPubKeyMan(desc_spk_man),
536  is_range ? std::optional(std::make_pair(wallet_descriptor.range_start, wallet_descriptor.range_end)) : std::nullopt,
537  wallet_descriptor.next_index
538  });
539  }
540 
541  std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](const auto& a, const auto& b) {
542  return a.descriptor < b.descriptor;
543  });
544 
545  UniValue descriptors(UniValue::VARR);
546  for (const WalletDescInfo& info : wallet_descriptors) {
548  spk.pushKV("desc", info.descriptor);
549  spk.pushKV("timestamp", info.creation_time);
550  spk.pushKV("active", info.active);
551  if (info.internal.has_value()) {
552  spk.pushKV("internal", info.internal.value());
553  }
554  if (info.range.has_value()) {
555  UniValue range(UniValue::VARR);
556  range.push_back(info.range->first);
557  range.push_back(info.range->second - 1);
558  spk.pushKV("range", std::move(range));
559  spk.pushKV("next", info.next_index);
560  spk.pushKV("next_index", info.next_index);
561  }
562  descriptors.push_back(std::move(spk));
563  }
564 
565  UniValue response(UniValue::VOBJ);
566  response.pushKV("wallet_name", wallet->GetName());
567  response.pushKV("descriptors", std::move(descriptors));
568 
569  return response;
570 },
571  };
572 }
573 
575 {
576  return RPCHelpMan{
577  "backupwallet",
578  "Safely copies the current wallet file to the specified destination, which can either be a directory or a path with a filename.\n",
579  {
580  {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
581  },
583  RPCExamples{
584  HelpExampleCli("backupwallet", "\"backup.dat\"")
585  + HelpExampleRpc("backupwallet", "\"backup.dat\"")
586  },
587  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
588 {
589  const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
590  if (!pwallet) return UniValue::VNULL;
591 
592  // Make sure the results are valid at least up to the most recent block
593  // the user could have gotten from another RPC command prior to now
594  pwallet->BlockUntilSyncedToCurrentChain();
595 
596  LOCK(pwallet->cs_wallet);
597 
598  std::string strDest = request.params[0].get_str();
599  if (!pwallet->BackupWallet(strDest)) {
600  throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
601  }
602 
603  return UniValue::VNULL;
604 },
605  };
606 }
607 
608 
610 {
611  return RPCHelpMan{
612  "restorewallet",
613  "Restores and loads a wallet from backup.\n"
614  "\nThe rescan is significantly faster if block filters are available"
615  "\n(using startup option \"-blockfilterindex=1\").\n",
616  {
617  {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"},
618  {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."},
619  {"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."},
620  },
621  RPCResult{
622  RPCResult::Type::OBJ, "", "",
623  {
624  {RPCResult::Type::STR, "name", "The wallet name if restored successfully."},
625  {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to restoring and loading the wallet.",
626  {
627  {RPCResult::Type::STR, "", ""},
628  }},
629  }
630  },
631  RPCExamples{
632  HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"")
633  + HelpExampleRpc("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"")
634  + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}})
635  + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}})
636  },
637  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
638 {
639 
640  WalletContext& context = EnsureWalletContext(request.context);
641 
642  auto backup_file = fs::u8path(request.params[1].get_str());
643 
644  std::string wallet_name = request.params[0].get_str();
645 
646  std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
647 
648  DatabaseStatus status;
649  bilingual_str error;
650  std::vector<bilingual_str> warnings;
651 
652  const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
653 
654  HandleWalletError(wallet, status, error);
655 
657  obj.pushKV("name", wallet->GetName());
658  PushWarnings(warnings, obj);
659 
660  return obj;
661 
662 },
663  };
664 }
665 } // namespace wallet
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
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:395
Helper for findBlock to selectively return pieces of block data.
Definition: chain.h:52
void push_back(UniValue val)
Definition: univalue.cpp:103
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:130
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_opts.type_str.at(0) will override the type of the value in a key-value pair, m_opts.type_str.at(1) will override the type in the argument description.
Definition: util.h:172
const std::vector< UniValue > & getValues() const
std::string HelpExampleRpcNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:207
Required arg.
RPCHelpMan restorewallet()
Definition: backup.cpp:609
Bilingual messages:
Definition: translation.h:24
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
std::map< CKeyID, CKey > keys
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:109
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > origins
const std::string & get_str() const
bool isNum() const
Definition: univalue.h:86
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1087
bool isStr() const
Definition: univalue.h:85
bool reserve(bool with_passphrase=false)
Definition: wallet.h:1098
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
Definition: backup.cpp:127
State of transaction confirmed in a block.
Definition: transaction.h:32
Int getInt() const
Definition: univalue.h:140
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
Invalid, missing or duplicate parameter.
Definition: protocol.h:44
static UniValue ProcessDescriptorImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Definition: backup.cpp:141
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:82
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
Definition: util.cpp:1308
std::string oneline_description
Should be empty unless it is supposed to override the auto-generated summary line.
Definition: util.h:171
Special type that is a STR with only hex chars.
void HandleWalletError(const std::shared_ptr< CWallet > &wallet, DatabaseStatus &status, bilingual_str &error)
Definition: util.cpp:127
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:201
const char * uvTypeName(UniValue::VType t)
Definition: univalue.cpp:217
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
Definition: merkleblock.h:126
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:70
std::string LabelFromValue(const UniValue &value)
Definition: util.cpp:104
uint256 hashMerkleRoot
Definition: block.h:32
#define LOCK(cs)
Definition: sync.h:258
Special array that has a fixed number of entries.
std::shared_ptr< Descriptor > descriptor
Definition: walletutil.h:66
WalletContext & EnsureWalletContext(const std::any &context)
Definition: util.cpp:95
Unexpected type was passed as parameter.
Definition: protocol.h:41
RPCHelpMan listdescriptors()
Definition: backup.cpp:464
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:309
General application defined errors.
Definition: protocol.h:40
std::string HelpExampleCliNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:188
void PushWarnings(const UniValue &warnings, UniValue &obj)
Push warning messages to an RPC "warnings" field as a JSON array of strings.
Definition: util.cpp:1378
Invalid address or key.
Definition: protocol.h:42
Txid GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:69
CPartialMerkleTree txn
Definition: merkleblock.h:131
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:183
RPCHelpMan importdescriptors()
Definition: backup.cpp:302
Descriptor with some wallet metadata.
Definition: walletutil.h:63
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
Special type that is a NUM or [NUM,NUM].
uint256 GetHash() const
Definition: block.cpp:15
DatabaseStatus
Definition: db.h:186
Optional argument for which the default value is omitted from help text for one of two reasons: ...
enum VType type() const
Definition: univalue.h:128
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:51
bool GetKey(const CKeyID &keyid, CKey &key) const override
auto result
Definition: common-types.h:74
RPCHelpMan importprunedfunds()
Definition: backup.cpp:39
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
static transaction_identifier FromUint256(const uint256 &id)
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: util.cpp:117
uint256 ExtractMatches(std::vector< Txid > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid&#39;s represented by this partial merkle tree and their respective indices with...
bool DecodeHexTx(CMutableTransaction &tx, const std::string &hex_tx, bool try_no_witness, bool try_witness)
Definition: core_io.cpp:227
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:23
Add the transaction to the mempool, but don&#39;t broadcast to anybody.
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
Definition: context.h:36
void EnsureWalletIsUnlocked(const CWallet &wallet)
Definition: util.cpp:88
bilingual_str ErrorString(const Result< T > &result)
Definition: result.h:93
A mutable version of CTransaction.
Definition: transaction.h:357
Wallet errors.
Definition: protocol.h:71
static path u8path(std::string_view utf8_str)
Definition: fs.h:81
void clear()
Definition: univalue.cpp:17
FoundBlock & height(int &height)
Definition: chain.h:56
size_t size() const
Definition: univalue.h:71
RPCHelpMan removeprunedfunds()
Definition: backup.cpp:94
An encapsulated private key.
Definition: key.h:35
FoundBlock & mtpTime(int64_t &mtp_time)
Definition: chain.h:59
RPCHelpMan backupwallet()
Definition: backup.cpp:574
void setArray()
Definition: univalue.cpp:91
is a home for public enum and struct type definitions that are used internally by node code...
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
FoundBlock & time(int64_t &time)
Definition: chain.h:57
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:64
Error parsing or validating structure in raw format.
Definition: protocol.h:46
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
Special type to denote elision (...)
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)
Definition: util.cpp:130