Merge #10839: Don't use pass by reference to const for cheaply-copied types (bool...
[bitcoinplatinum.git] / src / wallet / rpcdump.cpp
blob6ca947bf1bbe5c5e15d3ab27e8a4284d196bd2fa
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 <wallet/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/wallet.h>
17 #include <merkleblock.h>
18 #include <core_io.h>
20 #include <wallet/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 \"privkey\" ( \"label\" ) ( rescan )\n"
84 "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
85 "\nArguments:\n"
86 "1. \"privkey\" (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 by an RPC call, 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. Requires a new wallet backup.\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.tx)) {
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. Requires a new wallet backup.\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). Requires a new wallet backup to include imported keys.\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 "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n"
605 "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
606 "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
607 "\nArguments:\n"
608 "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
609 "\nResult:\n"
610 "{ (json object)\n"
611 " \"filename\" : { (string) The filename with full absolute path\n"
612 "}\n"
613 "\nExamples:\n"
614 + HelpExampleCli("dumpwallet", "\"test\"")
615 + HelpExampleRpc("dumpwallet", "\"test\"")
618 LOCK2(cs_main, pwallet->cs_wallet);
620 EnsureWalletIsUnlocked(pwallet);
622 boost::filesystem::path filepath = request.params[0].get_str();
623 filepath = boost::filesystem::absolute(filepath);
625 /* Prevent arbitrary files from being overwritten. There have been reports
626 * that users have overwritten wallet files this way:
627 * https://github.com/bitcoin/bitcoin/issues/9934
628 * It may also avoid other security issues.
630 if (boost::filesystem::exists(filepath)) {
631 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");
634 std::ofstream file;
635 file.open(filepath.string().c_str());
636 if (!file.is_open())
637 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
639 std::map<CTxDestination, int64_t> mapKeyBirth;
640 const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
641 pwallet->GetKeyBirthTimes(mapKeyBirth);
643 // sort time/key pairs
644 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
645 for (const auto& entry : mapKeyBirth) {
646 if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test
647 vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
650 mapKeyBirth.clear();
651 std::sort(vKeyBirth.begin(), vKeyBirth.end());
653 // produce output
654 file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
655 file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
656 file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
657 file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
658 file << "\n";
660 // add the base58check encoded extended master if the wallet uses HD
661 CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
662 if (!masterKeyID.IsNull())
664 CKey key;
665 if (pwallet->GetKey(masterKeyID, key)) {
666 CExtKey masterKey;
667 masterKey.SetMaster(key.begin(), key.size());
669 CBitcoinExtKey b58extkey;
670 b58extkey.SetKey(masterKey);
672 file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n";
675 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
676 const CKeyID &keyid = it->second;
677 std::string strTime = EncodeDumpTime(it->first);
678 std::string strAddr = EncodeDestination(keyid);
679 CKey key;
680 if (pwallet->GetKey(keyid, key)) {
681 file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
682 if (pwallet->mapAddressBook.count(keyid)) {
683 file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name));
684 } else if (keyid == masterKeyID) {
685 file << "hdmaster=1";
686 } else if (mapKeyPool.count(keyid)) {
687 file << "reserve=1";
688 } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
689 file << "inactivehdmaster=1";
690 } else {
691 file << "change=1";
693 file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwallet->mapKeyMetadata[keyid].hdKeypath : ""));
696 file << "\n";
697 file << "# End of dump\n";
698 file.close();
700 UniValue reply(UniValue::VOBJ);
701 reply.push_back(Pair("filename", filepath.string()));
703 return reply;
707 UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp)
709 try {
710 bool success = false;
712 // Required fields.
713 const UniValue& scriptPubKey = data["scriptPubKey"];
715 // Should have script or JSON with "address".
716 if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
717 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
720 // Optional fields.
721 const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
722 const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
723 const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
724 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
725 const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
726 const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
728 bool isScript = scriptPubKey.getType() == UniValue::VSTR;
729 bool isP2SH = strRedeemScript.length() > 0;
730 const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
732 // Parse the output.
733 CScript script;
734 CTxDestination dest;
736 if (!isScript) {
737 dest = DecodeDestination(output);
738 if (!IsValidDestination(dest)) {
739 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
741 script = GetScriptForDestination(dest);
742 } else {
743 if (!IsHex(output)) {
744 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
747 std::vector<unsigned char> vData(ParseHex(output));
748 script = CScript(vData.begin(), vData.end());
751 // Watchonly and private keys
752 if (watchOnly && keys.size()) {
753 throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
756 // Internal + Label
757 if (internal && data.exists("label")) {
758 throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
761 // Not having Internal + Script
762 if (!internal && isScript) {
763 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
766 // Keys / PubKeys size check.
767 if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
768 throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
771 // Invalid P2SH redeemScript
772 if (isP2SH && !IsHex(strRedeemScript)) {
773 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
776 // Process. //
778 // P2SH
779 if (isP2SH) {
780 // Import redeem script.
781 std::vector<unsigned char> vData(ParseHex(strRedeemScript));
782 CScript redeemScript = CScript(vData.begin(), vData.end());
784 // Invalid P2SH address
785 if (!script.IsPayToScriptHash()) {
786 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
789 pwallet->MarkDirty();
791 if (!pwallet->AddWatchOnly(redeemScript, timestamp)) {
792 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
795 if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) {
796 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
799 CTxDestination redeem_dest = CScriptID(redeemScript);
800 CScript redeemDestination = GetScriptForDestination(redeem_dest);
802 if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
803 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
806 pwallet->MarkDirty();
808 if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) {
809 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
812 // add to address book or update label
813 if (IsValidDestination(dest)) {
814 pwallet->SetAddressBook(dest, label, "receive");
817 // Import private keys.
818 if (keys.size()) {
819 for (size_t i = 0; i < keys.size(); i++) {
820 const std::string& privkey = keys[i].get_str();
822 CBitcoinSecret vchSecret;
823 bool fGood = vchSecret.SetString(privkey);
825 if (!fGood) {
826 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
829 CKey key = vchSecret.GetKey();
831 if (!key.IsValid()) {
832 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
835 CPubKey pubkey = key.GetPubKey();
836 assert(key.VerifyPubKey(pubkey));
838 CKeyID vchAddress = pubkey.GetID();
839 pwallet->MarkDirty();
840 pwallet->SetAddressBook(vchAddress, label, "receive");
842 if (pwallet->HaveKey(vchAddress)) {
843 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
846 pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
848 if (!pwallet->AddKeyPubKey(key, pubkey)) {
849 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
852 pwallet->UpdateTimeFirstKey(timestamp);
856 success = true;
857 } else {
858 // Import public keys.
859 if (pubKeys.size() && keys.size() == 0) {
860 const std::string& strPubKey = pubKeys[0].get_str();
862 if (!IsHex(strPubKey)) {
863 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
866 std::vector<unsigned char> vData(ParseHex(strPubKey));
867 CPubKey pubKey(vData.begin(), vData.end());
869 if (!pubKey.IsFullyValid()) {
870 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
873 CTxDestination pubkey_dest = pubKey.GetID();
875 // Consistency check.
876 if (!isScript && !(pubkey_dest == dest)) {
877 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
880 // Consistency check.
881 if (isScript) {
882 CTxDestination destination;
884 if (ExtractDestination(script, destination)) {
885 if (!(destination == pubkey_dest)) {
886 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
891 CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
893 if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
894 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
897 pwallet->MarkDirty();
899 if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) {
900 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
903 // add to address book or update label
904 if (IsValidDestination(pubkey_dest)) {
905 pwallet->SetAddressBook(pubkey_dest, label, "receive");
908 // TODO Is this necessary?
909 CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
911 if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
912 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
915 pwallet->MarkDirty();
917 if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
918 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
921 success = true;
924 // Import private keys.
925 if (keys.size()) {
926 const std::string& strPrivkey = keys[0].get_str();
928 // Checks.
929 CBitcoinSecret vchSecret;
930 bool fGood = vchSecret.SetString(strPrivkey);
932 if (!fGood) {
933 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
936 CKey key = vchSecret.GetKey();
937 if (!key.IsValid()) {
938 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
941 CPubKey pubKey = key.GetPubKey();
942 assert(key.VerifyPubKey(pubKey));
944 CTxDestination pubkey_dest = pubKey.GetID();
946 // Consistency check.
947 if (!isScript && !(pubkey_dest == dest)) {
948 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
951 // Consistency check.
952 if (isScript) {
953 CTxDestination destination;
955 if (ExtractDestination(script, destination)) {
956 if (!(destination == pubkey_dest)) {
957 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
962 CKeyID vchAddress = pubKey.GetID();
963 pwallet->MarkDirty();
964 pwallet->SetAddressBook(vchAddress, label, "receive");
966 if (pwallet->HaveKey(vchAddress)) {
967 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
970 pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
972 if (!pwallet->AddKeyPubKey(key, pubKey)) {
973 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
976 pwallet->UpdateTimeFirstKey(timestamp);
978 success = true;
981 // Import scriptPubKey only.
982 if (pubKeys.size() == 0 && keys.size() == 0) {
983 if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
984 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
987 pwallet->MarkDirty();
989 if (!pwallet->AddWatchOnly(script, timestamp)) {
990 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
993 if (scriptPubKey.getType() == UniValue::VOBJ) {
994 // add to address book or update label
995 if (IsValidDestination(dest)) {
996 pwallet->SetAddressBook(dest, label, "receive");
1000 success = true;
1004 UniValue result = UniValue(UniValue::VOBJ);
1005 result.pushKV("success", UniValue(success));
1006 return result;
1007 } catch (const UniValue& e) {
1008 UniValue result = UniValue(UniValue::VOBJ);
1009 result.pushKV("success", UniValue(false));
1010 result.pushKV("error", e);
1011 return result;
1012 } catch (...) {
1013 UniValue result = UniValue(UniValue::VOBJ);
1014 result.pushKV("success", UniValue(false));
1015 result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
1016 return result;
1020 int64_t GetImportTimestamp(const UniValue& data, int64_t now)
1022 if (data.exists("timestamp")) {
1023 const UniValue& timestamp = data["timestamp"];
1024 if (timestamp.isNum()) {
1025 return timestamp.get_int64();
1026 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
1027 return now;
1029 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
1031 throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
1034 UniValue importmulti(const JSONRPCRequest& mainRequest)
1036 CWallet * const pwallet = GetWalletForJSONRPCRequest(mainRequest);
1037 if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
1038 return NullUniValue;
1041 // clang-format off
1042 if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
1043 throw std::runtime_error(
1044 "importmulti \"requests\" ( \"options\" )\n\n"
1045 "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
1046 "Arguments:\n"
1047 "1. requests (array, required) Data to be imported\n"
1048 " [ (array of json objects)\n"
1049 " {\n"
1050 " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
1051 " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
1052 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1053 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1054 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1055 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1056 " creation time of all keys being imported by the importmulti call will be scanned.\n"
1057 " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
1058 " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
1059 " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
1060 " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
1061 " \"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"
1062 " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
1063 " }\n"
1064 " ,...\n"
1065 " ]\n"
1066 "2. options (json, optional)\n"
1067 " {\n"
1068 " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
1069 " }\n"
1070 "\nExamples:\n" +
1071 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1072 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1073 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
1075 "\nResponse is an array with the same size as the input that has the execution result :\n"
1076 " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
1078 // clang-format on
1080 RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1082 const UniValue& requests = mainRequest.params[0];
1084 //Default options
1085 bool fRescan = true;
1087 if (!mainRequest.params[1].isNull()) {
1088 const UniValue& options = mainRequest.params[1];
1090 if (options.exists("rescan")) {
1091 fRescan = options["rescan"].get_bool();
1095 LOCK2(cs_main, pwallet->cs_wallet);
1096 EnsureWalletIsUnlocked(pwallet);
1098 // Verify all timestamps are present before importing any keys.
1099 const int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0;
1100 for (const UniValue& data : requests.getValues()) {
1101 GetImportTimestamp(data, now);
1104 bool fRunScan = false;
1105 const int64_t minimumTimestamp = 1;
1106 int64_t nLowestTimestamp = 0;
1108 if (fRescan && chainActive.Tip()) {
1109 nLowestTimestamp = chainActive.Tip()->GetBlockTime();
1110 } else {
1111 fRescan = false;
1114 UniValue response(UniValue::VARR);
1116 for (const UniValue& data : requests.getValues()) {
1117 const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
1118 const UniValue result = ProcessImport(pwallet, data, timestamp);
1119 response.push_back(result);
1121 if (!fRescan) {
1122 continue;
1125 // If at least one request was successful then allow rescan.
1126 if (result["success"].get_bool()) {
1127 fRunScan = true;
1130 // Get the lowest timestamp.
1131 if (timestamp < nLowestTimestamp) {
1132 nLowestTimestamp = timestamp;
1136 if (fRescan && fRunScan && requests.size()) {
1137 int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, true /* update */);
1138 pwallet->ReacceptWalletTransactions();
1140 if (scannedTime > nLowestTimestamp) {
1141 std::vector<UniValue> results = response.getValues();
1142 response.clear();
1143 response.setArray();
1144 size_t i = 0;
1145 for (const UniValue& request : requests.getValues()) {
1146 // If key creation date is within the successfully scanned
1147 // range, or if the import result already has an error set, let
1148 // the result stand unmodified. Otherwise replace the result
1149 // with an error message.
1150 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1151 response.push_back(results.at(i));
1152 } else {
1153 UniValue result = UniValue(UniValue::VOBJ);
1154 result.pushKV("success", UniValue(false));
1155 result.pushKV(
1156 "error",
1157 JSONRPCError(
1158 RPC_MISC_ERROR,
1159 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1160 "block from time %d, which is after or within %d seconds of key creation, and "
1161 "could contain transactions pertaining to the key. As a result, transactions "
1162 "and coins using this key may not appear in the wallet. This error could be "
1163 "caused by pruning or data corruption (see bitcoind log for details) and could "
1164 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1165 "and -rescan options).",
1166 GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1167 response.push_back(std::move(result));
1169 ++i;
1174 return response;