Merge #11107: Fix races in AppInitMain and others with lock and atomic bools
[bitcoinplatinum.git] / src / wallet / rpcdump.cpp
blob1123fd6dbb18084e11b490738fa1829076566257
1 // Copyright (c) 2009-2016 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.
5 #include "base58.h"
6 #include "chain.h"
7 #include "rpc/safemode.h"
8 #include "rpc/server.h"
9 #include "init.h"
10 #include "validation.h"
11 #include "script/script.h"
12 #include "script/standard.h"
13 #include "sync.h"
14 #include "util.h"
15 #include "utiltime.h"
16 #include "wallet.h"
17 #include "merkleblock.h"
18 #include "core_io.h"
20 #include "rpcwallet.h"
22 #include <fstream>
23 #include <stdint.h>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/date_time/posix_time/posix_time.hpp>
28 #include <univalue.h>
31 std::string static EncodeDumpTime(int64_t nTime) {
32 return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
35 int64_t static DecodeDumpTime(const std::string &str) {
36 static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
37 static const std::locale loc(std::locale::classic(),
38 new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
39 std::istringstream iss(str);
40 iss.imbue(loc);
41 boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
42 iss >> ptime;
43 if (ptime.is_not_a_date_time())
44 return 0;
45 return (ptime - epoch).total_seconds();
48 std::string static EncodeDumpString(const std::string &str) {
49 std::stringstream ret;
50 for (unsigned char c : str) {
51 if (c <= 32 || c >= 128 || c == '%') {
52 ret << '%' << HexStr(&c, &c + 1);
53 } else {
54 ret << c;
57 return ret.str();
60 std::string DecodeDumpString(const std::string &str) {
61 std::stringstream ret;
62 for (unsigned int pos = 0; pos < str.length(); pos++) {
63 unsigned char c = str[pos];
64 if (c == '%' && pos+2 < str.length()) {
65 c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
66 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
67 pos += 2;
69 ret << c;
71 return ret.str();
74 UniValue importprivkey(const JSONRPCRequest& request)
76 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
77 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
78 return NullUniValue;
81 if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
82 throw std::runtime_error(
83 "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n"
84 "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
85 "\nArguments:\n"
86 "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
87 "2. \"label\" (string, optional, default=\"\") An optional label\n"
88 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
89 "\nNote: This call can take minutes to complete if rescan is true.\n"
90 "\nExamples:\n"
91 "\nDump a private key\n"
92 + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
93 "\nImport the private key with rescan\n"
94 + HelpExampleCli("importprivkey", "\"mykey\"") +
95 "\nImport using a label and without rescan\n"
96 + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
97 "\nImport using default blank label and without rescan\n"
98 + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
99 "\nAs a JSON-RPC call\n"
100 + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
104 LOCK2(cs_main, pwallet->cs_wallet);
106 EnsureWalletIsUnlocked(pwallet);
108 std::string strSecret = request.params[0].get_str();
109 std::string strLabel = "";
110 if (!request.params[1].isNull())
111 strLabel = request.params[1].get_str();
113 // Whether to perform rescan after import
114 bool fRescan = true;
115 if (!request.params[2].isNull())
116 fRescan = request.params[2].get_bool();
118 if (fRescan && fPruneMode)
119 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
121 CBitcoinSecret vchSecret;
122 bool fGood = vchSecret.SetString(strSecret);
124 if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
126 CKey key = vchSecret.GetKey();
127 if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
129 CPubKey pubkey = key.GetPubKey();
130 assert(key.VerifyPubKey(pubkey));
131 CKeyID vchAddress = pubkey.GetID();
133 pwallet->MarkDirty();
134 pwallet->SetAddressBook(vchAddress, strLabel, "receive");
136 // Don't throw error in case a key is already there
137 if (pwallet->HaveKey(vchAddress)) {
138 return NullUniValue;
141 pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
143 if (!pwallet->AddKeyPubKey(key, pubkey)) {
144 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
147 // whenever a key is imported, we need to scan the whole chain
148 pwallet->UpdateTimeFirstKey(1);
150 if (fRescan) {
151 pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
155 return NullUniValue;
158 UniValue abortrescan(const JSONRPCRequest& request)
160 CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
161 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
162 return NullUniValue;
165 if (request.fHelp || request.params.size() > 0)
166 throw std::runtime_error(
167 "abortrescan\n"
168 "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
169 "\nExamples:\n"
170 "\nImport a private key\n"
171 + HelpExampleCli("importprivkey", "\"mykey\"") +
172 "\nAbort the running wallet rescan\n"
173 + HelpExampleCli("abortrescan", "") +
174 "\nAs a JSON-RPC call\n"
175 + HelpExampleRpc("abortrescan", "")
178 ObserveSafeMode();
179 if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
180 pwallet->AbortRescan();
181 return true;
184 void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
185 void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
187 if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
188 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
191 pwallet->MarkDirty();
193 if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script, 0 /* nCreateTime */)) {
194 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
197 if (isRedeemScript) {
198 if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
199 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
201 ImportAddress(pwallet, CScriptID(script), strLabel);
202 } else {
203 CTxDestination destination;
204 if (ExtractDestination(script, destination)) {
205 pwallet->SetAddressBook(destination, strLabel, "receive");
210 void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel)
212 CScript script = GetScriptForDestination(dest);
213 ImportScript(pwallet, script, strLabel, false);
214 // add to address book or update label
215 if (IsValidDestination(dest))
216 pwallet->SetAddressBook(dest, strLabel, "receive");
219 UniValue importaddress(const JSONRPCRequest& request)
221 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
222 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
223 return NullUniValue;
226 if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
227 throw std::runtime_error(
228 "importaddress \"address\" ( \"label\" rescan p2sh )\n"
229 "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n"
230 "\nArguments:\n"
231 "1. \"script\" (string, required) The hex-encoded script (or address)\n"
232 "2. \"label\" (string, optional, default=\"\") An optional label\n"
233 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
234 "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
235 "\nNote: This call can take minutes to complete if rescan is true.\n"
236 "If you have the full public key, you should call importpubkey instead of this.\n"
237 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
238 "as change, and not show up in many RPCs.\n"
239 "\nExamples:\n"
240 "\nImport a script with rescan\n"
241 + HelpExampleCli("importaddress", "\"myscript\"") +
242 "\nImport using a label without rescan\n"
243 + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
244 "\nAs a JSON-RPC call\n"
245 + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")
249 std::string strLabel = "";
250 if (!request.params[1].isNull())
251 strLabel = request.params[1].get_str();
253 // Whether to perform rescan after import
254 bool fRescan = true;
255 if (!request.params[2].isNull())
256 fRescan = request.params[2].get_bool();
258 if (fRescan && fPruneMode)
259 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
261 // Whether to import a p2sh version, too
262 bool fP2SH = false;
263 if (!request.params[3].isNull())
264 fP2SH = request.params[3].get_bool();
266 LOCK2(cs_main, pwallet->cs_wallet);
268 CTxDestination dest = DecodeDestination(request.params[0].get_str());
269 if (IsValidDestination(dest)) {
270 if (fP2SH) {
271 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
273 ImportAddress(pwallet, dest, strLabel);
274 } else if (IsHex(request.params[0].get_str())) {
275 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
276 ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
277 } else {
278 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
281 if (fRescan)
283 pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
284 pwallet->ReacceptWalletTransactions();
287 return NullUniValue;
290 UniValue importprunedfunds(const JSONRPCRequest& request)
292 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
293 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
294 return NullUniValue;
297 if (request.fHelp || request.params.size() != 2)
298 throw std::runtime_error(
299 "importprunedfunds\n"
300 "\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"
301 "\nArguments:\n"
302 "1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
303 "2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
306 CMutableTransaction tx;
307 if (!DecodeHexTx(tx, request.params[0].get_str()))
308 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
309 uint256 hashTx = tx.GetHash();
310 CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx)));
312 CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
313 CMerkleBlock merkleBlock;
314 ssMB >> merkleBlock;
316 //Search partial merkle tree in proof for our transaction and index in valid block
317 std::vector<uint256> vMatch;
318 std::vector<unsigned int> vIndex;
319 unsigned int txnIndex = 0;
320 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
322 LOCK(cs_main);
324 if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()]))
325 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
327 std::vector<uint256>::const_iterator it;
328 if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx))==vMatch.end()) {
329 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
332 txnIndex = vIndex[it - vMatch.begin()];
334 else {
335 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
338 wtx.nIndex = txnIndex;
339 wtx.hashBlock = merkleBlock.header.GetHash();
341 LOCK2(cs_main, pwallet->cs_wallet);
343 if (pwallet->IsMine(wtx)) {
344 pwallet->AddToWallet(wtx, false);
345 return NullUniValue;
348 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
351 UniValue removeprunedfunds(const JSONRPCRequest& request)
353 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
354 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
355 return NullUniValue;
358 if (request.fHelp || request.params.size() != 1)
359 throw std::runtime_error(
360 "removeprunedfunds \"txid\"\n"
361 "\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"
362 "\nArguments:\n"
363 "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
364 "\nExamples:\n"
365 + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
366 "\nAs a JSON-RPC call\n"
367 + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
370 LOCK2(cs_main, pwallet->cs_wallet);
372 uint256 hash;
373 hash.SetHex(request.params[0].get_str());
374 std::vector<uint256> vHash;
375 vHash.push_back(hash);
376 std::vector<uint256> vHashOut;
378 if (pwallet->ZapSelectTx(vHash, vHashOut) != DB_LOAD_OK) {
379 throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
382 if(vHashOut.empty()) {
383 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
386 return NullUniValue;
389 UniValue importpubkey(const JSONRPCRequest& request)
391 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
392 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
393 return NullUniValue;
396 if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
397 throw std::runtime_error(
398 "importpubkey \"pubkey\" ( \"label\" rescan )\n"
399 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
400 "\nArguments:\n"
401 "1. \"pubkey\" (string, required) The hex-encoded public key\n"
402 "2. \"label\" (string, optional, default=\"\") An optional label\n"
403 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
404 "\nNote: This call can take minutes to complete if rescan is true.\n"
405 "\nExamples:\n"
406 "\nImport a public key with rescan\n"
407 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
408 "\nImport using a label without rescan\n"
409 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
410 "\nAs a JSON-RPC call\n"
411 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
415 std::string strLabel = "";
416 if (!request.params[1].isNull())
417 strLabel = request.params[1].get_str();
419 // Whether to perform rescan after import
420 bool fRescan = true;
421 if (!request.params[2].isNull())
422 fRescan = request.params[2].get_bool();
424 if (fRescan && fPruneMode)
425 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
427 if (!IsHex(request.params[0].get_str()))
428 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
429 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
430 CPubKey pubKey(data.begin(), data.end());
431 if (!pubKey.IsFullyValid())
432 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
434 LOCK2(cs_main, pwallet->cs_wallet);
436 ImportAddress(pwallet, pubKey.GetID(), strLabel);
437 ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
439 if (fRescan)
441 pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
442 pwallet->ReacceptWalletTransactions();
445 return NullUniValue;
449 UniValue importwallet(const JSONRPCRequest& request)
451 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
452 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
453 return NullUniValue;
456 if (request.fHelp || request.params.size() != 1)
457 throw std::runtime_error(
458 "importwallet \"filename\"\n"
459 "\nImports keys from a wallet dump file (see dumpwallet).\n"
460 "\nArguments:\n"
461 "1. \"filename\" (string, required) The wallet file\n"
462 "\nExamples:\n"
463 "\nDump the wallet\n"
464 + HelpExampleCli("dumpwallet", "\"test\"") +
465 "\nImport the wallet\n"
466 + HelpExampleCli("importwallet", "\"test\"") +
467 "\nImport using the json rpc call\n"
468 + HelpExampleRpc("importwallet", "\"test\"")
471 if (fPruneMode)
472 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
474 LOCK2(cs_main, pwallet->cs_wallet);
476 EnsureWalletIsUnlocked(pwallet);
478 std::ifstream file;
479 file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate);
480 if (!file.is_open())
481 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
483 int64_t nTimeBegin = chainActive.Tip()->GetBlockTime();
485 bool fGood = true;
487 int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
488 file.seekg(0, file.beg);
490 pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
491 while (file.good()) {
492 pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
493 std::string line;
494 std::getline(file, line);
495 if (line.empty() || line[0] == '#')
496 continue;
498 std::vector<std::string> vstr;
499 boost::split(vstr, line, boost::is_any_of(" "));
500 if (vstr.size() < 2)
501 continue;
502 CBitcoinSecret vchSecret;
503 if (!vchSecret.SetString(vstr[0]))
504 continue;
505 CKey key = vchSecret.GetKey();
506 CPubKey pubkey = key.GetPubKey();
507 assert(key.VerifyPubKey(pubkey));
508 CKeyID keyid = pubkey.GetID();
509 if (pwallet->HaveKey(keyid)) {
510 LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
511 continue;
513 int64_t nTime = DecodeDumpTime(vstr[1]);
514 std::string strLabel;
515 bool fLabel = true;
516 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
517 if (boost::algorithm::starts_with(vstr[nStr], "#"))
518 break;
519 if (vstr[nStr] == "change=1")
520 fLabel = false;
521 if (vstr[nStr] == "reserve=1")
522 fLabel = false;
523 if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
524 strLabel = DecodeDumpString(vstr[nStr].substr(6));
525 fLabel = true;
528 LogPrintf("Importing %s...\n", EncodeDestination(keyid));
529 if (!pwallet->AddKeyPubKey(key, pubkey)) {
530 fGood = false;
531 continue;
533 pwallet->mapKeyMetadata[keyid].nCreateTime = nTime;
534 if (fLabel)
535 pwallet->SetAddressBook(keyid, strLabel, "receive");
536 nTimeBegin = std::min(nTimeBegin, nTime);
538 file.close();
539 pwallet->ShowProgress("", 100); // hide progress dialog in GUI
540 pwallet->UpdateTimeFirstKey(nTimeBegin);
541 pwallet->RescanFromTime(nTimeBegin, false /* update */);
542 pwallet->MarkDirty();
544 if (!fGood)
545 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
547 return NullUniValue;
550 UniValue dumpprivkey(const JSONRPCRequest& request)
552 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
553 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
554 return NullUniValue;
557 if (request.fHelp || request.params.size() != 1)
558 throw std::runtime_error(
559 "dumpprivkey \"address\"\n"
560 "\nReveals the private key corresponding to 'address'.\n"
561 "Then the importprivkey can be used with this output\n"
562 "\nArguments:\n"
563 "1. \"address\" (string, required) The bitcoin address for the private key\n"
564 "\nResult:\n"
565 "\"key\" (string) The private key\n"
566 "\nExamples:\n"
567 + HelpExampleCli("dumpprivkey", "\"myaddress\"")
568 + HelpExampleCli("importprivkey", "\"mykey\"")
569 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
572 LOCK2(cs_main, pwallet->cs_wallet);
574 EnsureWalletIsUnlocked(pwallet);
576 std::string strAddress = request.params[0].get_str();
577 CTxDestination dest = DecodeDestination(strAddress);
578 if (!IsValidDestination(dest)) {
579 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
581 const CKeyID *keyID = boost::get<CKeyID>(&dest);
582 if (!keyID) {
583 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
585 CKey vchSecret;
586 if (!pwallet->GetKey(*keyID, vchSecret)) {
587 throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
589 return CBitcoinSecret(vchSecret).ToString();
593 UniValue dumpwallet(const JSONRPCRequest& request)
595 CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
596 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
597 return NullUniValue;
600 if (request.fHelp || request.params.size() != 1)
601 throw std::runtime_error(
602 "dumpwallet \"filename\"\n"
603 "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
604 "\nArguments:\n"
605 "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
606 "\nResult:\n"
607 "{ (json object)\n"
608 " \"filename\" : { (string) The filename with full absolute path\n"
609 "}\n"
610 "\nExamples:\n"
611 + HelpExampleCli("dumpwallet", "\"test\"")
612 + HelpExampleRpc("dumpwallet", "\"test\"")
615 LOCK2(cs_main, pwallet->cs_wallet);
617 EnsureWalletIsUnlocked(pwallet);
619 boost::filesystem::path filepath = request.params[0].get_str();
620 filepath = boost::filesystem::absolute(filepath);
622 /* Prevent arbitrary files from being overwritten. There have been reports
623 * that users have overwritten wallet files this way:
624 * https://github.com/bitcoin/bitcoin/issues/9934
625 * It may also avoid other security issues.
627 if (boost::filesystem::exists(filepath)) {
628 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");
631 std::ofstream file;
632 file.open(filepath.string().c_str());
633 if (!file.is_open())
634 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
636 std::map<CTxDestination, int64_t> mapKeyBirth;
637 const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
638 pwallet->GetKeyBirthTimes(mapKeyBirth);
640 // sort time/key pairs
641 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
642 for (const auto& entry : mapKeyBirth) {
643 if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test
644 vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
647 mapKeyBirth.clear();
648 std::sort(vKeyBirth.begin(), vKeyBirth.end());
650 // produce output
651 file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
652 file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
653 file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
654 file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
655 file << "\n";
657 // add the base58check encoded extended master if the wallet uses HD
658 CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
659 if (!masterKeyID.IsNull())
661 CKey key;
662 if (pwallet->GetKey(masterKeyID, key)) {
663 CExtKey masterKey;
664 masterKey.SetMaster(key.begin(), key.size());
666 CBitcoinExtKey b58extkey;
667 b58extkey.SetKey(masterKey);
669 file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n";
672 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
673 const CKeyID &keyid = it->second;
674 std::string strTime = EncodeDumpTime(it->first);
675 std::string strAddr = EncodeDestination(keyid);
676 CKey key;
677 if (pwallet->GetKey(keyid, key)) {
678 file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
679 if (pwallet->mapAddressBook.count(keyid)) {
680 file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name));
681 } else if (keyid == masterKeyID) {
682 file << "hdmaster=1";
683 } else if (mapKeyPool.count(keyid)) {
684 file << "reserve=1";
685 } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
686 file << "inactivehdmaster=1";
687 } else {
688 file << "change=1";
690 file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwallet->mapKeyMetadata[keyid].hdKeypath : ""));
693 file << "\n";
694 file << "# End of dump\n";
695 file.close();
697 UniValue reply(UniValue::VOBJ);
698 reply.push_back(Pair("filename", filepath.string()));
700 return reply;
704 UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp)
706 try {
707 bool success = false;
709 // Required fields.
710 const UniValue& scriptPubKey = data["scriptPubKey"];
712 // Should have script or JSON with "address".
713 if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
714 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
717 // Optional fields.
718 const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
719 const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
720 const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
721 const bool& internal = data.exists("internal") ? data["internal"].get_bool() : false;
722 const bool& watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
723 const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
725 bool isScript = scriptPubKey.getType() == UniValue::VSTR;
726 bool isP2SH = strRedeemScript.length() > 0;
727 const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
729 // Parse the output.
730 CScript script;
731 CTxDestination dest;
733 if (!isScript) {
734 dest = DecodeDestination(output);
735 if (!IsValidDestination(dest)) {
736 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
738 script = GetScriptForDestination(dest);
739 } else {
740 if (!IsHex(output)) {
741 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
744 std::vector<unsigned char> vData(ParseHex(output));
745 script = CScript(vData.begin(), vData.end());
748 // Watchonly and private keys
749 if (watchOnly && keys.size()) {
750 throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
753 // Internal + Label
754 if (internal && data.exists("label")) {
755 throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
758 // Not having Internal + Script
759 if (!internal && isScript) {
760 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
763 // Keys / PubKeys size check.
764 if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
765 throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
768 // Invalid P2SH redeemScript
769 if (isP2SH && !IsHex(strRedeemScript)) {
770 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
773 // Process. //
775 // P2SH
776 if (isP2SH) {
777 // Import redeem script.
778 std::vector<unsigned char> vData(ParseHex(strRedeemScript));
779 CScript redeemScript = CScript(vData.begin(), vData.end());
781 // Invalid P2SH address
782 if (!script.IsPayToScriptHash()) {
783 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
786 pwallet->MarkDirty();
788 if (!pwallet->AddWatchOnly(redeemScript, timestamp)) {
789 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
792 if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) {
793 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
796 CTxDestination redeem_dest = CScriptID(redeemScript);
797 CScript redeemDestination = GetScriptForDestination(redeem_dest);
799 if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
800 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
803 pwallet->MarkDirty();
805 if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) {
806 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
809 // add to address book or update label
810 if (IsValidDestination(dest)) {
811 pwallet->SetAddressBook(dest, label, "receive");
814 // Import private keys.
815 if (keys.size()) {
816 for (size_t i = 0; i < keys.size(); i++) {
817 const std::string& privkey = keys[i].get_str();
819 CBitcoinSecret vchSecret;
820 bool fGood = vchSecret.SetString(privkey);
822 if (!fGood) {
823 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
826 CKey key = vchSecret.GetKey();
828 if (!key.IsValid()) {
829 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
832 CPubKey pubkey = key.GetPubKey();
833 assert(key.VerifyPubKey(pubkey));
835 CKeyID vchAddress = pubkey.GetID();
836 pwallet->MarkDirty();
837 pwallet->SetAddressBook(vchAddress, label, "receive");
839 if (pwallet->HaveKey(vchAddress)) {
840 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
843 pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
845 if (!pwallet->AddKeyPubKey(key, pubkey)) {
846 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
849 pwallet->UpdateTimeFirstKey(timestamp);
853 success = true;
854 } else {
855 // Import public keys.
856 if (pubKeys.size() && keys.size() == 0) {
857 const std::string& strPubKey = pubKeys[0].get_str();
859 if (!IsHex(strPubKey)) {
860 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
863 std::vector<unsigned char> vData(ParseHex(strPubKey));
864 CPubKey pubKey(vData.begin(), vData.end());
866 if (!pubKey.IsFullyValid()) {
867 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
870 CTxDestination pubkey_dest = pubKey.GetID();
872 // Consistency check.
873 if (!isScript && !(pubkey_dest == dest)) {
874 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
877 // Consistency check.
878 if (isScript) {
879 CTxDestination destination;
881 if (ExtractDestination(script, destination)) {
882 if (!(destination == pubkey_dest)) {
883 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
888 CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
890 if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
891 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
894 pwallet->MarkDirty();
896 if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) {
897 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
900 // add to address book or update label
901 if (IsValidDestination(pubkey_dest)) {
902 pwallet->SetAddressBook(pubkey_dest, label, "receive");
905 // TODO Is this necessary?
906 CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
908 if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
909 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
912 pwallet->MarkDirty();
914 if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
915 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
918 success = true;
921 // Import private keys.
922 if (keys.size()) {
923 const std::string& strPrivkey = keys[0].get_str();
925 // Checks.
926 CBitcoinSecret vchSecret;
927 bool fGood = vchSecret.SetString(strPrivkey);
929 if (!fGood) {
930 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
933 CKey key = vchSecret.GetKey();
934 if (!key.IsValid()) {
935 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
938 CPubKey pubKey = key.GetPubKey();
939 assert(key.VerifyPubKey(pubKey));
941 CTxDestination pubkey_dest = pubKey.GetID();
943 // Consistency check.
944 if (!isScript && !(pubkey_dest == dest)) {
945 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
948 // Consistency check.
949 if (isScript) {
950 CTxDestination destination;
952 if (ExtractDestination(script, destination)) {
953 if (!(destination == pubkey_dest)) {
954 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
959 CKeyID vchAddress = pubKey.GetID();
960 pwallet->MarkDirty();
961 pwallet->SetAddressBook(vchAddress, label, "receive");
963 if (pwallet->HaveKey(vchAddress)) {
964 return false;
967 pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
969 if (!pwallet->AddKeyPubKey(key, pubKey)) {
970 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
973 pwallet->UpdateTimeFirstKey(timestamp);
975 success = true;
978 // Import scriptPubKey only.
979 if (pubKeys.size() == 0 && keys.size() == 0) {
980 if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
981 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
984 pwallet->MarkDirty();
986 if (!pwallet->AddWatchOnly(script, timestamp)) {
987 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
990 if (scriptPubKey.getType() == UniValue::VOBJ) {
991 // add to address book or update label
992 if (IsValidDestination(dest)) {
993 pwallet->SetAddressBook(dest, label, "receive");
997 success = true;
1001 UniValue result = UniValue(UniValue::VOBJ);
1002 result.pushKV("success", UniValue(success));
1003 return result;
1004 } catch (const UniValue& e) {
1005 UniValue result = UniValue(UniValue::VOBJ);
1006 result.pushKV("success", UniValue(false));
1007 result.pushKV("error", e);
1008 return result;
1009 } catch (...) {
1010 UniValue result = UniValue(UniValue::VOBJ);
1011 result.pushKV("success", UniValue(false));
1012 result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
1013 return result;
1017 int64_t GetImportTimestamp(const UniValue& data, int64_t now)
1019 if (data.exists("timestamp")) {
1020 const UniValue& timestamp = data["timestamp"];
1021 if (timestamp.isNum()) {
1022 return timestamp.get_int64();
1023 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
1024 return now;
1026 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
1028 throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
1031 UniValue importmulti(const JSONRPCRequest& mainRequest)
1033 CWallet * const pwallet = GetWalletForJSONRPCRequest(mainRequest);
1034 if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
1035 return NullUniValue;
1038 // clang-format off
1039 if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
1040 throw std::runtime_error(
1041 "importmulti \"requests\" ( \"options\" )\n\n"
1042 "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n"
1043 "Arguments:\n"
1044 "1. requests (array, required) Data to be imported\n"
1045 " [ (array of json objects)\n"
1046 " {\n"
1047 " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
1048 " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
1049 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1050 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1051 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1052 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1053 " creation time of all keys being imported by the importmulti call will be scanned.\n"
1054 " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
1055 " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
1056 " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
1057 " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
1058 " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
1059 " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
1060 " }\n"
1061 " ,...\n"
1062 " ]\n"
1063 "2. options (json, optional)\n"
1064 " {\n"
1065 " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
1066 " }\n"
1067 "\nExamples:\n" +
1068 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1069 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1070 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
1072 "\nResponse is an array with the same size as the input that has the execution result :\n"
1073 " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
1075 // clang-format on
1077 RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1079 const UniValue& requests = mainRequest.params[0];
1081 //Default options
1082 bool fRescan = true;
1084 if (!mainRequest.params[1].isNull()) {
1085 const UniValue& options = mainRequest.params[1];
1087 if (options.exists("rescan")) {
1088 fRescan = options["rescan"].get_bool();
1092 LOCK2(cs_main, pwallet->cs_wallet);
1093 EnsureWalletIsUnlocked(pwallet);
1095 // Verify all timestamps are present before importing any keys.
1096 const int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0;
1097 for (const UniValue& data : requests.getValues()) {
1098 GetImportTimestamp(data, now);
1101 bool fRunScan = false;
1102 const int64_t minimumTimestamp = 1;
1103 int64_t nLowestTimestamp = 0;
1105 if (fRescan && chainActive.Tip()) {
1106 nLowestTimestamp = chainActive.Tip()->GetBlockTime();
1107 } else {
1108 fRescan = false;
1111 UniValue response(UniValue::VARR);
1113 for (const UniValue& data : requests.getValues()) {
1114 const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
1115 const UniValue result = ProcessImport(pwallet, data, timestamp);
1116 response.push_back(result);
1118 if (!fRescan) {
1119 continue;
1122 // If at least one request was successful then allow rescan.
1123 if (result["success"].get_bool()) {
1124 fRunScan = true;
1127 // Get the lowest timestamp.
1128 if (timestamp < nLowestTimestamp) {
1129 nLowestTimestamp = timestamp;
1133 if (fRescan && fRunScan && requests.size()) {
1134 int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, true /* update */);
1135 pwallet->ReacceptWalletTransactions();
1137 if (scannedTime > nLowestTimestamp) {
1138 std::vector<UniValue> results = response.getValues();
1139 response.clear();
1140 response.setArray();
1141 size_t i = 0;
1142 for (const UniValue& request : requests.getValues()) {
1143 // If key creation date is within the successfully scanned
1144 // range, or if the import result already has an error set, let
1145 // the result stand unmodified. Otherwise replace the result
1146 // with an error message.
1147 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1148 response.push_back(results.at(i));
1149 } else {
1150 UniValue result = UniValue(UniValue::VOBJ);
1151 result.pushKV("success", UniValue(false));
1152 result.pushKV(
1153 "error",
1154 JSONRPCError(
1155 RPC_MISC_ERROR,
1156 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1157 "block from time %d, which is after or within %d seconds of key creation, and "
1158 "could contain transactions pertaining to the key. As a result, transactions "
1159 "and coins using this key may not appear in the wallet. This error could be "
1160 "caused by pruning or data corruption (see bitcoind log for details) and could "
1161 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1162 "and -rescan options).",
1163 GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1164 response.push_back(std::move(result));
1166 ++i;
1171 return response;