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