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.
7 #include "rpc/safemode.h"
8 #include "rpc/server.h"
10 #include "validation.h"
11 #include "script/script.h"
12 #include "script/standard.h"
17 #include "merkleblock.h"
20 #include "rpcwallet.h"
25 #include <boost/algorithm/string.hpp>
26 #include <boost/date_time/posix_time/posix_time.hpp>
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
);
41 boost::posix_time::ptime
ptime(boost::date_time::not_a_date_time
);
43 if (ptime
.is_not_a_date_time())
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);
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));
74 UniValue
importprivkey(const JSONRPCRequest
& request
)
76 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
77 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
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"
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"
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
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
)) {
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);
151 pwallet
->RescanFromTime(TIMESTAMP_MIN
, true /* update */);
158 UniValue
abortrescan(const JSONRPCRequest
& request
)
160 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
161 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
165 if (request
.fHelp
|| request
.params
.size() > 0)
166 throw std::runtime_error(
168 "\nStops current wallet rescan triggered e.g. by an importprivkey call.\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", "")
179 if (!pwallet
->IsScanning() || pwallet
->IsAbortingRescan()) return false;
180 pwallet
->AbortRescan();
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
);
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
)) {
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"
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"
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
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
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
)) {
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
);
278 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address or script");
283 pwallet
->RescanFromTime(TIMESTAMP_MIN
, true /* update */);
284 pwallet
->ReacceptWalletTransactions();
290 UniValue
importprunedfunds(const JSONRPCRequest
& request
)
292 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
293 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
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"
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
;
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
) {
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()];
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);
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
)) {
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"
363 "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
365 + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
366 "\nAs a JSON-RPC call\n"
367 + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
370 LOCK2(cs_main
, pwallet
->cs_wallet
);
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.");
389 UniValue
importpubkey(const JSONRPCRequest
& request
)
391 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
392 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
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"
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"
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
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);
441 pwallet
->RescanFromTime(TIMESTAMP_MIN
, true /* update */);
442 pwallet
->ReacceptWalletTransactions();
449 UniValue
importwallet(const JSONRPCRequest
& request
)
451 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
452 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
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"
461 "1. \"filename\" (string, required) The wallet file\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\"")
472 throw JSONRPCError(RPC_WALLET_ERROR
, "Importing wallets is disabled in pruned mode");
474 LOCK2(cs_main
, pwallet
->cs_wallet
);
476 EnsureWalletIsUnlocked(pwallet
);
479 file
.open(request
.params
[0].get_str().c_str(), std::ios::in
| std::ios::ate
);
481 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
483 int64_t nTimeBegin
= chainActive
.Tip()->GetBlockTime();
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))));
494 std::getline(file
, line
);
495 if (line
.empty() || line
[0] == '#')
498 std::vector
<std::string
> vstr
;
499 boost::split(vstr
, line
, boost::is_any_of(" "));
502 CBitcoinSecret vchSecret
;
503 if (!vchSecret
.SetString(vstr
[0]))
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
));
513 int64_t nTime
= DecodeDumpTime(vstr
[1]);
514 std::string strLabel
;
516 for (unsigned int nStr
= 2; nStr
< vstr
.size(); nStr
++) {
517 if (boost::algorithm::starts_with(vstr
[nStr
], "#"))
519 if (vstr
[nStr
] == "change=1")
521 if (vstr
[nStr
] == "reserve=1")
523 if (boost::algorithm::starts_with(vstr
[nStr
], "label=")) {
524 strLabel
= DecodeDumpString(vstr
[nStr
].substr(6));
528 LogPrintf("Importing %s...\n", EncodeDestination(keyid
));
529 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
533 pwallet
->mapKeyMetadata
[keyid
].nCreateTime
= nTime
;
535 pwallet
->SetAddressBook(keyid
, strLabel
, "receive");
536 nTimeBegin
= std::min(nTimeBegin
, nTime
);
539 pwallet
->ShowProgress("", 100); // hide progress dialog in GUI
540 pwallet
->UpdateTimeFirstKey(nTimeBegin
);
541 pwallet
->RescanFromTime(nTimeBegin
, false /* update */);
542 pwallet
->MarkDirty();
545 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding some keys to wallet");
550 UniValue
dumpprivkey(const JSONRPCRequest
& request
)
552 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
553 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
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"
563 "1. \"address\" (string, required) The bitcoin address for the private key\n"
565 "\"key\" (string) The private key\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
);
583 throw JSONRPCError(RPC_TYPE_ERROR
, "Address does not refer to a key");
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
)) {
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"
605 "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
608 " \"filename\" : { (string) The filename with full absolute path\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");
632 file
.open(filepath
.string().c_str());
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
));
648 std::sort(vKeyBirth
.begin(), vKeyBirth
.end());
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()));
657 // add the base58check encoded extended master if the wallet uses HD
658 CKeyID masterKeyID
= pwallet
->GetHDChain().masterKeyID
;
659 if (!masterKeyID
.IsNull())
662 if (pwallet
->GetKey(masterKeyID
, key
)) {
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
);
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
)) {
685 } else if (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
== "m") {
686 file
<< "inactivehdmaster=1";
690 file
<< strprintf(" # addr=%s%s\n", strAddr
, (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
.size() > 0 ? " hdkeypath="+pwallet
->mapKeyMetadata
[keyid
].hdKeypath
: ""));
694 file
<< "# End of dump\n";
697 UniValue
reply(UniValue::VOBJ
);
698 reply
.push_back(Pair("filename", filepath
.string()));
704 UniValue
ProcessImport(CWallet
* const pwallet
, const UniValue
& data
, const int64_t timestamp
)
707 bool success
= false;
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");
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();
734 dest
= DecodeDestination(output
);
735 if (!IsValidDestination(dest
)) {
736 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid address");
738 script
= GetScriptForDestination(dest
);
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");
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");
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.
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
);
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
);
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.
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");
921 // Import private keys.
923 const std::string
& strPrivkey
= keys
[0].get_str();
926 CBitcoinSecret vchSecret
;
927 bool fGood
= vchSecret
.SetString(strPrivkey
);
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.
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
)) {
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
);
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");
1001 UniValue result
= UniValue(UniValue::VOBJ
);
1002 result
.pushKV("success", UniValue(success
));
1004 } catch (const UniValue
& e
) {
1005 UniValue result
= UniValue(UniValue::VOBJ
);
1006 result
.pushKV("success", UniValue(false));
1007 result
.pushKV("error", e
);
1010 UniValue result
= UniValue(UniValue::VOBJ
);
1011 result
.pushKV("success", UniValue(false));
1012 result
.pushKV("error", JSONRPCError(RPC_MISC_ERROR
, "Missing required fields"));
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") {
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
;
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"
1044 "1. requests (array, required) Data to be imported\n"
1045 " [ (array of json objects)\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"
1063 "2. options (json, optional)\n"
1065 " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\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");
1077 RPCTypeCheck(mainRequest
.params
, {UniValue::VARR
, UniValue::VOBJ
});
1079 const UniValue
& requests
= mainRequest
.params
[0];
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();
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
);
1122 // If at least one request was successful then allow rescan.
1123 if (result
["success"].get_bool()) {
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();
1140 response
.setArray();
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
));
1150 UniValue result
= UniValue(UniValue::VOBJ
);
1151 result
.pushKV("success", UniValue(false));
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
));