1 // Copyright (c) 2009-2019 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/server.h>
11 #include <rpc/util.h>
12 #include <script/descriptor.h>
13 #include <script/script.h>
14 #include <script/standard.h>
15 #include <sync.h>
16 #include <util/bip32.h>
17 #include <util/system.h>
18 #include <util/time.h>
19 #include <util/translation.h>
20 #include <wallet/rpcwallet.h>
21 #include <wallet/wallet.h>
22 
23 #include <stdint.h>
24 #include <tuple>
25 
26 #include <boost/algorithm/string.hpp>
27 
28 #include <univalue.h>
29 
30 
31 
EncodeDumpString(const std::string & str)32 std::string static EncodeDumpString(const std::string &str) {
33     std::stringstream ret;
34     for (const unsigned char c : str) {
35         if (c <= 32 || c >= 128 || c == '%') {
36             ret << '%' << HexStr(&c, &c + 1);
37         } else {
38             ret << c;
39         }
40     }
41     return ret.str();
42 }
43 
DecodeDumpString(const std::string & str)44 static std::string DecodeDumpString(const std::string &str) {
45     std::stringstream ret;
46     for (unsigned int pos = 0; pos < str.length(); pos++) {
47         unsigned char c = str[pos];
48         if (c == '%' && pos+2 < str.length()) {
49             c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
50                 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
51             pos += 2;
52         }
53         ret << c;
54     }
55     return ret.str();
56 }
57 
GetWalletAddressesForKey(LegacyScriptPubKeyMan * spk_man,const CWallet * const pwallet,const CKeyID & keyid,std::string & strAddr,std::string & strLabel)58 static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
59 {
60     bool fLabelFound = false;
61     CKey key;
62     spk_man->GetKey(keyid, key);
63     for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
64         const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
65         if (address_book_entry) {
66             if (!strAddr.empty()) {
67                 strAddr += ",";
68             }
69             strAddr += EncodeDestination(dest);
70             strLabel = EncodeDumpString(address_book_entry->GetLabel());
71             fLabelFound = true;
72         }
73     }
74     if (!fLabelFound) {
75         strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), pwallet->m_default_address_type));
76     }
77     return fLabelFound;
78 }
79 
80 static const int64_t TIMESTAMP_MIN = 0;
81 
RescanWallet(CWallet & wallet,const WalletRescanReserver & reserver,int64_t time_begin=TIMESTAMP_MIN,bool update=true)82 static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver, int64_t time_begin = TIMESTAMP_MIN, bool update = true)
83 {
84     int64_t scanned_time = wallet.RescanFromTime(time_begin, reserver, update);
85     if (wallet.IsAbortingRescan()) {
86         throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
87     } else if (scanned_time > time_begin) {
88         throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
89     }
90 }
91 
importprivkey(const JSONRPCRequest & request)92 UniValue importprivkey(const JSONRPCRequest& request)
93 {
94     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
95     CWallet* const pwallet = wallet.get();
96     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
97         return NullUniValue;
98     }
99 
100             RPCHelpMan{"importprivkey",
101                 "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
102                 "Hint: use importmulti to import more than one private key.\n"
103             "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
104             "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"
105             "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
106                 {
107                     {"qtumprivkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
108                     {"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
109                     {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
110                 },
111                 RPCResult{RPCResult::Type::NONE, "", ""},
112                 RPCExamples{
113             "\nDump a private key\n"
114             + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
115             "\nImport the private key with rescan\n"
116             + HelpExampleCli("importprivkey", "\"mykey\"") +
117             "\nImport using a label and without rescan\n"
118             + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
119             "\nImport using default blank label and without rescan\n"
120             + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
121             "\nAs a JSON-RPC call\n"
122             + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
123                 },
124             }.Check(request);
125 
126     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
127         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
128     }
129 
130     EnsureLegacyScriptPubKeyMan(*wallet, true);
131 
132     WalletRescanReserver reserver(pwallet);
133     bool fRescan = true;
134     {
135         auto locked_chain = pwallet->chain().lock();
136         LOCK(pwallet->cs_wallet);
137 
138         EnsureWalletIsUnlocked(pwallet);
139 
140         std::string strSecret = request.params[0].get_str();
141         std::string strLabel = "";
142         if (!request.params[1].isNull())
143             strLabel = request.params[1].get_str();
144 
145         // Whether to perform rescan after import
146         if (!request.params[2].isNull())
147             fRescan = request.params[2].get_bool();
148 
149         if (fRescan && pwallet->chain().havePruned()) {
150             // Exit early and print an error.
151             // If a block is pruned after this check, we will import the key(s),
152             // but fail the rescan with a generic error.
153             throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
154         }
155 
156         if (fRescan && !reserver.reserve()) {
157             throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
158         }
159 
160         CKey key = DecodeSecret(strSecret);
161         if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
162         if (pwallet->m_wallet_unlock_staking_only)
163             throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
164 
165         CPubKey pubkey = key.GetPubKey();
166         CHECK_NONFATAL(key.VerifyPubKey(pubkey));
167         CKeyID vchAddress = pubkey.GetID();
168         {
169             pwallet->MarkDirty();
170 
171             // We don't know which corresponding address will be used;
172             // label all new addresses, and label existing addresses if a
173             // label was passed.
174             for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
175                 if (!request.params[1].isNull() || !pwallet->FindAddressBookEntry(dest)) {
176                     pwallet->SetAddressBook(dest, strLabel, "receive");
177                 }
178             }
179 
180             // Use timestamp of 1 to scan the whole chain
181             if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
182                 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
183             }
184 
185             // Add the wpkh script for this key if possible
186             if (pubkey.IsCompressed()) {
187                 pwallet->ImportScripts({GetScriptForDestination(WitnessV0KeyHash(vchAddress))}, 0 /* timestamp */);
188             }
189         }
190     }
191     if (fRescan) {
192         RescanWallet(*pwallet, reserver);
193     }
194 
195     return NullUniValue;
196 }
197 
abortrescan(const JSONRPCRequest & request)198 UniValue abortrescan(const JSONRPCRequest& request)
199 {
200     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
201     CWallet* const pwallet = wallet.get();
202     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
203         return NullUniValue;
204     }
205 
206             RPCHelpMan{"abortrescan",
207                 "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
208                 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
209                 {},
210                 RPCResult{RPCResult::Type::BOOL, "", "Whether the abort was successful"},
211                 RPCExamples{
212             "\nImport a private key\n"
213             + HelpExampleCli("importprivkey", "\"mykey\"") +
214             "\nAbort the running wallet rescan\n"
215             + HelpExampleCli("abortrescan", "") +
216             "\nAs a JSON-RPC call\n"
217             + HelpExampleRpc("abortrescan", "")
218                 },
219             }.Check(request);
220 
221     if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
222     pwallet->AbortRescan();
223     return true;
224 }
225 
importaddress(const JSONRPCRequest & request)226 UniValue importaddress(const JSONRPCRequest& request)
227 {
228     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
229     CWallet* const pwallet = wallet.get();
230     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
231         return NullUniValue;
232     }
233 
234             RPCHelpMan{"importaddress",
235                 "\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"
236             "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
237             "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"
238             "If you have the full public key, you should call importpubkey instead of this.\n"
239             "Hint: use importmulti to import more than one address.\n"
240             "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
241             "as change, and not show up in many RPCs.\n"
242             "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
243                 {
244                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Qtum address (or hex-encoded script)"},
245                     {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
246                     {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
247                     {"p2sh", RPCArg::Type::BOOL, /* default */ "false", "Add the P2SH version of the script as well"},
248                 },
249                 RPCResult{RPCResult::Type::NONE, "", ""},
250                 RPCExamples{
251             "\nImport an address with rescan\n"
252             + HelpExampleCli("importaddress", "\"myaddress\"") +
253             "\nImport using a label without rescan\n"
254             + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
255             "\nAs a JSON-RPC call\n"
256             + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
257                 },
258             }.Check(request);
259 
260     EnsureLegacyScriptPubKeyMan(*pwallet, true);
261 
262     std::string strLabel;
263     if (!request.params[1].isNull())
264         strLabel = request.params[1].get_str();
265 
266     // Whether to perform rescan after import
267     bool fRescan = true;
268     if (!request.params[2].isNull())
269         fRescan = request.params[2].get_bool();
270 
271     if (fRescan && pwallet->chain().havePruned()) {
272         // Exit early and print an error.
273         // If a block is pruned after this check, we will import the key(s),
274         // but fail the rescan with a generic error.
275         throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
276     }
277 
278     WalletRescanReserver reserver(pwallet);
279     if (fRescan && !reserver.reserve()) {
280         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
281     }
282 
283     // Whether to import a p2sh version, too
284     bool fP2SH = false;
285     if (!request.params[3].isNull())
286         fP2SH = request.params[3].get_bool();
287 
288     {
289         auto locked_chain = pwallet->chain().lock();
290         LOCK(pwallet->cs_wallet);
291 
292         CTxDestination dest = DecodeDestination(request.params[0].get_str());
293         if (IsValidDestination(dest)) {
294             if (fP2SH) {
295                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
296             }
297 
298             pwallet->MarkDirty();
299 
300             pwallet->ImportScriptPubKeys(strLabel, {GetScriptForDestination(dest)}, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
301         } else if (IsHex(request.params[0].get_str())) {
302             std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
303             CScript redeem_script(data.begin(), data.end());
304 
305             std::set<CScript> scripts = {redeem_script};
306             pwallet->ImportScripts(scripts, 0 /* timestamp */);
307 
308             if (fP2SH) {
309                 scripts.insert(GetScriptForDestination(ScriptHash(CScriptID(redeem_script))));
310             }
311 
312             pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
313         } else {
314             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address or script");
315         }
316     }
317     if (fRescan)
318     {
319         RescanWallet(*pwallet, reserver);
320         {
321             auto locked_chain = pwallet->chain().lock();
322             LOCK(pwallet->cs_wallet);
323             pwallet->ReacceptWalletTransactions();
324         }
325     }
326 
327     return NullUniValue;
328 }
329 
importprunedfunds(const JSONRPCRequest & request)330 UniValue importprunedfunds(const JSONRPCRequest& request)
331 {
332     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
333     CWallet* const pwallet = wallet.get();
334     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
335         return NullUniValue;
336     }
337 
338             RPCHelpMan{"importprunedfunds",
339                 "\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",
340                 {
341                     {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
342                     {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
343                 },
344                 RPCResult{RPCResult::Type::NONE, "", ""},
345                 RPCExamples{""},
346             }.Check(request);
347 
348     CMutableTransaction tx;
349     if (!DecodeHexTx(tx, request.params[0].get_str()))
350         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
351     uint256 hashTx = tx.GetHash();
352     CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx)));
353 
354     CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
355     CMerkleBlock merkleBlock;
356     ssMB >> merkleBlock;
357 
358     //Search partial merkle tree in proof for our transaction and index in valid block
359     std::vector<uint256> vMatch;
360     std::vector<unsigned int> vIndex;
361     if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
362         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
363     }
364 
365     auto locked_chain = pwallet->chain().lock();
366     Optional<int> height = locked_chain->getBlockHeight(merkleBlock.header.GetHash());
367     if (height == nullopt) {
368         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
369     }
370 
371     std::vector<uint256>::const_iterator it;
372     if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
373         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
374     }
375 
376     unsigned int txnIndex = vIndex[it - vMatch.begin()];
377 
378     CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *height, merkleBlock.header.GetHash(), txnIndex);
379     wtx.m_confirm = confirm;
380 
381     LOCK(pwallet->cs_wallet);
382 
383     if (pwallet->IsMine(*wtx.tx)) {
384         pwallet->AddToWallet(wtx, false);
385         return NullUniValue;
386     }
387 
388     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
389 }
390 
removeprunedfunds(const JSONRPCRequest & request)391 UniValue removeprunedfunds(const JSONRPCRequest& request)
392 {
393     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
394     CWallet* const pwallet = wallet.get();
395     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
396         return NullUniValue;
397     }
398 
399             RPCHelpMan{"removeprunedfunds",
400                 "\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",
401                 {
402                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
403                 },
404                 RPCResult{RPCResult::Type::NONE, "", ""},
405                 RPCExamples{
406                     HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
407             "\nAs a JSON-RPC call\n"
408             + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
409                 },
410             }.Check(request);
411 
412     auto locked_chain = pwallet->chain().lock();
413     LOCK(pwallet->cs_wallet);
414 
415     uint256 hash(ParseHashV(request.params[0], "txid"));
416     std::vector<uint256> vHash;
417     vHash.push_back(hash);
418     std::vector<uint256> vHashOut;
419 
420     if (pwallet->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
421         throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
422     }
423 
424     if(vHashOut.empty()) {
425         throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
426     }
427 
428     return NullUniValue;
429 }
430 
importpubkey(const JSONRPCRequest & request)431 UniValue importpubkey(const JSONRPCRequest& request)
432 {
433     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
434     CWallet* const pwallet = wallet.get();
435     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
436         return NullUniValue;
437     }
438 
439             RPCHelpMan{"importpubkey",
440                 "\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"
441                 "Hint: use importmulti to import more than one public key.\n"
442             "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
443             "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"
444             "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
445                 {
446                     {"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
447                     {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
448                     {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
449                 },
450                 RPCResult{RPCResult::Type::NONE, "", ""},
451                 RPCExamples{
452             "\nImport a public key with rescan\n"
453             + HelpExampleCli("importpubkey", "\"mypubkey\"") +
454             "\nImport using a label without rescan\n"
455             + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
456             "\nAs a JSON-RPC call\n"
457             + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
458                 },
459             }.Check(request);
460 
461     EnsureLegacyScriptPubKeyMan(*wallet, true);
462 
463     std::string strLabel;
464     if (!request.params[1].isNull())
465         strLabel = request.params[1].get_str();
466 
467     // Whether to perform rescan after import
468     bool fRescan = true;
469     if (!request.params[2].isNull())
470         fRescan = request.params[2].get_bool();
471 
472     if (fRescan && pwallet->chain().havePruned()) {
473         // Exit early and print an error.
474         // If a block is pruned after this check, we will import the key(s),
475         // but fail the rescan with a generic error.
476         throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
477     }
478 
479     WalletRescanReserver reserver(pwallet);
480     if (fRescan && !reserver.reserve()) {
481         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
482     }
483 
484     if (!IsHex(request.params[0].get_str()))
485         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
486     std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
487     CPubKey pubKey(data.begin(), data.end());
488     if (!pubKey.IsFullyValid())
489         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
490 
491     {
492         auto locked_chain = pwallet->chain().lock();
493         LOCK(pwallet->cs_wallet);
494 
495         std::set<CScript> script_pub_keys;
496         for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
497             script_pub_keys.insert(GetScriptForDestination(dest));
498         }
499 
500         pwallet->MarkDirty();
501 
502         pwallet->ImportScriptPubKeys(strLabel, script_pub_keys, true /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
503 
504         pwallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , {} /* key_origins */, false /* add_keypool */, false /* internal */, 1 /* timestamp */);
505     }
506     if (fRescan)
507     {
508         RescanWallet(*pwallet, reserver);
509         {
510             auto locked_chain = pwallet->chain().lock();
511             LOCK(pwallet->cs_wallet);
512             pwallet->ReacceptWalletTransactions();
513         }
514     }
515 
516     return NullUniValue;
517 }
518 
519 
importwallet(const JSONRPCRequest & request)520 UniValue importwallet(const JSONRPCRequest& request)
521 {
522     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
523     CWallet* const pwallet = wallet.get();
524     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
525         return NullUniValue;
526     }
527 
528             RPCHelpMan{"importwallet",
529                 "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
530                 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
531                 {
532                     {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
533                 },
534                 RPCResult{RPCResult::Type::NONE, "", ""},
535                 RPCExamples{
536             "\nDump the wallet\n"
537             + HelpExampleCli("dumpwallet", "\"test\"") +
538             "\nImport the wallet\n"
539             + HelpExampleCli("importwallet", "\"test\"") +
540             "\nImport using the json rpc call\n"
541             + HelpExampleRpc("importwallet", "\"test\"")
542                 },
543             }.Check(request);
544 
545     EnsureLegacyScriptPubKeyMan(*wallet, true);
546 
547     if (pwallet->chain().havePruned()) {
548         // Exit early and print an error.
549         // If a block is pruned after this check, we will import the key(s),
550         // but fail the rescan with a generic error.
551         throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
552     }
553 
554     WalletRescanReserver reserver(pwallet);
555     if (!reserver.reserve()) {
556         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
557     }
558 
559     int64_t nTimeBegin = 0;
560     bool fGood = true;
561     {
562         auto locked_chain = pwallet->chain().lock();
563         LOCK(pwallet->cs_wallet);
564 
565         EnsureWalletIsUnlocked(pwallet);
566 
567         fsbridge::ifstream file;
568         file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
569         if (!file.is_open()) {
570             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
571         }
572         Optional<int> tip_height = locked_chain->getHeight();
573         nTimeBegin = tip_height ? locked_chain->getBlockTime(*tip_height) : 0;
574 
575         int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
576         file.seekg(0, file.beg);
577 
578         // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
579         // we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
580         pwallet->chain().showProgress(strprintf("%s " + _("Importing...").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
581         std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
582         std::vector<std::pair<CScript, int64_t>> scripts;
583         while (file.good()) {
584             pwallet->chain().showProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
585             std::string line;
586             std::getline(file, line);
587             if (line.empty() || line[0] == '#')
588                 continue;
589 
590             std::vector<std::string> vstr;
591             boost::split(vstr, line, boost::is_any_of(" "));
592             if (vstr.size() < 2)
593                 continue;
594             CKey key = DecodeSecret(vstr[0]);
595             if (key.IsValid()) {
596                 int64_t nTime = ParseISO8601DateTime(vstr[1]);
597                 std::string strLabel;
598                 bool fLabel = true;
599                 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
600                     if (vstr[nStr].front() == '#')
601                         break;
602                     if (vstr[nStr] == "change=1")
603                         fLabel = false;
604                     if (vstr[nStr] == "reserve=1")
605                         fLabel = false;
606                     if (vstr[nStr].substr(0,6) == "label=") {
607                         strLabel = DecodeDumpString(vstr[nStr].substr(6));
608                         fLabel = true;
609                     }
610                 }
611                 keys.push_back(std::make_tuple(key, nTime, fLabel, strLabel));
612             } else if(IsHex(vstr[0])) {
613                 std::vector<unsigned char> vData(ParseHex(vstr[0]));
614                 CScript script = CScript(vData.begin(), vData.end());
615                 int64_t birth_time = ParseISO8601DateTime(vstr[1]);
616                 scripts.push_back(std::pair<CScript, int64_t>(script, birth_time));
617             }
618         }
619         file.close();
620         // We now know whether we are importing private keys, so we can error if private keys are disabled
621         if (keys.size() > 0 && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
622             pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
623             throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when private keys are disabled");
624         }
625         double total = (double)(keys.size() + scripts.size());
626         double progress = 0;
627         for (const auto& key_tuple : keys) {
628             pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
629             const CKey& key = std::get<0>(key_tuple);
630             int64_t time = std::get<1>(key_tuple);
631             bool has_label = std::get<2>(key_tuple);
632             std::string label = std::get<3>(key_tuple);
633 
634             CPubKey pubkey = key.GetPubKey();
635             CHECK_NONFATAL(key.VerifyPubKey(pubkey));
636             CKeyID keyid = pubkey.GetID();
637 
638             pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
639 
640             if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
641                 pwallet->WalletLogPrintf("Error importing key for %s\n", EncodeDestination(PKHash(keyid)));
642                 fGood = false;
643                 continue;
644             }
645 
646             if (has_label)
647                 pwallet->SetAddressBook(PKHash(keyid), label, "receive");
648 
649             nTimeBegin = std::min(nTimeBegin, time);
650             progress++;
651         }
652         for (const auto& script_pair : scripts) {
653             pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
654             const CScript& script = script_pair.first;
655             int64_t time = script_pair.second;
656 
657             if (!pwallet->ImportScripts({script}, time)) {
658                 pwallet->WalletLogPrintf("Error importing script %s\n", HexStr(script));
659                 fGood = false;
660                 continue;
661             }
662             if (time > 0) {
663                 nTimeBegin = std::min(nTimeBegin, time);
664             }
665 
666             progress++;
667         }
668         pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
669     }
670     pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
671     RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
672     pwallet->MarkDirty();
673 
674     if (!fGood)
675         throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
676 
677     return NullUniValue;
678 }
679 
dumpprivkey(const JSONRPCRequest & request)680 UniValue dumpprivkey(const JSONRPCRequest& request)
681 {
682     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
683     const CWallet* const pwallet = wallet.get();
684     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
685         return NullUniValue;
686     }
687 
688             RPCHelpMan{"dumpprivkey",
689                 "\nReveals the private key corresponding to 'address'.\n"
690                 "Then the importprivkey can be used with this output\n",
691                 {
692                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address for the private key"},
693                 },
694                 RPCResult{
695                     RPCResult::Type::STR, "key", "The private key"
696                 },
697                 RPCExamples{
698                     HelpExampleCli("dumpprivkey", "\"myaddress\"")
699             + HelpExampleCli("importprivkey", "\"mykey\"")
700             + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
701                 },
702             }.Check(request);
703 
704     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
705 
706     auto locked_chain = pwallet->chain().lock();
707     LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
708 
709     EnsureWalletIsUnlocked(pwallet);
710 
711     std::string strAddress = request.params[0].get_str();
712     CTxDestination dest = DecodeDestination(strAddress);
713     if (!IsValidDestination(dest)) {
714         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
715         if (pwallet->m_wallet_unlock_staking_only)
716             throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
717     }
718     auto keyid = GetKeyForDestination(spk_man, dest);
719     if (keyid.IsNull()) {
720         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
721     }
722     CKey vchSecret;
723     if (!spk_man.GetKey(keyid, vchSecret)) {
724         throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
725     }
726     return EncodeSecret(vchSecret);
727 }
728 
729 
dumpwallet(const JSONRPCRequest & request)730 UniValue dumpwallet(const JSONRPCRequest& request)
731 {
732     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
733     const CWallet* const pwallet = wallet.get();
734     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
735         return NullUniValue;
736     }
737 
738             RPCHelpMan{"dumpwallet",
739                 "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n",
740                 {
741                     {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to qtumd)"},
742                 },
743                 RPCResult{
744                     RPCResult::Type::OBJ, "", "",
745                     {
746                         {RPCResult::Type::STR, "filename", "The filename with full absolute path"},
747                     }
748                 },
749                 RPCExamples{
750                     HelpExampleCli("dumpwallet", "\"test\"")
751             + HelpExampleRpc("dumpwallet", "\"test\"")
752                 },
753             }.Check(request);
754 
755     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
756 
757     auto locked_chain = pwallet->chain().lock();
758     LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
759 
760     EnsureWalletIsUnlocked(pwallet);
761 
762     fs::path filepath = request.params[0].get_str();
763     filepath = fs::absolute(filepath);
764 
765     /* Prevent arbitrary files from being overwritten. There have been reports
766      * that users have overwritten wallet files this way:
767      * https://github.com/bitcoin/bitcoin/issues/9934
768      * It may also avoid other security issues.
769      */
770     if (fs::exists(filepath)) {
771         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");
772     }
773 
774     fsbridge::ofstream file;
775     file.open(filepath);
776     if (!file.is_open())
777         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
778 
779     std::map<CKeyID, int64_t> mapKeyBirth;
780     const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
781     pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
782 
783     std::set<CScriptID> scripts = spk_man.GetCScripts();
784 
785     // sort time/key pairs
786     std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
787     for (const auto& entry : mapKeyBirth) {
788         vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
789     }
790     mapKeyBirth.clear();
791     std::sort(vKeyBirth.begin(), vKeyBirth.end());
792 
793     // produce output
794     file << strprintf("# Wallet dump created by Qtum %s\n", CLIENT_BUILD);
795     file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
796     const Optional<int> tip_height = locked_chain->getHeight();
797     file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.get_value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)");
798     file << strprintf("#   mined on %s\n", tip_height ? FormatISO8601DateTime(locked_chain->getBlockTime(*tip_height)) : "(missing block time)");
799     file << "\n";
800 
801     // add the base58check encoded extended master if the wallet uses HD
802     CKeyID seed_id = spk_man.GetHDChain().seed_id;
803     if (!seed_id.IsNull())
804     {
805         CKey seed;
806         if (spk_man.GetKey(seed_id, seed)) {
807             CExtKey masterKey;
808             masterKey.SetSeed(seed.begin(), seed.size());
809 
810             file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
811         }
812     }
813     for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
814         const CKeyID &keyid = it->second;
815         std::string strTime = FormatISO8601DateTime(it->first);
816         std::string strAddr;
817         std::string strLabel;
818         CKey key;
819         if (spk_man.GetKey(keyid, key)) {
820             file << strprintf("%s %s ", EncodeSecret(key), strTime);
821             if (GetWalletAddressesForKey(&spk_man, pwallet, keyid, strAddr, strLabel)) {
822                file << strprintf("label=%s", strLabel);
823             } else if (keyid == seed_id) {
824                 file << "hdseed=1";
825             } else if (mapKeyPool.count(keyid)) {
826                 file << "reserve=1";
827             } else if (spk_man.mapKeyMetadata[keyid].hdKeypath == "s") {
828                 file << "inactivehdseed=1";
829             } else {
830                 file << "change=1";
831             }
832             file << strprintf(" # addr=%s%s\n", strAddr, (spk_man.mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(spk_man.mapKeyMetadata[keyid].key_origin.path) : ""));
833         }
834     }
835     file << "\n";
836     for (const CScriptID &scriptid : scripts) {
837         CScript script;
838         std::string create_time = "0";
839         std::string address = EncodeDestination(ScriptHash(scriptid));
840         // get birth times for scripts with metadata
841         auto it = spk_man.m_script_metadata.find(scriptid);
842         if (it != spk_man.m_script_metadata.end()) {
843             create_time = FormatISO8601DateTime(it->second.nCreateTime);
844         }
845         if(spk_man.GetCScript(scriptid, script)) {
846             file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time);
847             file << strprintf(" # addr=%s\n", address);
848         }
849     }
850     file << "\n";
851     file << "# End of dump\n";
852     file.close();
853 
854     UniValue reply(UniValue::VOBJ);
855     reply.pushKV("filename", filepath.string());
856 
857     return reply;
858 }
859 
860 struct ImportData
861 {
862     // Input data
863     std::unique_ptr<CScript> redeemscript; //!< Provided redeemScript; will be moved to `import_scripts` if relevant.
864     std::unique_ptr<CScript> witnessscript; //!< Provided witnessScript; will be moved to `import_scripts` if relevant.
865 
866     // Output data
867     std::set<CScript> import_scripts;
868     std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
869     std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
870 };
871 
872 enum class ScriptContext
873 {
874     TOP, //!< Top-level scriptPubKey
875     P2SH, //!< P2SH redeemScript
876     WITNESS_V0, //!< P2WSH witnessScript
877 };
878 
879 // 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.
880 // Returns an error string, or the empty string for success.
RecurseImportData(const CScript & script,ImportData & import_data,const ScriptContext script_ctx)881 static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx)
882 {
883     // Use Solver to obtain script type and parsed pubkeys or hashes:
884     std::vector<std::vector<unsigned char>> solverdata;
885     txnouttype script_type = Solver(script, solverdata);
886 
887     switch (script_type) {
888     case TX_PUBKEY: {
889         CPubKey pubkey(solverdata[0].begin(), solverdata[0].end());
890         import_data.used_keys.emplace(pubkey.GetID(), false);
891         return "";
892     }
893     case TX_PUBKEYHASH: {
894         CKeyID id = CKeyID(uint160(solverdata[0]));
895         import_data.used_keys[id] = true;
896         return "";
897     }
898     case TX_SCRIPTHASH: {
899         if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
900         if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
901         CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
902         CScriptID id = CScriptID(uint160(solverdata[0]));
903         auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
904         if (!subscript) return "missing redeemscript";
905         if (CScriptID(*subscript) != id) return "redeemScript does not match the scriptPubKey";
906         import_data.import_scripts.emplace(*subscript);
907         return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
908     }
909     case TX_MULTISIG: {
910         for (size_t i = 1; i + 1< solverdata.size(); ++i) {
911             CPubKey pubkey(solverdata[i].begin(), solverdata[i].end());
912             import_data.used_keys.emplace(pubkey.GetID(), false);
913         }
914         return "";
915     }
916     case TX_WITNESS_V0_SCRIPTHASH: {
917         if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
918         uint256 fullid(solverdata[0]);
919         CScriptID id;
920         CRIPEMD160().Write(fullid.begin(), fullid.size()).Finalize(id.begin());
921         auto subscript = std::move(import_data.witnessscript); // Remove redeemscript from import_data to check for superfluous script later.
922         if (!subscript) return "missing witnessscript";
923         if (CScriptID(*subscript) != id) return "witnessScript does not match the scriptPubKey or redeemScript";
924         if (script_ctx == ScriptContext::TOP) {
925             import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WSH requires the TOP script imported (see script/ismine.cpp)
926         }
927         import_data.import_scripts.emplace(*subscript);
928         return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
929     }
930     case TX_WITNESS_V0_KEYHASH: {
931         if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
932         CKeyID id = CKeyID(uint160(solverdata[0]));
933         import_data.used_keys[id] = true;
934         if (script_ctx == ScriptContext::TOP) {
935             import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WPKH requires the TOP script imported (see script/ismine.cpp)
936         }
937         return "";
938     }
939     case TX_NULL_DATA:
940         return "unspendable script";
941     case TX_NONSTANDARD:
942     case TX_WITNESS_UNKNOWN:
943     default:
944         return "unrecognized script";
945     }
946 }
947 
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)948 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)
949 {
950     UniValue warnings(UniValue::VARR);
951 
952     // First ensure scriptPubKey has either a script or JSON with "address" string
953     const UniValue& scriptPubKey = data["scriptPubKey"];
954     bool isScript = scriptPubKey.getType() == UniValue::VSTR;
955     if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
956         throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
957     }
958     const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
959 
960     // Optional fields.
961     const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
962     const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
963     const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
964     const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
965     const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
966     const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
967 
968     if (data.exists("range")) {
969         throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for a non-descriptor import");
970     }
971 
972     // Generate the script and destination for the scriptPubKey provided
973     CScript script;
974     if (!isScript) {
975         CTxDestination dest = DecodeDestination(output);
976         if (!IsValidDestination(dest)) {
977             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
978         }
979         script = GetScriptForDestination(dest);
980     } else {
981         if (!IsHex(output)) {
982             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
983         }
984         std::vector<unsigned char> vData(ParseHex(output));
985         script = CScript(vData.begin(), vData.end());
986         CTxDestination dest;
987         if (!ExtractDestination(script, dest) && !internal) {
988             throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
989         }
990     }
991     script_pub_keys.emplace(script);
992 
993     // Parse all arguments
994     if (strRedeemScript.size()) {
995         if (!IsHex(strRedeemScript)) {
996             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
997         }
998         auto parsed_redeemscript = ParseHex(strRedeemScript);
999         import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
1000     }
1001     if (witness_script_hex.size()) {
1002         if (!IsHex(witness_script_hex)) {
1003             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
1004         }
1005         auto parsed_witnessscript = ParseHex(witness_script_hex);
1006         import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
1007     }
1008     for (size_t i = 0; i < pubKeys.size(); ++i) {
1009         const auto& str = pubKeys[i].get_str();
1010         if (!IsHex(str)) {
1011             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
1012         }
1013         auto parsed_pubkey = ParseHex(str);
1014         CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
1015         if (!pubkey.IsFullyValid()) {
1016             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
1017         }
1018         pubkey_map.emplace(pubkey.GetID(), pubkey);
1019         ordered_pubkeys.push_back(pubkey.GetID());
1020     }
1021     for (size_t i = 0; i < keys.size(); ++i) {
1022         const auto& str = keys[i].get_str();
1023         CKey key = DecodeSecret(str);
1024         if (!key.IsValid()) {
1025             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
1026         }
1027         CPubKey pubkey = key.GetPubKey();
1028         CKeyID id = pubkey.GetID();
1029         if (pubkey_map.count(id)) {
1030             pubkey_map.erase(id);
1031         }
1032         privkey_map.emplace(id, key);
1033     }
1034 
1035 
1036     // Verify and process input data
1037     have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
1038     if (have_solving_data) {
1039         // Match up data in import_data with the scriptPubKey in script.
1040         auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
1041 
1042         // Verify whether the watchonly option corresponds to the availability of private keys.
1043         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; });
1044         if (!watchOnly && !spendable) {
1045             warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1046         }
1047         if (watchOnly && spendable) {
1048             warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1049         }
1050 
1051         // Check that all required keys for solvability are provided.
1052         if (error.empty()) {
1053             for (const auto& require_key : import_data.used_keys) {
1054                 if (!require_key.second) continue; // Not a required key
1055                 if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
1056                     error = "some required keys are missing";
1057                 }
1058             }
1059         }
1060 
1061         if (!error.empty()) {
1062             warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
1063             import_data = ImportData();
1064             pubkey_map.clear();
1065             privkey_map.clear();
1066             have_solving_data = false;
1067         } else {
1068             // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
1069             if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
1070             if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
1071             for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
1072                 auto oldit = it++;
1073                 if (import_data.used_keys.count(oldit->first) == 0) {
1074                     warnings.push_back("Ignoring irrelevant private key.");
1075                     privkey_map.erase(oldit);
1076                 }
1077             }
1078             for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
1079                 auto oldit = it++;
1080                 auto key_data_it = import_data.used_keys.find(oldit->first);
1081                 if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
1082                     warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
1083                     pubkey_map.erase(oldit);
1084                 }
1085             }
1086         }
1087     }
1088 
1089     return warnings;
1090 }
1091 
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)1092 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)
1093 {
1094     UniValue warnings(UniValue::VARR);
1095 
1096     const std::string& descriptor = data["desc"].get_str();
1097     FlatSigningProvider keys;
1098     std::string error;
1099     auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
1100     if (!parsed_desc) {
1101         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
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 * const pwallet,const UniValue & data,const int64_t timestamp)1178 static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->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 && !pwallet->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 (pwallet->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 (pwallet->IsMine(script) & ISMINE_SPENDABLE) {
1222                 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script.begin(), script.end()) + "\")");
1223             }
1224         }
1225 
1226         // All good, time to import
1227         pwallet->MarkDirty();
1228         if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) {
1229             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
1230         }
1231         if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
1232             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
1233         }
1234         if (!pwallet->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 (!pwallet->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(const JSONRPCRequest & mainRequest)1268 UniValue importmulti(const JSONRPCRequest& mainRequest)
1269 {
1270     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
1271     CWallet* const pwallet = wallet.get();
1272     if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
1273         return NullUniValue;
1274     }
1275 
1276             RPCHelpMan{"importmulti",
1277                 "\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"
1278                 "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"
1279                 "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"
1280             "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
1281             "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
1282             "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
1283                 {
1284                     {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1285                         {
1286                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
1287                                 {
1288                                     {"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
1289                                     {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
1290                                         /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
1291                                     },
1292                                     {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n"
1293         "                                                              or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1294         "                                                              key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1295         "                                                              \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1296         "                                                              0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1297         "                                                              creation time of all keys being imported by the importmulti call will be scanned.",
1298                                         /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
1299                                     },
1300                                     {"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
1301                                     {"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
1302                                     {"pubkeys", RPCArg::Type::ARR, /* default */ "empty array", "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).",
1303                                         {
1304                                             {"pubKey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
1305                                         }
1306                                     },
1307                                     {"keys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
1308                                         {
1309                                             {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
1310                                         }
1311                                     },
1312                                     {"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"},
1313                                     {"internal", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
1314                                     {"watchonly", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be considered watchonly."},
1315                                     {"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
1316                                     {"keypool", RPCArg::Type::BOOL, /* 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"},
1317                                 },
1318                             },
1319                         },
1320                         "\"requests\""},
1321                     {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
1322                         {
1323                             {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Stating if should rescan the blockchain after all imports"},
1324                         },
1325                         "\"options\""},
1326                 },
1327                 RPCResult{
1328                     RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
1329                     {
1330                         {RPCResult::Type::OBJ, "", "",
1331                         {
1332                             {RPCResult::Type::BOOL, "success", ""},
1333                             {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
1334                             {
1335                                 {RPCResult::Type::STR, "", ""},
1336                             }},
1337                             {RPCResult::Type::OBJ, "error", /* optional */ true, "",
1338                             {
1339                                 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1340                             }},
1341                         }},
1342                     }
1343                 },
1344                 RPCExamples{
1345                     HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1346                                           "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1347                     HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
1348                 },
1349             }.Check(mainRequest);
1350 
1351 
1352     RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1353 
1354     EnsureLegacyScriptPubKeyMan(*wallet, true);
1355 
1356     const UniValue& requests = mainRequest.params[0];
1357 
1358     //Default options
1359     bool fRescan = true;
1360 
1361     if (!mainRequest.params[1].isNull()) {
1362         const UniValue& options = mainRequest.params[1];
1363 
1364         if (options.exists("rescan")) {
1365             fRescan = options["rescan"].get_bool();
1366         }
1367     }
1368 
1369     WalletRescanReserver reserver(pwallet);
1370     if (fRescan && !reserver.reserve()) {
1371         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1372     }
1373 
1374     int64_t now = 0;
1375     bool fRunScan = false;
1376     int64_t nLowestTimestamp = 0;
1377     UniValue response(UniValue::VARR);
1378     {
1379         auto locked_chain = pwallet->chain().lock();
1380         LOCK(pwallet->cs_wallet);
1381         EnsureWalletIsUnlocked(pwallet);
1382 
1383         // Verify all timestamps are present before importing any keys.
1384         const Optional<int> tip_height = locked_chain->getHeight();
1385         now = tip_height ? locked_chain->getBlockMedianTimePast(*tip_height) : 0;
1386         for (const UniValue& data : requests.getValues()) {
1387             GetImportTimestamp(data, now);
1388         }
1389 
1390         const int64_t minimumTimestamp = 1;
1391 
1392         if (fRescan && tip_height) {
1393             nLowestTimestamp = locked_chain->getBlockTime(*tip_height);
1394         } else {
1395             fRescan = false;
1396         }
1397 
1398         for (const UniValue& data : requests.getValues()) {
1399             const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
1400             const UniValue result = ProcessImport(pwallet, data, timestamp);
1401             response.push_back(result);
1402 
1403             if (!fRescan) {
1404                 continue;
1405             }
1406 
1407             // If at least one request was successful then allow rescan.
1408             if (result["success"].get_bool()) {
1409                 fRunScan = true;
1410             }
1411 
1412             // Get the lowest timestamp.
1413             if (timestamp < nLowestTimestamp) {
1414                 nLowestTimestamp = timestamp;
1415             }
1416         }
1417     }
1418     if (fRescan && fRunScan && requests.size()) {
1419         int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
1420         {
1421             auto locked_chain = pwallet->chain().lock();
1422             LOCK(pwallet->cs_wallet);
1423             pwallet->ReacceptWalletTransactions();
1424         }
1425 
1426         if (pwallet->IsAbortingRescan()) {
1427             throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
1428         }
1429         if (scannedTime > nLowestTimestamp) {
1430             std::vector<UniValue> results = response.getValues();
1431             response.clear();
1432             response.setArray();
1433             size_t i = 0;
1434             for (const UniValue& request : requests.getValues()) {
1435                 // If key creation date is within the successfully scanned
1436                 // range, or if the import result already has an error set, let
1437                 // the result stand unmodified. Otherwise replace the result
1438                 // with an error message.
1439                 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1440                     response.push_back(results.at(i));
1441                 } else {
1442                     UniValue result = UniValue(UniValue::VOBJ);
1443                     result.pushKV("success", UniValue(false));
1444                     result.pushKV(
1445                         "error",
1446                         JSONRPCError(
1447                             RPC_MISC_ERROR,
1448                             strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1449                                       "block from time %d, which is after or within %d seconds of key creation, and "
1450                                       "could contain transactions pertaining to the key. As a result, transactions "
1451                                       "and coins using this key may not appear in the wallet. This error could be "
1452                                       "caused by pruning or data corruption (see qtumd log for details) and could "
1453                                       "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1454                                       "and -rescan options).",
1455                                 GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1456                     response.push_back(std::move(result));
1457                 }
1458                 ++i;
1459             }
1460         }
1461     }
1462 
1463     return response;
1464 }
1465