1 // Copyright (c) 2009-2020 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 <core_io.h>
7 #include <interfaces/chain.h>
8 #include <key_io.h>
9 #include <merkleblock.h>
10 #include <rpc/util.h>
11 #include <script/descriptor.h>
12 #include <script/script.h>
13 #include <script/standard.h>
14 #include <sync.h>
15 #include <util/bip32.h>
16 #include <util/system.h>
17 #include <util/time.h>
18 #include <util/translation.h>
19 #include <wallet/rpcwallet.h>
20 #include <wallet/wallet.h>
21
22 #include <stdint.h>
23 #include <tuple>
24
25 #include <boost/algorithm/string.hpp>
26
27 #include <univalue.h>
28
29
30
31 using interfaces::FoundBlock;
32
EncodeDumpString(const std::string & str)33 std::string static EncodeDumpString(const std::string &str) {
34 std::stringstream ret;
35 for (const unsigned char c : str) {
36 if (c <= 32 || c >= 128 || c == '%') {
37 ret << '%' << HexStr(Span<const unsigned char>(&c, 1));
38 } else {
39 ret << c;
40 }
41 }
42 return ret.str();
43 }
44
DecodeDumpString(const std::string & str)45 static std::string DecodeDumpString(const std::string &str) {
46 std::stringstream ret;
47 for (unsigned int pos = 0; pos < str.length(); pos++) {
48 unsigned char c = str[pos];
49 if (c == '%' && pos+2 < str.length()) {
50 c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
51 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
52 pos += 2;
53 }
54 ret << c;
55 }
56 return ret.str();
57 }
58
GetWalletAddressesForKey(LegacyScriptPubKeyMan * spk_man,const CWallet & wallet,const CKeyID & keyid,std::string & strAddr,std::string & strLabel)59 static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet& wallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
60 {
61 bool fLabelFound = false;
62 CKey key;
63 spk_man->GetKey(keyid, key);
64 for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
65 const auto* address_book_entry = wallet.FindAddressBookEntry(dest);
66 if (address_book_entry) {
67 if (!strAddr.empty()) {
68 strAddr += ",";
69 }
70 strAddr += EncodeDestination(dest);
71 strLabel = EncodeDumpString(address_book_entry->GetLabel());
72 fLabelFound = true;
73 }
74 }
75 if (!fLabelFound) {
76 strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), wallet.m_default_address_type));
77 }
78 return fLabelFound;
79 }
80
81 static const int64_t TIMESTAMP_MIN = 0;
82
RescanWallet(CWallet & wallet,const WalletRescanReserver & reserver,int64_t time_begin=TIMESTAMP_MIN,bool update=true)83 static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver, int64_t time_begin = TIMESTAMP_MIN, bool update = true)
84 {
85 int64_t scanned_time = wallet.RescanFromTime(time_begin, reserver, update);
86 if (wallet.IsAbortingRescan()) {
87 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
88 } else if (scanned_time > time_begin) {
89 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
90 }
91 }
92
importprivkey()93 RPCHelpMan importprivkey()
94 {
95 return RPCHelpMan{"importprivkey",
96 "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
97 "Hint: use importmulti to import more than one private key.\n"
98 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
99 "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
100 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
101 {
102 {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
103 {"label", RPCArg::Type::STR, RPCArg::DefaultHint{"current label if address exists, otherwise \"\""}, "An optional label"},
104 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
105 },
106 RPCResult{RPCResult::Type::NONE, "", ""},
107 RPCExamples{
108 "\nDump a private key\n"
109 + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
110 "\nImport the private key with rescan\n"
111 + HelpExampleCli("importprivkey", "\"mykey\"") +
112 "\nImport using a label and without rescan\n"
113 + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
114 "\nImport using default blank label and without rescan\n"
115 + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
116 "\nAs a JSON-RPC call\n"
117 + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
118 },
119 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
120 {
121 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
122 if (!pwallet) return NullUniValue;
123
124 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
125 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
126 }
127
128 EnsureLegacyScriptPubKeyMan(*pwallet, true);
129
130 WalletRescanReserver reserver(*pwallet);
131 bool fRescan = true;
132 {
133 LOCK(pwallet->cs_wallet);
134
135 EnsureWalletIsUnlocked(*pwallet);
136
137 std::string strSecret = request.params[0].get_str();
138 std::string strLabel = "";
139 if (!request.params[1].isNull())
140 strLabel = request.params[1].get_str();
141
142 // Whether to perform rescan after import
143 if (!request.params[2].isNull())
144 fRescan = request.params[2].get_bool();
145
146 if (fRescan && pwallet->chain().havePruned()) {
147 // Exit early and print an error.
148 // If a block is pruned after this check, we will import the key(s),
149 // but fail the rescan with a generic error.
150 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
151 }
152
153 if (fRescan && !reserver.reserve()) {
154 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
155 }
156
157 CKey key = DecodeSecret(strSecret);
158 if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
159
160 CPubKey pubkey = key.GetPubKey();
161 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
162 CKeyID vchAddress = pubkey.GetID();
163 {
164 pwallet->MarkDirty();
165
166 // We don't know which corresponding address will be used;
167 // label all new addresses, and label existing addresses if a
168 // label was passed.
169 for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
170 if (!request.params[1].isNull() || !pwallet->FindAddressBookEntry(dest)) {
171 pwallet->SetAddressBook(dest, strLabel, "receive");
172 }
173 }
174
175 // Use timestamp of 1 to scan the whole chain
176 if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
177 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
178 }
179
180 // Add the wpkh script for this key if possible
181 if (pubkey.IsCompressed()) {
182 pwallet->ImportScripts({GetScriptForDestination(WitnessV0KeyHash(vchAddress))}, 0 /* timestamp */);
183 }
184 }
185 }
186 if (fRescan) {
187 RescanWallet(*pwallet, reserver);
188 }
189
190 return NullUniValue;
191 },
192 };
193 }
194
abortrescan()195 RPCHelpMan abortrescan()
196 {
197 return RPCHelpMan{"abortrescan",
198 "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
199 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
200 {},
201 RPCResult{RPCResult::Type::BOOL, "", "Whether the abort was successful"},
202 RPCExamples{
203 "\nImport a private key\n"
204 + HelpExampleCli("importprivkey", "\"mykey\"") +
205 "\nAbort the running wallet rescan\n"
206 + HelpExampleCli("abortrescan", "") +
207 "\nAs a JSON-RPC call\n"
208 + HelpExampleRpc("abortrescan", "")
209 },
210 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
211 {
212 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
213 if (!pwallet) return NullUniValue;
214
215 if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
216 pwallet->AbortRescan();
217 return true;
218 },
219 };
220 }
221
importaddress()222 RPCHelpMan importaddress()
223 {
224 return RPCHelpMan{"importaddress",
225 "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
226 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
227 "may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
228 "If you have the full public key, you should call importpubkey instead of this.\n"
229 "Hint: use importmulti to import more than one address.\n"
230 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
231 "as change, and not show up in many RPCs.\n"
232 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
233 {
234 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
235 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
236 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
237 {"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false}, "Add the P2SH version of the script as well"},
238 },
239 RPCResult{RPCResult::Type::NONE, "", ""},
240 RPCExamples{
241 "\nImport an address with rescan\n"
242 + HelpExampleCli("importaddress", "\"myaddress\"") +
243 "\nImport using a label without rescan\n"
244 + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
245 "\nAs a JSON-RPC call\n"
246 + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
247 },
248 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
249 {
250 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
251 if (!pwallet) return NullUniValue;
252
253 EnsureLegacyScriptPubKeyMan(*pwallet, true);
254
255 std::string strLabel;
256 if (!request.params[1].isNull())
257 strLabel = request.params[1].get_str();
258
259 // Whether to perform rescan after import
260 bool fRescan = true;
261 if (!request.params[2].isNull())
262 fRescan = request.params[2].get_bool();
263
264 if (fRescan && pwallet->chain().havePruned()) {
265 // Exit early and print an error.
266 // If a block is pruned after this check, we will import the key(s),
267 // but fail the rescan with a generic error.
268 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
269 }
270
271 WalletRescanReserver reserver(*pwallet);
272 if (fRescan && !reserver.reserve()) {
273 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
274 }
275
276 // Whether to import a p2sh version, too
277 bool fP2SH = false;
278 if (!request.params[3].isNull())
279 fP2SH = request.params[3].get_bool();
280
281 {
282 LOCK(pwallet->cs_wallet);
283
284 CTxDestination dest = DecodeDestination(request.params[0].get_str());
285 if (IsValidDestination(dest)) {
286 if (fP2SH) {
287 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
288 }
289 if (OutputTypeFromDestination(dest) == OutputType::BECH32M) {
290 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
291 }
292
293 pwallet->MarkDirty();
294
295 pwallet->ImportScriptPubKeys(strLabel, {GetScriptForDestination(dest)}, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
296 } else if (IsHex(request.params[0].get_str())) {
297 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
298 CScript redeem_script(data.begin(), data.end());
299
300 std::set<CScript> scripts = {redeem_script};
301 pwallet->ImportScripts(scripts, 0 /* timestamp */);
302
303 if (fP2SH) {
304 scripts.insert(GetScriptForDestination(ScriptHash(redeem_script)));
305 }
306
307 pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
308 } else {
309 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
310 }
311 }
312 if (fRescan)
313 {
314 RescanWallet(*pwallet, reserver);
315 {
316 LOCK(pwallet->cs_wallet);
317 pwallet->ReacceptWalletTransactions();
318 }
319 }
320
321 return NullUniValue;
322 },
323 };
324 }
325
importprunedfunds()326 RPCHelpMan importprunedfunds()
327 {
328 return RPCHelpMan{"importprunedfunds",
329 "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
330 {
331 {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
332 {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
333 },
334 RPCResult{RPCResult::Type::NONE, "", ""},
335 RPCExamples{""},
336 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
337 {
338 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
339 if (!pwallet) return NullUniValue;
340
341 CMutableTransaction tx;
342 if (!DecodeHexTx(tx, request.params[0].get_str())) {
343 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
344 }
345 uint256 hashTx = tx.GetHash();
346
347 CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
348 CMerkleBlock merkleBlock;
349 ssMB >> merkleBlock;
350
351 //Search partial merkle tree in proof for our transaction and index in valid block
352 std::vector<uint256> vMatch;
353 std::vector<unsigned int> vIndex;
354 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
355 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
356 }
357
358 LOCK(pwallet->cs_wallet);
359 int height;
360 if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
361 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
362 }
363
364 std::vector<uint256>::const_iterator it;
365 if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
366 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
367 }
368
369 unsigned int txnIndex = vIndex[it - vMatch.begin()];
370
371 CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, merkleBlock.header.GetHash(), txnIndex);
372
373 CTransactionRef tx_ref = MakeTransactionRef(tx);
374 if (pwallet->IsMine(*tx_ref)) {
375 pwallet->AddToWallet(std::move(tx_ref), confirm);
376 return NullUniValue;
377 }
378
379 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
380 },
381 };
382 }
383
removeprunedfunds()384 RPCHelpMan removeprunedfunds()
385 {
386 return RPCHelpMan{"removeprunedfunds",
387 "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
388 {
389 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
390 },
391 RPCResult{RPCResult::Type::NONE, "", ""},
392 RPCExamples{
393 HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
394 "\nAs a JSON-RPC call\n"
395 + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
396 },
397 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
398 {
399 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
400 if (!pwallet) return NullUniValue;
401
402 LOCK(pwallet->cs_wallet);
403
404 uint256 hash(ParseHashV(request.params[0], "txid"));
405 std::vector<uint256> vHash;
406 vHash.push_back(hash);
407 std::vector<uint256> vHashOut;
408
409 if (pwallet->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
410 throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
411 }
412
413 if(vHashOut.empty()) {
414 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
415 }
416
417 return NullUniValue;
418 },
419 };
420 }
421
importpubkey()422 RPCHelpMan importpubkey()
423 {
424 return RPCHelpMan{"importpubkey",
425 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
426 "Hint: use importmulti to import more than one public key.\n"
427 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
428 "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
429 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
430 {
431 {"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
432 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
433 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
434 },
435 RPCResult{RPCResult::Type::NONE, "", ""},
436 RPCExamples{
437 "\nImport a public key with rescan\n"
438 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
439 "\nImport using a label without rescan\n"
440 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
441 "\nAs a JSON-RPC call\n"
442 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
443 },
444 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
445 {
446 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
447 if (!pwallet) return NullUniValue;
448
449 EnsureLegacyScriptPubKeyMan(*pwallet, true);
450
451 std::string strLabel;
452 if (!request.params[1].isNull())
453 strLabel = request.params[1].get_str();
454
455 // Whether to perform rescan after import
456 bool fRescan = true;
457 if (!request.params[2].isNull())
458 fRescan = request.params[2].get_bool();
459
460 if (fRescan && pwallet->chain().havePruned()) {
461 // Exit early and print an error.
462 // If a block is pruned after this check, we will import the key(s),
463 // but fail the rescan with a generic error.
464 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
465 }
466
467 WalletRescanReserver reserver(*pwallet);
468 if (fRescan && !reserver.reserve()) {
469 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
470 }
471
472 if (!IsHex(request.params[0].get_str()))
473 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
474 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
475 CPubKey pubKey(data);
476 if (!pubKey.IsFullyValid())
477 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
478
479 {
480 LOCK(pwallet->cs_wallet);
481
482 std::set<CScript> script_pub_keys;
483 for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
484 script_pub_keys.insert(GetScriptForDestination(dest));
485 }
486
487 pwallet->MarkDirty();
488
489 pwallet->ImportScriptPubKeys(strLabel, script_pub_keys, true /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
490
491 pwallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , {} /* key_origins */, false /* add_keypool */, false /* internal */, 1 /* timestamp */);
492 }
493 if (fRescan)
494 {
495 RescanWallet(*pwallet, reserver);
496 {
497 LOCK(pwallet->cs_wallet);
498 pwallet->ReacceptWalletTransactions();
499 }
500 }
501
502 return NullUniValue;
503 },
504 };
505 }
506
507
importwallet()508 RPCHelpMan importwallet()
509 {
510 return RPCHelpMan{"importwallet",
511 "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
512 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
513 {
514 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
515 },
516 RPCResult{RPCResult::Type::NONE, "", ""},
517 RPCExamples{
518 "\nDump the wallet\n"
519 + HelpExampleCli("dumpwallet", "\"test\"") +
520 "\nImport the wallet\n"
521 + HelpExampleCli("importwallet", "\"test\"") +
522 "\nImport using the json rpc call\n"
523 + HelpExampleRpc("importwallet", "\"test\"")
524 },
525 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
526 {
527 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
528 if (!pwallet) return NullUniValue;
529
530 EnsureLegacyScriptPubKeyMan(*pwallet, true);
531
532 if (pwallet->chain().havePruned()) {
533 // Exit early and print an error.
534 // If a block is pruned after this check, we will import the key(s),
535 // but fail the rescan with a generic error.
536 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
537 }
538
539 WalletRescanReserver reserver(*pwallet);
540 if (!reserver.reserve()) {
541 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
542 }
543
544 int64_t nTimeBegin = 0;
545 bool fGood = true;
546 {
547 LOCK(pwallet->cs_wallet);
548
549 EnsureWalletIsUnlocked(*pwallet);
550
551 fsbridge::ifstream file;
552 file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
553 if (!file.is_open()) {
554 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
555 }
556 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nTimeBegin)));
557
558 int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
559 file.seekg(0, file.beg);
560
561 // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
562 // we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
563 pwallet->chain().showProgress(strprintf("%s " + _("Importing…").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
564 std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
565 std::vector<std::pair<CScript, int64_t>> scripts;
566 while (file.good()) {
567 pwallet->chain().showProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
568 std::string line;
569 std::getline(file, line);
570 if (line.empty() || line[0] == '#')
571 continue;
572
573 std::vector<std::string> vstr;
574 boost::split(vstr, line, boost::is_any_of(" "));
575 if (vstr.size() < 2)
576 continue;
577 CKey key = DecodeSecret(vstr[0]);
578 if (key.IsValid()) {
579 int64_t nTime = ParseISO8601DateTime(vstr[1]);
580 std::string strLabel;
581 bool fLabel = true;
582 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
583 if (vstr[nStr].front() == '#')
584 break;
585 if (vstr[nStr] == "change=1")
586 fLabel = false;
587 if (vstr[nStr] == "reserve=1")
588 fLabel = false;
589 if (vstr[nStr].substr(0,6) == "label=") {
590 strLabel = DecodeDumpString(vstr[nStr].substr(6));
591 fLabel = true;
592 }
593 }
594 keys.push_back(std::make_tuple(key, nTime, fLabel, strLabel));
595 } else if(IsHex(vstr[0])) {
596 std::vector<unsigned char> vData(ParseHex(vstr[0]));
597 CScript script = CScript(vData.begin(), vData.end());
598 int64_t birth_time = ParseISO8601DateTime(vstr[1]);
599 scripts.push_back(std::pair<CScript, int64_t>(script, birth_time));
600 }
601 }
602 file.close();
603 // We now know whether we are importing private keys, so we can error if private keys are disabled
604 if (keys.size() > 0 && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
605 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
606 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when private keys are disabled");
607 }
608 double total = (double)(keys.size() + scripts.size());
609 double progress = 0;
610 for (const auto& key_tuple : keys) {
611 pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
612 const CKey& key = std::get<0>(key_tuple);
613 int64_t time = std::get<1>(key_tuple);
614 bool has_label = std::get<2>(key_tuple);
615 std::string label = std::get<3>(key_tuple);
616
617 CPubKey pubkey = key.GetPubKey();
618 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
619 CKeyID keyid = pubkey.GetID();
620
621 pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
622
623 if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
624 pwallet->WalletLogPrintf("Error importing key for %s\n", EncodeDestination(PKHash(keyid)));
625 fGood = false;
626 continue;
627 }
628
629 if (has_label)
630 pwallet->SetAddressBook(PKHash(keyid), label, "receive");
631
632 nTimeBegin = std::min(nTimeBegin, time);
633 progress++;
634 }
635 for (const auto& script_pair : scripts) {
636 pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
637 const CScript& script = script_pair.first;
638 int64_t time = script_pair.second;
639
640 if (!pwallet->ImportScripts({script}, time)) {
641 pwallet->WalletLogPrintf("Error importing script %s\n", HexStr(script));
642 fGood = false;
643 continue;
644 }
645 if (time > 0) {
646 nTimeBegin = std::min(nTimeBegin, time);
647 }
648
649 progress++;
650 }
651 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
652 }
653 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
654 RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
655 pwallet->MarkDirty();
656
657 if (!fGood)
658 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
659
660 return NullUniValue;
661 },
662 };
663 }
664
dumpprivkey()665 RPCHelpMan dumpprivkey()
666 {
667 return RPCHelpMan{"dumpprivkey",
668 "\nReveals the private key corresponding to 'address'.\n"
669 "Then the importprivkey can be used with this output\n",
670 {
671 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
672 },
673 RPCResult{
674 RPCResult::Type::STR, "key", "The private key"
675 },
676 RPCExamples{
677 HelpExampleCli("dumpprivkey", "\"myaddress\"")
678 + HelpExampleCli("importprivkey", "\"mykey\"")
679 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
680 },
681 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
682 {
683 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
684 if (!pwallet) return NullUniValue;
685
686 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
687
688 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
689
690 EnsureWalletIsUnlocked(*pwallet);
691
692 std::string strAddress = request.params[0].get_str();
693 CTxDestination dest = DecodeDestination(strAddress);
694 if (!IsValidDestination(dest)) {
695 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
696 }
697 auto keyid = GetKeyForDestination(spk_man, dest);
698 if (keyid.IsNull()) {
699 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
700 }
701 CKey vchSecret;
702 if (!spk_man.GetKey(keyid, vchSecret)) {
703 throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
704 }
705 return EncodeSecret(vchSecret);
706 },
707 };
708 }
709
710
dumpwallet()711 RPCHelpMan dumpwallet()
712 {
713 return RPCHelpMan{"dumpwallet",
714 "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
715 "Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
716 "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
717 "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
718 {
719 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (absolute path recommended)"},
720 },
721 RPCResult{
722 RPCResult::Type::OBJ, "", "",
723 {
724 {RPCResult::Type::STR, "filename", "The filename with full absolute path"},
725 }
726 },
727 RPCExamples{
728 HelpExampleCli("dumpwallet", "\"test\"")
729 + HelpExampleRpc("dumpwallet", "\"test\"")
730 },
731 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
732 {
733 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
734 if (!pwallet) return NullUniValue;
735
736 CWallet& wallet = *pwallet;
737 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(wallet);
738
739 // Make sure the results are valid at least up to the most recent block
740 // the user could have gotten from another RPC command prior to now
741 wallet.BlockUntilSyncedToCurrentChain();
742
743 LOCK(wallet.cs_wallet);
744
745 EnsureWalletIsUnlocked(wallet);
746
747 fs::path filepath = request.params[0].get_str();
748 filepath = fs::absolute(filepath);
749
750 /* Prevent arbitrary files from being overwritten. There have been reports
751 * that users have overwritten wallet files this way:
752 * https://github.com/bitcoin/bitcoin/issues/9934
753 * It may also avoid other security issues.
754 */
755 if (fs::exists(filepath)) {
756 throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first");
757 }
758
759 fsbridge::ofstream file;
760 file.open(filepath);
761 if (!file.is_open())
762 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
763
764 std::map<CKeyID, int64_t> mapKeyBirth;
765 wallet.GetKeyBirthTimes(mapKeyBirth);
766
767 int64_t block_time = 0;
768 CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
769
770 // Note: To avoid a lock order issue, access to cs_main must be locked before cs_KeyStore.
771 // So we do the two things in this function that lock cs_main first: GetKeyBirthTimes, and findBlock.
772 LOCK(spk_man.cs_KeyStore);
773
774 const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
775 std::set<CScriptID> scripts = spk_man.GetCScripts();
776
777 // sort time/key pairs
778 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
779 for (const auto& entry : mapKeyBirth) {
780 vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
781 }
782 mapKeyBirth.clear();
783 std::sort(vKeyBirth.begin(), vKeyBirth.end());
784
785 // produce output
786 file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
787 file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
788 file << strprintf("# * Best block at time of backup was %i (%s),\n", wallet.GetLastBlockHeight(), wallet.GetLastBlockHash().ToString());
789 file << strprintf("# mined on %s\n", FormatISO8601DateTime(block_time));
790 file << "\n";
791
792 // add the base58check encoded extended master if the wallet uses HD
793 CKeyID seed_id = spk_man.GetHDChain().seed_id;
794 if (!seed_id.IsNull())
795 {
796 CKey seed;
797 if (spk_man.GetKey(seed_id, seed)) {
798 CExtKey masterKey;
799 masterKey.SetSeed(seed.begin(), seed.size());
800
801 file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
802 }
803 }
804 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
805 const CKeyID &keyid = it->second;
806 std::string strTime = FormatISO8601DateTime(it->first);
807 std::string strAddr;
808 std::string strLabel;
809 CKey key;
810 if (spk_man.GetKey(keyid, key)) {
811 file << strprintf("%s %s ", EncodeSecret(key), strTime);
812 if (GetWalletAddressesForKey(&spk_man, wallet, keyid, strAddr, strLabel)) {
813 file << strprintf("label=%s", strLabel);
814 } else if (keyid == seed_id) {
815 file << "hdseed=1";
816 } else if (mapKeyPool.count(keyid)) {
817 file << "reserve=1";
818 } else if (spk_man.mapKeyMetadata[keyid].hdKeypath == "s") {
819 file << "inactivehdseed=1";
820 } else {
821 file << "change=1";
822 }
823 file << strprintf(" # addr=%s%s\n", strAddr, (spk_man.mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(spk_man.mapKeyMetadata[keyid].key_origin.path) : ""));
824 }
825 }
826 file << "\n";
827 for (const CScriptID &scriptid : scripts) {
828 CScript script;
829 std::string create_time = "0";
830 std::string address = EncodeDestination(ScriptHash(scriptid));
831 // get birth times for scripts with metadata
832 auto it = spk_man.m_script_metadata.find(scriptid);
833 if (it != spk_man.m_script_metadata.end()) {
834 create_time = FormatISO8601DateTime(it->second.nCreateTime);
835 }
836 if(spk_man.GetCScript(scriptid, script)) {
837 file << strprintf("%s %s script=1", HexStr(script), create_time);
838 file << strprintf(" # addr=%s\n", address);
839 }
840 }
841 file << "\n";
842 file << "# End of dump\n";
843 file.close();
844
845 UniValue reply(UniValue::VOBJ);
846 reply.pushKV("filename", filepath.string());
847
848 return reply;
849 },
850 };
851 }
852
853 struct ImportData
854 {
855 // Input data
856 std::unique_ptr<CScript> redeemscript; //!< Provided redeemScript; will be moved to `import_scripts` if relevant.
857 std::unique_ptr<CScript> witnessscript; //!< Provided witnessScript; will be moved to `import_scripts` if relevant.
858
859 // Output data
860 std::set<CScript> import_scripts;
861 std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
862 std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
863 };
864
865 enum class ScriptContext
866 {
867 TOP, //!< Top-level scriptPubKey
868 P2SH, //!< P2SH redeemScript
869 WITNESS_V0, //!< P2WSH witnessScript
870 };
871
872 // Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used.
873 // Returns an error string, or the empty string for success.
RecurseImportData(const CScript & script,ImportData & import_data,const ScriptContext script_ctx)874 static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx)
875 {
876 // Use Solver to obtain script type and parsed pubkeys or hashes:
877 std::vector<std::vector<unsigned char>> solverdata;
878 TxoutType script_type = Solver(script, solverdata);
879
880 switch (script_type) {
881 case TxoutType::PUBKEY: {
882 CPubKey pubkey(solverdata[0]);
883 import_data.used_keys.emplace(pubkey.GetID(), false);
884 return "";
885 }
886 case TxoutType::PUBKEYHASH: {
887 CKeyID id = CKeyID(uint160(solverdata[0]));
888 import_data.used_keys[id] = true;
889 return "";
890 }
891 case TxoutType::SCRIPTHASH: {
892 if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
893 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
894 CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
895 CScriptID id = CScriptID(uint160(solverdata[0]));
896 auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
897 if (!subscript) return "missing redeemscript";
898 if (CScriptID(*subscript) != id) return "redeemScript does not match the scriptPubKey";
899 import_data.import_scripts.emplace(*subscript);
900 return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
901 }
902 case TxoutType::MULTISIG: {
903 for (size_t i = 1; i + 1< solverdata.size(); ++i) {
904 CPubKey pubkey(solverdata[i]);
905 import_data.used_keys.emplace(pubkey.GetID(), false);
906 }
907 return "";
908 }
909 case TxoutType::WITNESS_V0_SCRIPTHASH: {
910 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
911 uint256 fullid(solverdata[0]);
912 CScriptID id;
913 CRIPEMD160().Write(fullid.begin(), fullid.size()).Finalize(id.begin());
914 auto subscript = std::move(import_data.witnessscript); // Remove redeemscript from import_data to check for superfluous script later.
915 if (!subscript) return "missing witnessscript";
916 if (CScriptID(*subscript) != id) return "witnessScript does not match the scriptPubKey or redeemScript";
917 if (script_ctx == ScriptContext::TOP) {
918 import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WSH requires the TOP script imported (see script/ismine.cpp)
919 }
920 import_data.import_scripts.emplace(*subscript);
921 return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
922 }
923 case TxoutType::WITNESS_V0_KEYHASH: {
924 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
925 CKeyID id = CKeyID(uint160(solverdata[0]));
926 import_data.used_keys[id] = true;
927 if (script_ctx == ScriptContext::TOP) {
928 import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WPKH requires the TOP script imported (see script/ismine.cpp)
929 }
930 return "";
931 }
932 case TxoutType::NULL_DATA:
933 return "unspendable script";
934 case TxoutType::NONSTANDARD:
935 case TxoutType::WITNESS_UNKNOWN:
936 case TxoutType::WITNESS_V1_TAPROOT:
937 return "unrecognized script";
938 } // no default case, so the compiler can warn about missing cases
939 CHECK_NONFATAL(false);
940 }
941
ProcessImportLegacy(ImportData & import_data,std::map<CKeyID,CPubKey> & pubkey_map,std::map<CKeyID,CKey> & privkey_map,std::set<CScript> & script_pub_keys,bool & have_solving_data,const UniValue & data,std::vector<CKeyID> & ordered_pubkeys)942 static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
943 {
944 UniValue warnings(UniValue::VARR);
945
946 // First ensure scriptPubKey has either a script or JSON with "address" string
947 const UniValue& scriptPubKey = data["scriptPubKey"];
948 bool isScript = scriptPubKey.getType() == UniValue::VSTR;
949 if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
950 throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
951 }
952 const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
953
954 // Optional fields.
955 const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
956 const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
957 const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
958 const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
959 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
960 const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
961
962 if (data.exists("range")) {
963 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for a non-descriptor import");
964 }
965
966 // Generate the script and destination for the scriptPubKey provided
967 CScript script;
968 if (!isScript) {
969 CTxDestination dest = DecodeDestination(output);
970 if (!IsValidDestination(dest)) {
971 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
972 }
973 if (OutputTypeFromDestination(dest) == OutputType::BECH32M) {
974 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
975 }
976 script = GetScriptForDestination(dest);
977 } else {
978 if (!IsHex(output)) {
979 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
980 }
981 std::vector<unsigned char> vData(ParseHex(output));
982 script = CScript(vData.begin(), vData.end());
983 CTxDestination dest;
984 if (!ExtractDestination(script, dest) && !internal) {
985 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
986 }
987 }
988 script_pub_keys.emplace(script);
989
990 // Parse all arguments
991 if (strRedeemScript.size()) {
992 if (!IsHex(strRedeemScript)) {
993 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
994 }
995 auto parsed_redeemscript = ParseHex(strRedeemScript);
996 import_data.redeemscript = std::make_unique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
997 }
998 if (witness_script_hex.size()) {
999 if (!IsHex(witness_script_hex)) {
1000 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
1001 }
1002 auto parsed_witnessscript = ParseHex(witness_script_hex);
1003 import_data.witnessscript = std::make_unique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
1004 }
1005 for (size_t i = 0; i < pubKeys.size(); ++i) {
1006 const auto& str = pubKeys[i].get_str();
1007 if (!IsHex(str)) {
1008 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
1009 }
1010 auto parsed_pubkey = ParseHex(str);
1011 CPubKey pubkey(parsed_pubkey);
1012 if (!pubkey.IsFullyValid()) {
1013 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
1014 }
1015 pubkey_map.emplace(pubkey.GetID(), pubkey);
1016 ordered_pubkeys.push_back(pubkey.GetID());
1017 }
1018 for (size_t i = 0; i < keys.size(); ++i) {
1019 const auto& str = keys[i].get_str();
1020 CKey key = DecodeSecret(str);
1021 if (!key.IsValid()) {
1022 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
1023 }
1024 CPubKey pubkey = key.GetPubKey();
1025 CKeyID id = pubkey.GetID();
1026 if (pubkey_map.count(id)) {
1027 pubkey_map.erase(id);
1028 }
1029 privkey_map.emplace(id, key);
1030 }
1031
1032
1033 // Verify and process input data
1034 have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
1035 if (have_solving_data) {
1036 // Match up data in import_data with the scriptPubKey in script.
1037 auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
1038
1039 // Verify whether the watchonly option corresponds to the availability of private keys.
1040 bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
1041 if (!watchOnly && !spendable) {
1042 warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1043 }
1044 if (watchOnly && spendable) {
1045 warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1046 }
1047
1048 // Check that all required keys for solvability are provided.
1049 if (error.empty()) {
1050 for (const auto& require_key : import_data.used_keys) {
1051 if (!require_key.second) continue; // Not a required key
1052 if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
1053 error = "some required keys are missing";
1054 }
1055 }
1056 }
1057
1058 if (!error.empty()) {
1059 warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
1060 import_data = ImportData();
1061 pubkey_map.clear();
1062 privkey_map.clear();
1063 have_solving_data = false;
1064 } else {
1065 // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
1066 if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
1067 if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
1068 for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
1069 auto oldit = it++;
1070 if (import_data.used_keys.count(oldit->first) == 0) {
1071 warnings.push_back("Ignoring irrelevant private key.");
1072 privkey_map.erase(oldit);
1073 }
1074 }
1075 for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
1076 auto oldit = it++;
1077 auto key_data_it = import_data.used_keys.find(oldit->first);
1078 if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
1079 warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
1080 pubkey_map.erase(oldit);
1081 }
1082 }
1083 }
1084 }
1085
1086 return warnings;
1087 }
1088
ProcessImportDescriptor(ImportData & import_data,std::map<CKeyID,CPubKey> & pubkey_map,std::map<CKeyID,CKey> & privkey_map,std::set<CScript> & script_pub_keys,bool & have_solving_data,const UniValue & data,std::vector<CKeyID> & ordered_pubkeys)1089 static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
1090 {
1091 UniValue warnings(UniValue::VARR);
1092
1093 const std::string& descriptor = data["desc"].get_str();
1094 FlatSigningProvider keys;
1095 std::string error;
1096 auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
1097 if (!parsed_desc) {
1098 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
1099 }
1100 if (parsed_desc->GetOutputType() == OutputType::BECH32M) {
1101 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets");
1102 }
1103
1104 have_solving_data = parsed_desc->IsSolvable();
1105 const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
1106
1107 int64_t range_start = 0, range_end = 0;
1108 if (!parsed_desc->IsRange() && data.exists("range")) {
1109 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1110 } else if (parsed_desc->IsRange()) {
1111 if (!data.exists("range")) {
1112 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
1113 }
1114 std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
1115 }
1116
1117 const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
1118
1119 // Expand all descriptors to get public keys and scripts, and private keys if available.
1120 for (int i = range_start; i <= range_end; ++i) {
1121 FlatSigningProvider out_keys;
1122 std::vector<CScript> scripts_temp;
1123 parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1124 std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1125 for (const auto& key_pair : out_keys.pubkeys) {
1126 ordered_pubkeys.push_back(key_pair.first);
1127 }
1128
1129 for (const auto& x : out_keys.scripts) {
1130 import_data.import_scripts.emplace(x.second);
1131 }
1132
1133 parsed_desc->ExpandPrivate(i, keys, out_keys);
1134
1135 std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1136 std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1137 import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1138 }
1139
1140 for (size_t i = 0; i < priv_keys.size(); ++i) {
1141 const auto& str = priv_keys[i].get_str();
1142 CKey key = DecodeSecret(str);
1143 if (!key.IsValid()) {
1144 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
1145 }
1146 CPubKey pubkey = key.GetPubKey();
1147 CKeyID id = pubkey.GetID();
1148
1149 // Check if this private key corresponds to a public key from the descriptor
1150 if (!pubkey_map.count(id)) {
1151 warnings.push_back("Ignoring irrelevant private key.");
1152 } else {
1153 privkey_map.emplace(id, key);
1154 }
1155 }
1156
1157 // Check if all the public keys have corresponding private keys in the import for spendability.
1158 // This does not take into account threshold multisigs which could be spendable without all keys.
1159 // Thus, threshold multisigs without all keys will be considered not spendable here, even if they are,
1160 // perhaps triggering a false warning message. This is consistent with the current wallet IsMine check.
1161 bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
1162 [&](const std::pair<CKeyID, CPubKey>& used_key) {
1163 return privkey_map.count(used_key.first) > 0;
1164 }) && std::all_of(import_data.key_origins.begin(), import_data.key_origins.end(),
1165 [&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
1166 return privkey_map.count(entry.first) > 0;
1167 });
1168 if (!watch_only && !spendable) {
1169 warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1170 }
1171 if (watch_only && spendable) {
1172 warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1173 }
1174
1175 return warnings;
1176 }
1177
ProcessImport(CWallet & wallet,const UniValue & data,const int64_t timestamp)1178 static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1179 {
1180 UniValue warnings(UniValue::VARR);
1181 UniValue result(UniValue::VOBJ);
1182
1183 try {
1184 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1185 // Internal addresses should not have a label
1186 if (internal && data.exists("label")) {
1187 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
1188 }
1189 const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1190 const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
1191
1192 // Add to keypool only works with privkeys disabled
1193 if (add_keypool && !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1194 throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
1195 }
1196
1197 ImportData import_data;
1198 std::map<CKeyID, CPubKey> pubkey_map;
1199 std::map<CKeyID, CKey> privkey_map;
1200 std::set<CScript> script_pub_keys;
1201 std::vector<CKeyID> ordered_pubkeys;
1202 bool have_solving_data;
1203
1204 if (data.exists("scriptPubKey") && data.exists("desc")) {
1205 throw JSONRPCError(RPC_INVALID_PARAMETER, "Both a descriptor and a scriptPubKey should not be provided.");
1206 } else if (data.exists("scriptPubKey")) {
1207 warnings = ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1208 } else if (data.exists("desc")) {
1209 warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1210 } else {
1211 throw JSONRPCError(RPC_INVALID_PARAMETER, "Either a descriptor or scriptPubKey must be provided.");
1212 }
1213
1214 // If private keys are disabled, abort if private keys are being imported
1215 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
1216 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
1217 }
1218
1219 // Check whether we have any work to do
1220 for (const CScript& script : script_pub_keys) {
1221 if (wallet.IsMine(script) & ISMINE_SPENDABLE) {
1222 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
1223 }
1224 }
1225
1226 // All good, time to import
1227 wallet.MarkDirty();
1228 if (!wallet.ImportScripts(import_data.import_scripts, timestamp)) {
1229 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
1230 }
1231 if (!wallet.ImportPrivKeys(privkey_map, timestamp)) {
1232 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
1233 }
1234 if (!wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
1235 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
1236 }
1237 if (!wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
1238 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
1239 }
1240
1241 result.pushKV("success", UniValue(true));
1242 } catch (const UniValue& e) {
1243 result.pushKV("success", UniValue(false));
1244 result.pushKV("error", e);
1245 } catch (...) {
1246 result.pushKV("success", UniValue(false));
1247
1248 result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
1249 }
1250 if (warnings.size()) result.pushKV("warnings", warnings);
1251 return result;
1252 }
1253
GetImportTimestamp(const UniValue & data,int64_t now)1254 static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
1255 {
1256 if (data.exists("timestamp")) {
1257 const UniValue& timestamp = data["timestamp"];
1258 if (timestamp.isNum()) {
1259 return timestamp.get_int64();
1260 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
1261 return now;
1262 }
1263 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
1264 }
1265 throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
1266 }
1267
importmulti()1268 RPCHelpMan importmulti()
1269 {
1270 return RPCHelpMan{"importmulti",
1271 "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
1272 "If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
1273 "Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
1274 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
1275 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
1276 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
1277 {
1278 {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1279 {
1280 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
1281 {
1282 {"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
1283 {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
1284 /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
1285 },
1286 {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n"
1287 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1288 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1289 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1290 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1291 " creation time of all keys being imported by the importmulti call will be scanned.",
1292 /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
1293 },
1294 {"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
1295 {"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
1296 {"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
1297 {
1298 {"pubKey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
1299 }
1300 },
1301 {"keys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
1302 {
1303 {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
1304 }
1305 },
1306 {"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"},
1307 {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
1308 {"watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be considered watchonly."},
1309 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
1310 {"keypool", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
1311 },
1312 },
1313 },
1314 "\"requests\""},
1315 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
1316 {
1317 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Stating if should rescan the blockchain after all imports"},
1318 },
1319 "\"options\""},
1320 },
1321 RPCResult{
1322 RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
1323 {
1324 {RPCResult::Type::OBJ, "", "",
1325 {
1326 {RPCResult::Type::BOOL, "success", ""},
1327 {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
1328 {
1329 {RPCResult::Type::STR, "", ""},
1330 }},
1331 {RPCResult::Type::OBJ, "error", /* optional */ true, "",
1332 {
1333 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1334 }},
1335 }},
1336 }
1337 },
1338 RPCExamples{
1339 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1340 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1341 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
1342 },
1343 [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
1344 {
1345 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
1346 if (!pwallet) return NullUniValue;
1347
1348 RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1349
1350 EnsureLegacyScriptPubKeyMan(*pwallet, true);
1351
1352 const UniValue& requests = mainRequest.params[0];
1353
1354 //Default options
1355 bool fRescan = true;
1356
1357 if (!mainRequest.params[1].isNull()) {
1358 const UniValue& options = mainRequest.params[1];
1359
1360 if (options.exists("rescan")) {
1361 fRescan = options["rescan"].get_bool();
1362 }
1363 }
1364
1365 WalletRescanReserver reserver(*pwallet);
1366 if (fRescan && !reserver.reserve()) {
1367 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1368 }
1369
1370 int64_t now = 0;
1371 bool fRunScan = false;
1372 int64_t nLowestTimestamp = 0;
1373 UniValue response(UniValue::VARR);
1374 {
1375 LOCK(pwallet->cs_wallet);
1376 EnsureWalletIsUnlocked(*pwallet);
1377
1378 // Verify all timestamps are present before importing any keys.
1379 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
1380 for (const UniValue& data : requests.getValues()) {
1381 GetImportTimestamp(data, now);
1382 }
1383
1384 const int64_t minimumTimestamp = 1;
1385
1386 for (const UniValue& data : requests.getValues()) {
1387 const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
1388 const UniValue result = ProcessImport(*pwallet, data, timestamp);
1389 response.push_back(result);
1390
1391 if (!fRescan) {
1392 continue;
1393 }
1394
1395 // If at least one request was successful then allow rescan.
1396 if (result["success"].get_bool()) {
1397 fRunScan = true;
1398 }
1399
1400 // Get the lowest timestamp.
1401 if (timestamp < nLowestTimestamp) {
1402 nLowestTimestamp = timestamp;
1403 }
1404 }
1405 }
1406 if (fRescan && fRunScan && requests.size()) {
1407 int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
1408 {
1409 LOCK(pwallet->cs_wallet);
1410 pwallet->ReacceptWalletTransactions();
1411 }
1412
1413 if (pwallet->IsAbortingRescan()) {
1414 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
1415 }
1416 if (scannedTime > nLowestTimestamp) {
1417 std::vector<UniValue> results = response.getValues();
1418 response.clear();
1419 response.setArray();
1420 size_t i = 0;
1421 for (const UniValue& request : requests.getValues()) {
1422 // If key creation date is within the successfully scanned
1423 // range, or if the import result already has an error set, let
1424 // the result stand unmodified. Otherwise replace the result
1425 // with an error message.
1426 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1427 response.push_back(results.at(i));
1428 } else {
1429 UniValue result = UniValue(UniValue::VOBJ);
1430 result.pushKV("success", UniValue(false));
1431 result.pushKV(
1432 "error",
1433 JSONRPCError(
1434 RPC_MISC_ERROR,
1435 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1436 "block from time %d, which is after or within %d seconds of key creation, and "
1437 "could contain transactions pertaining to the key. As a result, transactions "
1438 "and coins using this key may not appear in the wallet. This error could be "
1439 "caused by pruning or data corruption (see bitcoind log for details) and could "
1440 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1441 "and -rescan options).",
1442 GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1443 response.push_back(std::move(result));
1444 }
1445 ++i;
1446 }
1447 }
1448 }
1449
1450 return response;
1451 },
1452 };
1453 }
1454
ProcessDescriptorImport(CWallet & wallet,const UniValue & data,const int64_t timestamp)1455 static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1456 {
1457 UniValue warnings(UniValue::VARR);
1458 UniValue result(UniValue::VOBJ);
1459
1460 try {
1461 if (!data.exists("desc")) {
1462 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");
1463 }
1464
1465 const std::string& descriptor = data["desc"].get_str();
1466 const bool active = data.exists("active") ? data["active"].get_bool() : false;
1467 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1468 const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1469
1470 // Parse descriptor string
1471 FlatSigningProvider keys;
1472 std::string error;
1473 auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
1474 if (!parsed_desc) {
1475 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
1476 }
1477
1478 // Range check
1479 int64_t range_start = 0, range_end = 1, next_index = 0;
1480 if (!parsed_desc->IsRange() && data.exists("range")) {
1481 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1482 } else if (parsed_desc->IsRange()) {
1483 if (data.exists("range")) {
1484 auto range = ParseDescriptorRange(data["range"]);
1485 range_start = range.first;
1486 range_end = range.second + 1; // Specified range end is inclusive, but we need range end as exclusive
1487 } else {
1488 warnings.push_back("Range not given, using default keypool range");
1489 range_start = 0;
1490 range_end = gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE);
1491 }
1492 next_index = range_start;
1493
1494 if (data.exists("next_index")) {
1495 next_index = data["next_index"].get_int64();
1496 // bound checks
1497 if (next_index < range_start || next_index >= range_end) {
1498 throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");
1499 }
1500 }
1501 }
1502
1503 // Active descriptors must be ranged
1504 if (active && !parsed_desc->IsRange()) {
1505 throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");
1506 }
1507
1508 // Ranged descriptors should not have a label
1509 if (data.exists("range") && data.exists("label")) {
1510 throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
1511 }
1512
1513 // Internal addresses should not have a label either
1514 if (internal && data.exists("label")) {
1515 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
1516 }
1517
1518 // Combo descriptor check
1519 if (active && !parsed_desc->IsSingleType()) {
1520 throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
1521 }
1522
1523 // If the wallet disabled private keys, abort if private keys exist
1524 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
1525 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
1526 }
1527
1528 // Need to ExpandPrivate to check if private keys are available for all pubkeys
1529 FlatSigningProvider expand_keys;
1530 std::vector<CScript> scripts;
1531 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
1532 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
1533 }
1534 parsed_desc->ExpandPrivate(0, keys, expand_keys);
1535
1536 // Check if all private keys are provided
1537 bool have_all_privkeys = !expand_keys.keys.empty();
1538 for (const auto& entry : expand_keys.origins) {
1539 const CKeyID& key_id = entry.first;
1540 CKey key;
1541 if (!expand_keys.GetKey(key_id, key)) {
1542 have_all_privkeys = false;
1543 break;
1544 }
1545 }
1546
1547 // Taproot descriptors cannot be imported if Taproot is not yet active.
1548 // Check if this is a Taproot descriptor
1549 CTxDestination dest;
1550 ExtractDestination(scripts[0], dest);
1551 if (std::holds_alternative<WitnessV1Taproot>(dest)) {
1552 // Check if Taproot is active
1553 if (!wallet.chain().isTaprootActive()) {
1554 // Taproot is not active, raise an error
1555 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
1556 }
1557 }
1558
1559 // If private keys are enabled, check some things.
1560 if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1561 if (keys.keys.empty()) {
1562 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
1563 }
1564 if (!have_all_privkeys) {
1565 warnings.push_back("Not all private keys provided. Some wallet functionality may return unexpected errors");
1566 }
1567 }
1568
1569 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
1570
1571 // Check if the wallet already contains the descriptor
1572 auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
1573 if (existing_spk_manager) {
1574 if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
1575 throw JSONRPCError(RPC_INVALID_PARAMETER, error);
1576 }
1577 }
1578
1579 // Add descriptor to the wallet
1580 auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
1581 if (spk_manager == nullptr) {
1582 throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
1583 }
1584
1585 // Set descriptor as active if necessary
1586 if (active) {
1587 if (!w_desc.descriptor->GetOutputType()) {
1588 warnings.push_back("Unknown output type, cannot set descriptor to active.");
1589 } else {
1590 wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1591 }
1592 } else {
1593 if (w_desc.descriptor->GetOutputType()) {
1594 wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1595 }
1596 }
1597
1598 result.pushKV("success", UniValue(true));
1599 } catch (const UniValue& e) {
1600 result.pushKV("success", UniValue(false));
1601 result.pushKV("error", e);
1602 }
1603 if (warnings.size()) result.pushKV("warnings", warnings);
1604 return result;
1605 }
1606
importdescriptors()1607 RPCHelpMan importdescriptors()
1608 {
1609 return RPCHelpMan{"importdescriptors",
1610 "\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
1611 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
1612 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
1613 {
1614 {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1615 {
1616 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
1617 {
1618 {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
1619 {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
1620 {"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"},
1621 {"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"},
1622 {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
1623 " Use the string \"now\" to substitute the current synced blockchain time.\n"
1624 " \"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
1625 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
1626 " of all descriptors being imported will be scanned.",
1627 /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
1628 },
1629 {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
1630 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
1631 },
1632 },
1633 },
1634 "\"requests\""},
1635 },
1636 RPCResult{
1637 RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
1638 {
1639 {RPCResult::Type::OBJ, "", "",
1640 {
1641 {RPCResult::Type::BOOL, "success", ""},
1642 {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
1643 {
1644 {RPCResult::Type::STR, "", ""},
1645 }},
1646 {RPCResult::Type::OBJ, "error", /* optional */ true, "",
1647 {
1648 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1649 }},
1650 }},
1651 }
1652 },
1653 RPCExamples{
1654 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
1655 "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1656 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
1657 },
1658 [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
1659 {
1660 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
1661 if (!pwallet) return NullUniValue;
1662
1663 // Make sure wallet is a descriptor wallet
1664 if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
1665 throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
1666 }
1667
1668 RPCTypeCheck(main_request.params, {UniValue::VARR, UniValue::VOBJ});
1669
1670 WalletRescanReserver reserver(*pwallet);
1671 if (!reserver.reserve()) {
1672 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1673 }
1674
1675 const UniValue& requests = main_request.params[0];
1676 const int64_t minimum_timestamp = 1;
1677 int64_t now = 0;
1678 int64_t lowest_timestamp = 0;
1679 bool rescan = false;
1680 UniValue response(UniValue::VARR);
1681 {
1682 LOCK(pwallet->cs_wallet);
1683 EnsureWalletIsUnlocked(*pwallet);
1684
1685 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
1686
1687 // Get all timestamps and extract the lowest timestamp
1688 for (const UniValue& request : requests.getValues()) {
1689 // This throws an error if "timestamp" doesn't exist
1690 const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
1691 const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
1692 response.push_back(result);
1693
1694 if (lowest_timestamp > timestamp ) {
1695 lowest_timestamp = timestamp;
1696 }
1697
1698 // If we know the chain tip, and at least one request was successful then allow rescan
1699 if (!rescan && result["success"].get_bool()) {
1700 rescan = true;
1701 }
1702 }
1703 pwallet->ConnectScriptPubKeyManNotifiers();
1704 }
1705
1706 // Rescan the blockchain using the lowest timestamp
1707 if (rescan) {
1708 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, true /* update */);
1709 {
1710 LOCK(pwallet->cs_wallet);
1711 pwallet->ReacceptWalletTransactions();
1712 }
1713
1714 if (pwallet->IsAbortingRescan()) {
1715 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
1716 }
1717
1718 if (scanned_time > lowest_timestamp) {
1719 std::vector<UniValue> results = response.getValues();
1720 response.clear();
1721 response.setArray();
1722
1723 // Compose the response
1724 for (unsigned int i = 0; i < requests.size(); ++i) {
1725 const UniValue& request = requests.getValues().at(i);
1726
1727 // If the descriptor timestamp is within the successfully scanned
1728 // range, or if the import result already has an error set, let
1729 // the result stand unmodified. Otherwise replace the result
1730 // with an error message.
1731 if (scanned_time <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1732 response.push_back(results.at(i));
1733 } else {
1734 UniValue result = UniValue(UniValue::VOBJ);
1735 result.pushKV("success", UniValue(false));
1736 result.pushKV(
1737 "error",
1738 JSONRPCError(
1739 RPC_MISC_ERROR,
1740 strprintf("Rescan failed for descriptor with timestamp %d. There was an error reading a "
1741 "block from time %d, which is after or within %d seconds of key creation, and "
1742 "could contain transactions pertaining to the desc. As a result, transactions "
1743 "and coins using this desc may not appear in the wallet. This error could be "
1744 "caused by pruning or data corruption (see bitcoind log for details) and could "
1745 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1746 "and -rescan options).",
1747 GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1748 response.push_back(std::move(result));
1749 }
1750 }
1751 }
1752 }
1753
1754 return response;
1755 },
1756 };
1757 }
1758
listdescriptors()1759 RPCHelpMan listdescriptors()
1760 {
1761 return RPCHelpMan{
1762 "listdescriptors",
1763 "\nList descriptors imported into a descriptor-enabled wallet.",
1764 {},
1765 RPCResult{RPCResult::Type::OBJ, "", "", {
1766 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
1767 {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects",
1768 {
1769 {RPCResult::Type::OBJ, "", "", {
1770 {RPCResult::Type::STR, "desc", "Descriptor string representation"},
1771 {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
1772 {RPCResult::Type::BOOL, "active", "Activeness flag"},
1773 {RPCResult::Type::BOOL, "internal", true, "Whether this is an internal or external descriptor; defined only for active descriptors"},
1774 {RPCResult::Type::ARR_FIXED, "range", true, "Defined only for ranged descriptors", {
1775 {RPCResult::Type::NUM, "", "Range start inclusive"},
1776 {RPCResult::Type::NUM, "", "Range end inclusive"},
1777 }},
1778 {RPCResult::Type::NUM, "next", true, "The next index to generate addresses from; defined only for ranged descriptors"},
1779 }},
1780 }}
1781 }},
1782 RPCExamples{
1783 HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
1784 },
1785 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1786 {
1787 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1788 if (!wallet) return NullUniValue;
1789
1790 if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
1791 throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
1792 }
1793
1794 LOCK(wallet->cs_wallet);
1795
1796 UniValue descriptors(UniValue::VARR);
1797 const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
1798 for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) {
1799 const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
1800 if (!desc_spk_man) {
1801 throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type.");
1802 }
1803 UniValue spk(UniValue::VOBJ);
1804 LOCK(desc_spk_man->cs_desc_man);
1805 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
1806 std::string descriptor;
1807 if (!desc_spk_man->GetDescriptorString(descriptor)) {
1808 throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string.");
1809 }
1810 spk.pushKV("desc", descriptor);
1811 spk.pushKV("timestamp", wallet_descriptor.creation_time);
1812 const bool active = active_spk_mans.count(desc_spk_man) != 0;
1813 spk.pushKV("active", active);
1814 const auto& type = wallet_descriptor.descriptor->GetOutputType();
1815 if (active && type) {
1816 spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man);
1817 }
1818 if (wallet_descriptor.descriptor->IsRange()) {
1819 UniValue range(UniValue::VARR);
1820 range.push_back(wallet_descriptor.range_start);
1821 range.push_back(wallet_descriptor.range_end - 1);
1822 spk.pushKV("range", range);
1823 spk.pushKV("next", wallet_descriptor.next_index);
1824 }
1825 descriptors.push_back(spk);
1826 }
1827
1828 UniValue response(UniValue::VOBJ);
1829 response.pushKV("wallet_name", wallet->GetName());
1830 response.pushKV("descriptors", descriptors);
1831
1832 return response;
1833 },
1834 };
1835 }
1836