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