Branch data Line data Source code
1 : : // Copyright (c) 2009-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <chain.h>
6 : : #include <clientversion.h>
7 : : #include <core_io.h>
8 : : #include <hash.h>
9 : : #include <interfaces/chain.h>
10 : : #include <key_io.h>
11 : : #include <merkleblock.h>
12 : : #include <node/types.h>
13 : : #include <rpc/util.h>
14 : : #include <script/descriptor.h>
15 : : #include <script/script.h>
16 : : #include <script/solver.h>
17 : : #include <sync.h>
18 : : #include <uint256.h>
19 : : #include <util/bip32.h>
20 : : #include <util/check.h>
21 : : #include <util/fs.h>
22 : : #include <util/time.h>
23 : : #include <util/translation.h>
24 : : #include <wallet/export.h>
25 : : #include <wallet/imports.h>
26 : : #include <wallet/rpc/util.h>
27 : : #include <wallet/scan.h>
28 : : #include <wallet/wallet.h>
29 : :
30 : : #include <cstdint>
31 : : #include <fstream>
32 : : #include <tuple>
33 : : #include <string>
34 : :
35 : : #include <univalue.h>
36 : :
37 : :
38 : :
39 : : using interfaces::FoundBlock;
40 : :
41 : : namespace wallet {
42 : 0 : RPCMethod importprunedfunds()
43 : : {
44 : 0 : return RPCMethod{
45 : 0 : "importprunedfunds",
46 [ # # ]: 0 : "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",
47 : : {
48 [ # # # # ]: 0 : {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
49 [ # # # # ]: 0 : {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
50 : : },
51 [ # # # # : 0 : RPCResult{RPCResult::Type::NONE, "", ""},
# # # # ]
52 [ # # # # ]: 0 : RPCExamples{""},
53 : 0 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
54 : : {
55 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
56 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
57 : :
58 [ # # ]: 0 : CMutableTransaction tx;
59 [ # # # # : 0 : if (!DecodeHexTx(tx, request.params[0].get_str())) {
# # # # ]
60 [ # # # # ]: 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
61 : : }
62 : :
63 [ # # ]: 0 : CMerkleBlock merkleBlock;
64 [ # # # # : 0 : SpanReader{ParseHexV(request.params[1], "proof")} >> merkleBlock;
# # ]
65 : :
66 : : //Search partial merkle tree in proof for our transaction and index in valid block
67 : 0 : std::vector<Txid> vMatch;
68 : 0 : std::vector<unsigned int> vIndex;
69 [ # # ]: 0 : if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
70 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
71 : : }
72 : :
73 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
74 : 0 : int height;
75 [ # # # # : 0 : if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
# # ]
76 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
77 : : }
78 : :
79 : 0 : std::vector<Txid>::const_iterator it;
80 [ # # # # ]: 0 : if ((it = std::find(vMatch.begin(), vMatch.end(), tx.GetHash())) == vMatch.end()) {
81 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
82 : : }
83 : :
84 [ # # ]: 0 : unsigned int txnIndex = vIndex[it - vMatch.begin()];
85 : :
86 [ # # ]: 0 : const CTransactionRef tx_ref = MakeTransactionRef(tx);
87 [ # # # # ]: 0 : auto tx_state = TxStateConfirmed{merkleBlock.header.GetHash(), height, static_cast<int>(txnIndex)};
88 [ # # # # ]: 0 : if (pwallet->AddToWalletIfInvolvingMe(tx_ref, tx_state, /*rescanning_old_block=*/false)) {
89 [ # # ]: 0 : return UniValue::VNULL;
90 : : }
91 : :
92 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
93 [ # # ]: 0 : },
94 [ # # # # : 0 : };
# # # # ]
95 [ # # # # : 0 : }
# # ]
96 : :
97 : : /**
98 : : * Converts the timestamp from UniValue data to an int64_t.
99 : : *
100 : : * @returns The import timestamp in int64_t, or std::nullopt if now was provided.
101 : : */
102 : 0 : static std::optional<int64_t> GetImportTimestamp(const UniValue& data)
103 : : {
104 [ # # ]: 0 : if (data.exists("timestamp")) {
105 [ # # ]: 0 : const UniValue& timestamp = data["timestamp"];
106 [ # # ]: 0 : if (timestamp.isNum()) {
107 : 0 : const int64_t value{timestamp.getInt<int64_t>()};
108 [ # # ]: 0 : if (value < 0) {
109 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Timestamp must not be negative");
110 : : }
111 : 0 : return value;
112 [ # # # # ]: 0 : } else if (timestamp.isStr() && timestamp.get_str() == "now") {
113 : 0 : return std::nullopt; // std::nullopt means use the current best block's MTP time
114 : : }
115 [ # # # # : 0 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
# # ]
116 : : }
117 [ # # # # ]: 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
118 : : }
119 : :
120 : 0 : static ImportDescriptorRequest ProcessUniValueDescriptor(const UniValue& data, std::optional<int64_t> timestamp)
121 : : {
122 [ # # ]: 0 : ImportDescriptorRequest request;
123 [ # # # # ]: 0 : if (!data.exists("desc")) {
124 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");
125 : : }
126 [ # # # # : 0 : request.descriptor = data["desc"].get_str();
# # # # ]
127 [ # # # # : 0 : request.label = LabelFromValue(data["label"]);
# # ]
128 : 0 : request.timestamp = timestamp;
129 [ # # # # : 0 : if (data.exists("active")) request.active = data["active"].get_bool();
# # # # #
# ]
130 [ # # # # : 0 : if (data.exists("internal")) request.internal = data["internal"].get_bool();
# # # # #
# ]
131 : 0 : if (data.exists("range")) request.range = ParseDescriptorRange(data["range"]);
[ # # # #
# # # # #
# # # ]
132 [ # # # # : 0 : if (data.exists("next_index")) request.next_index = data["next_index"].getInt<int64_t>();
# # # # #
# ]
133 : 0 : return request;
134 : 0 : }
135 : :
136 : 0 : RPCMethod importdescriptors()
137 : : {
138 : 0 : return RPCMethod{
139 : 0 : "importdescriptors",
140 [ # # ]: 0 : "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"
141 : : "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"
142 : : "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
143 : : "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
144 : : "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
145 : : {
146 [ # # # # ]: 0 : {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
147 : : {
148 [ # # # # ]: 0 : {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
149 : : {
150 [ # # # # ]: 0 : {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
151 [ # # # # : 0 : {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
# # ]
152 [ # # # # ]: 0 : {"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"},
153 [ # # # # ]: 0 : {"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"},
154 [ # # # # ]: 0 : {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
155 : : "Use the string \"now\" to substitute the current synced blockchain time.\n"
156 : : "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
157 : : "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
158 : 0 : "of all descriptors being imported will be scanned as well as the mempool.",
159 [ # # ]: 0 : RPCArgOptions{.type_str={"timestamp | \"now\"", "integer / string"}}
160 : : },
161 [ # # # # : 0 : {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
# # ]
162 [ # # # # : 0 : {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"},
# # ]
163 : : },
164 : : },
165 : : },
166 [ # # ]: 0 : RPCArgOptions{.oneline_description="requests"}},
167 : : },
168 [ # # ]: 0 : RPCResult{
169 [ # # # # ]: 0 : RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
170 : : {
171 [ # # # # ]: 0 : {RPCResult::Type::OBJ, "", "",
172 : : {
173 [ # # # # ]: 0 : {RPCResult::Type::BOOL, "success", ""},
174 [ # # # # ]: 0 : {RPCResult::Type::ARR, "warnings", /*optional=*/true, "",
175 : : {
176 [ # # # # ]: 0 : {RPCResult::Type::STR, "", ""},
177 : : }},
178 [ # # # # ]: 0 : {RPCResult::Type::OBJ, "error", /*optional=*/true, "",
179 : : {
180 [ # # # # ]: 0 : {RPCResult::Type::NUM, "code", "JSONRPC error code"},
181 [ # # # # ]: 0 : {RPCResult::Type::STR, "message", "JSONRPC error message"},
182 : : }},
183 : : }},
184 : : }
185 : 0 : },
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
186 : 0 : RPCExamples{
187 [ # # # # : 0 : HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
# # ]
188 : 0 : "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
189 [ # # # # : 0 : HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
# # # # ]
190 [ # # ]: 0 : },
191 : 0 : [](const RPCMethod& self, const JSONRPCRequest& main_request) -> UniValue
192 : : {
193 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
194 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
195 [ # # ]: 0 : CWallet& wallet{*pwallet};
196 : :
197 [ # # ]: 0 : const UniValue& univalue_requests = main_request.params[0];
198 : : // One result per input, in input order.
199 [ # # # # ]: 0 : std::vector<UniValue> results(univalue_requests.size());
200 : : // Successfully parsed requests, each with the index of the input it came from.
201 : 0 : struct ParsedRequest {
202 : : size_t input_index;
203 : : ImportDescriptorRequest request;
204 : : };
205 : 0 : std::vector<ParsedRequest> requests;
206 : :
207 : : // Malformed inputs (e.g. invalid label) are returned as per-item failures.
208 [ # # # # ]: 0 : for (size_t i = 0; i < univalue_requests.size(); ++i) {
209 : : // Throws a top-level RPC error if "timestamp" is missing or invalid
210 [ # # # # ]: 0 : std::optional<int64_t> timestamp = GetImportTimestamp(univalue_requests[i]);
211 : 0 : try {
212 : 0 : requests.push_back({i, ProcessUniValueDescriptor(univalue_requests[i], timestamp)});
213 [ - - ]: 0 : } catch (const UniValue& e) {
214 : 0 : results[i] = UniValue(UniValue::VOBJ);
215 [ - - - - : 0 : results[i].pushKV("success", UniValue(false));
- - ]
216 [ - - - - : 0 : results[i].pushKV("error", e);
- - ]
217 : 0 : }
218 : : }
219 : :
220 : : // Hand off the successfully parsed requests to the batch importer, which
221 : : // handles wallet locking, rescanning and rescan-failure error composition.
222 : 0 : std::vector<ImportDescriptorRequest> descriptor_requests;
223 [ # # # # ]: 0 : descriptor_requests.reserve(requests.size());
224 [ # # ]: 0 : for (auto& parsed : requests) {
225 [ # # ]: 0 : descriptor_requests.push_back(std::move(parsed.request));
226 : : }
227 : :
228 [ # # ]: 0 : std::vector<ImportResult> import_results{ProcessDescriptorsImport(wallet, descriptor_requests)};
229 : :
230 : : // Wallet-wide precondition failure (e.g. already rescanning, or locked):
231 : : // surface as a top-level RPC error.
232 [ # # # # : 0 : if (import_results.size() == 1 && import_results[0].has_error() && import_results[0].error->is_general_error) {
# # # # ]
233 [ # # ]: 0 : const ImportError& import_error = import_results[0].error.value();
234 [ # # ]: 0 : RPCErrorCode rpc_error_code{HandleWalletErrorCode(import_error.wallet_error.code)};
235 [ # # ]: 0 : throw JSONRPCError(rpc_error_code, import_error.wallet_error.message.original);
236 : : }
237 : :
238 : : // Translate each ImportResult into the per-input UniValue result. Inputs
239 : : // that failed to parse already hold an error in results[] and are not
240 : : // part of `requests`, so use input_index to map each import_results[k]
241 : : // back to its slot.
242 [ # # # # ]: 0 : CHECK_NONFATAL(import_results.size() == requests.size());
243 [ # # # # ]: 0 : for (size_t k = 0; k < requests.size(); ++k) {
244 : 0 : UniValue& result = results[requests[k].input_index];
245 : 0 : const ImportResult& import_result = import_results[k];
246 : 0 : result = UniValue(UniValue::VOBJ);
247 : 0 : UniValue warnings(UniValue::VARR);
248 [ # # ]: 0 : if (import_result.has_error()) {
249 [ # # ]: 0 : const WalletError& error = import_result.error.value().wallet_error;
250 : 0 : auto write_error = [&result, &error](int code) {
251 [ # # # # ]: 0 : result.pushKV("success", false);
252 [ # # # # ]: 0 : result.pushKV("error", JSONRPCError(code, error.message.original));
253 : 0 : };
254 [ # # ]: 0 : if (error.code == WalletErrorCode::UnlockNeeded) NONFATAL_UNREACHABLE();
255 [ # # # # ]: 0 : write_error(HandleWalletErrorCode(error.code));
256 : : } else {
257 [ # # # # : 0 : result.pushKV("success", true);
# # ]
258 : : }
259 [ # # ]: 0 : for (const auto& w : import_result.warnings) {
260 [ # # # # ]: 0 : warnings.push_back(w);
261 : : }
262 [ # # ]: 0 : PushWarnings(warnings, result);
263 : 0 : }
264 : :
265 : 0 : UniValue response(UniValue::VARR);
266 [ # # ]: 0 : for (UniValue& result : results) {
267 [ # # ]: 0 : response.push_back(std::move(result));
268 : : }
269 : 0 : return response;
270 [ # # # # ]: 0 : },
271 : 0 : };
[ # # # #
# # # # #
# # # # #
# # # # #
# ]
272 : 0 : }
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
273 : :
274 : 0 : RPCMethod listdescriptors()
275 : : {
276 : 0 : return RPCMethod{
277 : 0 : "listdescriptors",
278 [ # # ]: 0 : "List all descriptors present in a wallet.\n",
279 : : {
280 [ # # # # : 0 : {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private descriptors."}
# # ]
281 : : },
282 [ # # # # : 0 : RPCResult{RPCResult::Type::OBJ, "", "", {
# # ]
283 [ # # # # ]: 0 : {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
284 [ # # # # ]: 0 : {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects (sorted by descriptor string representation)",
285 : : {
286 [ # # # # ]: 0 : {RPCResult::Type::OBJ, "", "", {
287 [ # # # # ]: 0 : {RPCResult::Type::STR, "desc", "Descriptor string representation"},
288 [ # # # # ]: 0 : {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
289 [ # # # # ]: 0 : {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
290 [ # # # # ]: 0 : {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"},
291 [ # # # # ]: 0 : {RPCResult::Type::ARR_FIXED, "range", /*optional=*/true, "Defined only for ranged descriptors", {
292 [ # # # # ]: 0 : {RPCResult::Type::NUM, "", "Range start inclusive"},
293 [ # # # # ]: 0 : {RPCResult::Type::NUM, "", "Range end inclusive"},
294 : : }},
295 [ # # # # ]: 0 : {RPCResult::Type::NUM, "next", /*optional=*/true, "Same as next_index field. Kept for compatibility reason."},
296 [ # # # # ]: 0 : {RPCResult::Type::NUM, "next_index", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
297 : : }},
298 : : }}
299 : 0 : }},
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
300 : 0 : RPCExamples{
301 : 0 : HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
[ # # # #
# # # # #
# # # #
# ]
302 : 0 : + HelpExampleCli("listdescriptors", "true") + HelpExampleRpc("listdescriptors", "true")
[ # # # #
# # # # #
# # # # #
# # ]
303 [ # # ]: 0 : },
304 : 0 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
305 : : {
306 [ # # ]: 0 : const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
307 [ # # ]: 0 : if (!wallet) return UniValue::VNULL;
308 : :
309 [ # # ]: 0 : const bool priv{self.Arg<bool>("private")};
310 [ # # # # : 0 : if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && priv) {
# # ]
311 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Can't get private descriptor string for watch-only wallets");
312 : : }
313 [ # # ]: 0 : if (priv) {
314 [ # # ]: 0 : EnsureWalletIsUnlocked(*wallet);
315 : : }
316 : :
317 [ # # ]: 0 : LOCK(wallet->cs_wallet);
318 [ # # ]: 0 : util::Expected<std::vector<WalletDescInfo>, std::string> exported = ExportDescriptors(*wallet, priv);
319 [ # # ]: 0 : if (!exported) {
320 [ # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, exported.error());
321 : : }
322 [ # # ]: 0 : std::vector<WalletDescInfo> wallet_descriptors = *exported;
323 : :
324 : 0 : std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](const auto& a, const auto& b) {
325 : 0 : return a.descriptor < b.descriptor;
[ # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
326 : : });
327 : :
328 : 0 : UniValue descriptors(UniValue::VARR);
329 [ # # ]: 0 : for (const WalletDescInfo& info : wallet_descriptors) {
330 : 0 : UniValue spk(UniValue::VOBJ);
331 [ # # # # : 0 : spk.pushKV("desc", info.descriptor);
# # ]
332 [ # # # # : 0 : spk.pushKV("timestamp", info.creation_time);
# # ]
333 [ # # # # : 0 : spk.pushKV("active", info.active);
# # ]
334 [ # # ]: 0 : if (info.internal.has_value()) {
335 [ # # # # : 0 : spk.pushKV("internal", info.internal.value());
# # ]
336 : : }
337 [ # # ]: 0 : if (info.range.has_value()) {
338 : 0 : UniValue range(UniValue::VARR);
339 [ # # # # ]: 0 : range.push_back(info.range->first);
340 [ # # # # ]: 0 : range.push_back(info.range->second - 1);
341 [ # # # # ]: 0 : spk.pushKV("range", std::move(range));
342 [ # # # # : 0 : spk.pushKV("next", info.next_index);
# # ]
343 [ # # # # : 0 : spk.pushKV("next_index", info.next_index);
# # ]
344 : 0 : }
345 [ # # ]: 0 : descriptors.push_back(std::move(spk));
346 : 0 : }
347 : :
348 : 0 : UniValue response(UniValue::VOBJ);
349 [ # # # # : 0 : response.pushKV("wallet_name", wallet->GetName());
# # ]
350 [ # # # # ]: 0 : response.pushKV("descriptors", std::move(descriptors));
351 : :
352 : 0 : return response;
353 [ # # ]: 0 : },
354 [ # # # # : 0 : };
# # # # ]
355 : 0 : }
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
356 : :
357 : 0 : RPCMethod backupwallet()
358 : : {
359 : 0 : return RPCMethod{
360 : 0 : "backupwallet",
361 [ # # ]: 0 : "Safely copies the current wallet file to the specified destination, which can either be a directory or a path with a filename.\n",
362 : : {
363 [ # # # # ]: 0 : {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
364 : : },
365 [ # # # # : 0 : RPCResult{RPCResult::Type::NONE, "", ""},
# # # # ]
366 : 0 : RPCExamples{
367 [ # # # # : 0 : HelpExampleCli("backupwallet", "\"backup.dat\"")
# # ]
368 [ # # # # : 0 : + HelpExampleRpc("backupwallet", "\"backup.dat\"")
# # # # ]
369 [ # # ]: 0 : },
370 : 0 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
371 : : {
372 [ # # ]: 0 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
373 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
374 : :
375 : : // Make sure the results are valid at least up to the most recent block
376 : : // the user could have gotten from another RPC command prior to now
377 [ # # ]: 0 : pwallet->BlockUntilSyncedToCurrentChain();
378 : :
379 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
380 : :
381 [ # # # # : 0 : std::string strDest = request.params[0].get_str();
# # ]
382 [ # # # # ]: 0 : if (!pwallet->BackupWallet(strDest)) {
383 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
384 : : }
385 : :
386 : 0 : return UniValue::VNULL;
387 [ # # ]: 0 : },
388 [ # # # # : 0 : };
# # # # ]
389 [ # # ]: 0 : }
390 : :
391 : :
392 : 0 : RPCMethod restorewallet()
393 : : {
394 : 0 : return RPCMethod{
395 : 0 : "restorewallet",
396 [ # # ]: 0 : "Restores and loads a wallet from backup.\n"
397 : : "\nThe rescan is significantly faster if block filters are available"
398 : : "\n(using startup option \"-blockfilterindex=1\").\n",
399 : : {
400 [ # # # # ]: 0 : {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"},
401 [ # # # # ]: 0 : {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."},
402 [ # # # # ]: 0 : {"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."},
403 : : },
404 [ # # ]: 0 : RPCResult{
405 [ # # # # ]: 0 : RPCResult::Type::OBJ, "", "",
406 : : {
407 [ # # # # ]: 0 : {RPCResult::Type::STR, "name", "The wallet name if restored successfully."},
408 [ # # # # ]: 0 : {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to restoring and loading the wallet.",
409 : : {
410 [ # # # # ]: 0 : {RPCResult::Type::STR, "", ""},
411 : : }},
412 : : }
413 : 0 : },
[ # # # #
# # # # #
# # # #
# ]
414 : 0 : RPCExamples{
415 [ # # # # : 0 : HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"")
# # ]
416 [ # # # # : 0 : + HelpExampleRpc("restorewallet", R"("testwallet", "home\\backups\\backup-file.bak")")
# # # # ]
417 : 0 : + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak"}, {"load_on_startup", true}})
[ # # # #
# # # # #
# # # ]
418 : 0 : + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak"}, {"load_on_startup", true}})
[ # # # #
# # # # #
# # # ]
419 [ # # ]: 0 : },
420 : 0 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
421 : : {
422 : :
423 : 0 : WalletContext& context = EnsureWalletContext(request.context);
424 : :
425 [ # # ]: 0 : auto backup_file = fs::u8path(request.params[1].get_str());
426 : :
427 [ # # # # : 0 : std::string wallet_name = request.params[0].get_str();
# # ]
428 : :
429 [ # # # # : 0 : std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
# # # # ]
430 : :
431 : 0 : DatabaseStatus status;
432 [ # # ]: 0 : bilingual_str error;
433 : 0 : std::vector<bilingual_str> warnings;
434 : :
435 [ # # ]: 0 : const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
436 : :
437 [ # # ]: 0 : HandleWalletError(wallet, status, error);
438 : :
439 : 0 : UniValue obj(UniValue::VOBJ);
440 [ # # # # : 0 : obj.pushKV("name", wallet->GetName());
# # ]
441 [ # # ]: 0 : PushWarnings(warnings, obj);
442 : :
443 [ # # ]: 0 : return obj;
444 : :
445 : 0 : },
446 [ # # # # : 0 : };
# # # # ]
447 : 0 : }
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
448 : : } // namespace wallet
|