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/server.h"
9 #include "validation.h"
10 #include "script/script.h"
11 #include "script/standard.h"
16 #include "merkleblock.h"
19 #include "rpcwallet.h"
24 #include <boost/algorithm/string.hpp>
25 #include <boost/date_time/posix_time/posix_time.hpp>
29 #include <boost/foreach.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 BOOST_FOREACH(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
.size() > 1)
111 strLabel
= request
.params
[1].get_str();
113 // Whether to perform rescan after import
115 if (request
.params
.size() > 2)
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
->ScanForWalletTransactions(chainActive
.Genesis(), true);
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", "")
178 if (!pwallet
->IsScanning() || pwallet
->IsAbortingRescan()) return false;
179 pwallet
->AbortRescan();
183 void ImportAddress(CWallet
*, const CBitcoinAddress
& address
, const std::string
& strLabel
);
184 void ImportScript(CWallet
* const pwallet
, const CScript
& script
, const std::string
& strLabel
, bool isRedeemScript
)
186 if (!isRedeemScript
&& ::IsMine(*pwallet
, script
) == ISMINE_SPENDABLE
) {
187 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
190 pwallet
->MarkDirty();
192 if (!pwallet
->HaveWatchOnly(script
) && !pwallet
->AddWatchOnly(script
, 0 /* nCreateTime */)) {
193 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
196 if (isRedeemScript
) {
197 if (!pwallet
->HaveCScript(script
) && !pwallet
->AddCScript(script
)) {
198 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding p2sh redeemScript to wallet");
200 ImportAddress(pwallet
, CBitcoinAddress(CScriptID(script
)), strLabel
);
202 CTxDestination destination
;
203 if (ExtractDestination(script
, destination
)) {
204 pwallet
->SetAddressBook(destination
, strLabel
, "receive");
209 void ImportAddress(CWallet
* const pwallet
, const CBitcoinAddress
& address
, const std::string
& strLabel
)
211 CScript script
= GetScriptForDestination(address
.Get());
212 ImportScript(pwallet
, script
, strLabel
, false);
213 // add to address book or update label
214 if (address
.IsValid())
215 pwallet
->SetAddressBook(address
.Get(), strLabel
, "receive");
218 UniValue
importaddress(const JSONRPCRequest
& request
)
220 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
221 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
225 if (request
.fHelp
|| request
.params
.size() < 1 || request
.params
.size() > 4)
226 throw std::runtime_error(
227 "importaddress \"address\" ( \"label\" rescan p2sh )\n"
228 "\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 "1. \"script\" (string, required) The hex-encoded script (or address)\n"
231 "2. \"label\" (string, optional, default=\"\") An optional label\n"
232 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
233 "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
234 "\nNote: This call can take minutes to complete if rescan is true.\n"
235 "If you have the full public key, you should call importpubkey instead of this.\n"
236 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
237 "as change, and not show up in many RPCs.\n"
239 "\nImport a script with rescan\n"
240 + HelpExampleCli("importaddress", "\"myscript\"") +
241 "\nImport using a label without rescan\n"
242 + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
243 "\nAs a JSON-RPC call\n"
244 + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")
248 std::string strLabel
= "";
249 if (request
.params
.size() > 1)
250 strLabel
= request
.params
[1].get_str();
252 // Whether to perform rescan after import
254 if (request
.params
.size() > 2)
255 fRescan
= request
.params
[2].get_bool();
257 if (fRescan
&& fPruneMode
)
258 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
260 // Whether to import a p2sh version, too
262 if (request
.params
.size() > 3)
263 fP2SH
= request
.params
[3].get_bool();
265 LOCK2(cs_main
, pwallet
->cs_wallet
);
267 CBitcoinAddress
address(request
.params
[0].get_str());
268 if (address
.IsValid()) {
270 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Cannot use the p2sh flag with an address - use a script instead");
271 ImportAddress(pwallet
, address
, strLabel
);
272 } else if (IsHex(request
.params
[0].get_str())) {
273 std::vector
<unsigned char> data(ParseHex(request
.params
[0].get_str()));
274 ImportScript(pwallet
, CScript(data
.begin(), data
.end()), strLabel
, fP2SH
);
276 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address or script");
281 pwallet
->ScanForWalletTransactions(chainActive
.Genesis(), true);
282 pwallet
->ReacceptWalletTransactions();
288 UniValue
importprunedfunds(const JSONRPCRequest
& request
)
290 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
291 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
295 if (request
.fHelp
|| request
.params
.size() != 2)
296 throw std::runtime_error(
297 "importprunedfunds\n"
298 "\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"
300 "1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
301 "2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
304 CMutableTransaction tx
;
305 if (!DecodeHexTx(tx
, request
.params
[0].get_str()))
306 throw JSONRPCError(RPC_DESERIALIZATION_ERROR
, "TX decode failed");
307 uint256 hashTx
= tx
.GetHash();
308 CWalletTx
wtx(pwallet
, MakeTransactionRef(std::move(tx
)));
310 CDataStream
ssMB(ParseHexV(request
.params
[1], "proof"), SER_NETWORK
, PROTOCOL_VERSION
);
311 CMerkleBlock merkleBlock
;
314 //Search partial merkle tree in proof for our transaction and index in valid block
315 std::vector
<uint256
> vMatch
;
316 std::vector
<unsigned int> vIndex
;
317 unsigned int txnIndex
= 0;
318 if (merkleBlock
.txn
.ExtractMatches(vMatch
, vIndex
) == merkleBlock
.header
.hashMerkleRoot
) {
322 if (!mapBlockIndex
.count(merkleBlock
.header
.GetHash()) || !chainActive
.Contains(mapBlockIndex
[merkleBlock
.header
.GetHash()]))
323 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Block not found in chain");
325 std::vector
<uint256
>::const_iterator it
;
326 if ((it
= std::find(vMatch
.begin(), vMatch
.end(), hashTx
))==vMatch
.end()) {
327 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Transaction given doesn't exist in proof");
330 txnIndex
= vIndex
[it
- vMatch
.begin()];
333 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Something wrong with merkleblock");
336 wtx
.nIndex
= txnIndex
;
337 wtx
.hashBlock
= merkleBlock
.header
.GetHash();
339 LOCK2(cs_main
, pwallet
->cs_wallet
);
341 if (pwallet
->IsMine(wtx
)) {
342 pwallet
->AddToWallet(wtx
, false);
346 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "No addresses in wallet correspond to included transaction");
349 UniValue
removeprunedfunds(const JSONRPCRequest
& request
)
351 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
352 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
356 if (request
.fHelp
|| request
.params
.size() != 1)
357 throw std::runtime_error(
358 "removeprunedfunds \"txid\"\n"
359 "\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"
361 "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
363 + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
364 "\nAs a JSON-RPC call\n"
365 + HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
368 LOCK2(cs_main
, pwallet
->cs_wallet
);
371 hash
.SetHex(request
.params
[0].get_str());
372 std::vector
<uint256
> vHash
;
373 vHash
.push_back(hash
);
374 std::vector
<uint256
> vHashOut
;
376 if (pwallet
->ZapSelectTx(vHash
, vHashOut
) != DB_LOAD_OK
) {
377 throw JSONRPCError(RPC_WALLET_ERROR
, "Could not properly delete the transaction.");
380 if(vHashOut
.empty()) {
381 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Transaction does not exist in wallet.");
387 UniValue
importpubkey(const JSONRPCRequest
& request
)
389 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
390 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
394 if (request
.fHelp
|| request
.params
.size() < 1 || request
.params
.size() > 4)
395 throw std::runtime_error(
396 "importpubkey \"pubkey\" ( \"label\" rescan )\n"
397 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
399 "1. \"pubkey\" (string, required) The hex-encoded public key\n"
400 "2. \"label\" (string, optional, default=\"\") An optional label\n"
401 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
402 "\nNote: This call can take minutes to complete if rescan is true.\n"
404 "\nImport a public key with rescan\n"
405 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
406 "\nImport using a label without rescan\n"
407 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
408 "\nAs a JSON-RPC call\n"
409 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
413 std::string strLabel
= "";
414 if (request
.params
.size() > 1)
415 strLabel
= request
.params
[1].get_str();
417 // Whether to perform rescan after import
419 if (request
.params
.size() > 2)
420 fRescan
= request
.params
[2].get_bool();
422 if (fRescan
&& fPruneMode
)
423 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
425 if (!IsHex(request
.params
[0].get_str()))
426 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey must be a hex string");
427 std::vector
<unsigned char> data(ParseHex(request
.params
[0].get_str()));
428 CPubKey
pubKey(data
.begin(), data
.end());
429 if (!pubKey
.IsFullyValid())
430 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey is not a valid public key");
432 LOCK2(cs_main
, pwallet
->cs_wallet
);
434 ImportAddress(pwallet
, CBitcoinAddress(pubKey
.GetID()), strLabel
);
435 ImportScript(pwallet
, GetScriptForRawPubKey(pubKey
), strLabel
, false);
439 pwallet
->ScanForWalletTransactions(chainActive
.Genesis(), true);
440 pwallet
->ReacceptWalletTransactions();
447 UniValue
importwallet(const JSONRPCRequest
& request
)
449 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
450 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
454 if (request
.fHelp
|| request
.params
.size() != 1)
455 throw std::runtime_error(
456 "importwallet \"filename\"\n"
457 "\nImports keys from a wallet dump file (see dumpwallet).\n"
459 "1. \"filename\" (string, required) The wallet file\n"
461 "\nDump the wallet\n"
462 + HelpExampleCli("dumpwallet", "\"test\"") +
463 "\nImport the wallet\n"
464 + HelpExampleCli("importwallet", "\"test\"") +
465 "\nImport using the json rpc call\n"
466 + HelpExampleRpc("importwallet", "\"test\"")
470 throw JSONRPCError(RPC_WALLET_ERROR
, "Importing wallets is disabled in pruned mode");
472 LOCK2(cs_main
, pwallet
->cs_wallet
);
474 EnsureWalletIsUnlocked(pwallet
);
477 file
.open(request
.params
[0].get_str().c_str(), std::ios::in
| std::ios::ate
);
479 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
481 int64_t nTimeBegin
= chainActive
.Tip()->GetBlockTime();
485 int64_t nFilesize
= std::max((int64_t)1, (int64_t)file
.tellg());
486 file
.seekg(0, file
.beg
);
488 pwallet
->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
489 while (file
.good()) {
490 pwallet
->ShowProgress("", std::max(1, std::min(99, (int)(((double)file
.tellg() / (double)nFilesize
) * 100))));
492 std::getline(file
, line
);
493 if (line
.empty() || line
[0] == '#')
496 std::vector
<std::string
> vstr
;
497 boost::split(vstr
, line
, boost::is_any_of(" "));
500 CBitcoinSecret vchSecret
;
501 if (!vchSecret
.SetString(vstr
[0]))
503 CKey key
= vchSecret
.GetKey();
504 CPubKey pubkey
= key
.GetPubKey();
505 assert(key
.VerifyPubKey(pubkey
));
506 CKeyID keyid
= pubkey
.GetID();
507 if (pwallet
->HaveKey(keyid
)) {
508 LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid
).ToString());
511 int64_t nTime
= DecodeDumpTime(vstr
[1]);
512 std::string strLabel
;
514 for (unsigned int nStr
= 2; nStr
< vstr
.size(); nStr
++) {
515 if (boost::algorithm::starts_with(vstr
[nStr
], "#"))
517 if (vstr
[nStr
] == "change=1")
519 if (vstr
[nStr
] == "reserve=1")
521 if (boost::algorithm::starts_with(vstr
[nStr
], "label=")) {
522 strLabel
= DecodeDumpString(vstr
[nStr
].substr(6));
526 LogPrintf("Importing %s...\n", CBitcoinAddress(keyid
).ToString());
527 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
531 pwallet
->mapKeyMetadata
[keyid
].nCreateTime
= nTime
;
533 pwallet
->SetAddressBook(keyid
, strLabel
, "receive");
534 nTimeBegin
= std::min(nTimeBegin
, nTime
);
537 pwallet
->ShowProgress("", 100); // hide progress dialog in GUI
538 pwallet
->UpdateTimeFirstKey(nTimeBegin
);
540 CBlockIndex
*pindex
= chainActive
.FindEarliestAtLeast(nTimeBegin
- TIMESTAMP_WINDOW
);
542 LogPrintf("Rescanning last %i blocks\n", pindex
? chainActive
.Height() - pindex
->nHeight
+ 1 : 0);
543 pwallet
->ScanForWalletTransactions(pindex
);
544 pwallet
->MarkDirty();
547 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding some keys to wallet");
552 UniValue
dumpprivkey(const JSONRPCRequest
& request
)
554 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
555 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
559 if (request
.fHelp
|| request
.params
.size() != 1)
560 throw std::runtime_error(
561 "dumpprivkey \"address\"\n"
562 "\nReveals the private key corresponding to 'address'.\n"
563 "Then the importprivkey can be used with this output\n"
565 "1. \"address\" (string, required) The bitcoin address for the private key\n"
567 "\"key\" (string) The private key\n"
569 + HelpExampleCli("dumpprivkey", "\"myaddress\"")
570 + HelpExampleCli("importprivkey", "\"mykey\"")
571 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
574 LOCK2(cs_main
, pwallet
->cs_wallet
);
576 EnsureWalletIsUnlocked(pwallet
);
578 std::string strAddress
= request
.params
[0].get_str();
579 CBitcoinAddress address
;
580 if (!address
.SetString(strAddress
))
581 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address");
583 if (!address
.GetKeyID(keyID
))
584 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.\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
);
620 boost::filesystem::path filepath
= request
.params
[0].get_str();
621 filepath
= boost::filesystem::absolute(filepath
);
622 file
.open(filepath
.string().c_str());
624 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
626 std::map
<CTxDestination
, int64_t> mapKeyBirth
;
627 std::set
<CKeyID
> setKeyPool
;
628 pwallet
->GetKeyBirthTimes(mapKeyBirth
);
629 pwallet
->GetAllReserveKeys(setKeyPool
);
631 // sort time/key pairs
632 std::vector
<std::pair
<int64_t, CKeyID
> > vKeyBirth
;
633 for (const auto& entry
: mapKeyBirth
) {
634 if (const CKeyID
* keyID
= boost::get
<CKeyID
>(&entry
.first
)) { // set and test
635 vKeyBirth
.push_back(std::make_pair(entry
.second
, *keyID
));
639 std::sort(vKeyBirth
.begin(), vKeyBirth
.end());
642 file
<< strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD
);
643 file
<< strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
644 file
<< strprintf("# * Best block at time of backup was %i (%s),\n", chainActive
.Height(), chainActive
.Tip()->GetBlockHash().ToString());
645 file
<< strprintf("# mined on %s\n", EncodeDumpTime(chainActive
.Tip()->GetBlockTime()));
648 // add the base58check encoded extended master if the wallet uses HD
649 CKeyID masterKeyID
= pwallet
->GetHDChain().masterKeyID
;
650 if (!masterKeyID
.IsNull())
653 if (pwallet
->GetKey(masterKeyID
, key
)) {
655 masterKey
.SetMaster(key
.begin(), key
.size());
657 CBitcoinExtKey b58extkey
;
658 b58extkey
.SetKey(masterKey
);
660 file
<< "# extended private masterkey: " << b58extkey
.ToString() << "\n\n";
663 for (std::vector
<std::pair
<int64_t, CKeyID
> >::const_iterator it
= vKeyBirth
.begin(); it
!= vKeyBirth
.end(); it
++) {
664 const CKeyID
&keyid
= it
->second
;
665 std::string strTime
= EncodeDumpTime(it
->first
);
666 std::string strAddr
= CBitcoinAddress(keyid
).ToString();
668 if (pwallet
->GetKey(keyid
, key
)) {
669 file
<< strprintf("%s %s ", CBitcoinSecret(key
).ToString(), strTime
);
670 if (pwallet
->mapAddressBook
.count(keyid
)) {
671 file
<< strprintf("label=%s", EncodeDumpString(pwallet
->mapAddressBook
[keyid
].name
));
672 } else if (keyid
== masterKeyID
) {
673 file
<< "hdmaster=1";
674 } else if (setKeyPool
.count(keyid
)) {
676 } else if (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
== "m") {
677 file
<< "inactivehdmaster=1";
681 file
<< strprintf(" # addr=%s%s\n", strAddr
, (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
.size() > 0 ? " hdkeypath="+pwallet
->mapKeyMetadata
[keyid
].hdKeypath
: ""));
685 file
<< "# End of dump\n";
688 UniValue
reply(UniValue::VOBJ
);
689 reply
.push_back(Pair("filename", filepath
.string()));
695 UniValue
ProcessImport(CWallet
* const pwallet
, const UniValue
& data
, const int64_t timestamp
)
698 bool success
= false;
701 const UniValue
& scriptPubKey
= data
["scriptPubKey"];
703 // Should have script or JSON with "address".
704 if (!(scriptPubKey
.getType() == UniValue::VOBJ
&& scriptPubKey
.exists("address")) && !(scriptPubKey
.getType() == UniValue::VSTR
)) {
705 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Invalid scriptPubKey");
709 const std::string
& strRedeemScript
= data
.exists("redeemscript") ? data
["redeemscript"].get_str() : "";
710 const UniValue
& pubKeys
= data
.exists("pubkeys") ? data
["pubkeys"].get_array() : UniValue();
711 const UniValue
& keys
= data
.exists("keys") ? data
["keys"].get_array() : UniValue();
712 const bool& internal
= data
.exists("internal") ? data
["internal"].get_bool() : false;
713 const bool& watchOnly
= data
.exists("watchonly") ? data
["watchonly"].get_bool() : false;
714 const std::string
& label
= data
.exists("label") && !internal
? data
["label"].get_str() : "";
716 bool isScript
= scriptPubKey
.getType() == UniValue::VSTR
;
717 bool isP2SH
= strRedeemScript
.length() > 0;
718 const std::string
& output
= isScript
? scriptPubKey
.get_str() : scriptPubKey
["address"].get_str();
722 CBitcoinAddress address
;
725 address
= CBitcoinAddress(output
);
726 if (!address
.IsValid()) {
727 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid address");
729 script
= GetScriptForDestination(address
.Get());
731 if (!IsHex(output
)) {
732 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid scriptPubKey");
735 std::vector
<unsigned char> vData(ParseHex(output
));
736 script
= CScript(vData
.begin(), vData
.end());
739 // Watchonly and private keys
740 if (watchOnly
&& keys
.size()) {
741 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Incompatibility found between watchonly and keys");
745 if (internal
&& data
.exists("label")) {
746 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Incompatibility found between internal and label");
749 // Not having Internal + Script
750 if (!internal
&& isScript
) {
751 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Internal must be set for hex scriptPubKey");
754 // Keys / PubKeys size check.
755 if (!isP2SH
&& (keys
.size() > 1 || pubKeys
.size() > 1)) { // Address / scriptPubKey
756 throw JSONRPCError(RPC_INVALID_PARAMETER
, "More than private key given for one address");
759 // Invalid P2SH redeemScript
760 if (isP2SH
&& !IsHex(strRedeemScript
)) {
761 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid redeem script");
768 // Import redeem script.
769 std::vector
<unsigned char> vData(ParseHex(strRedeemScript
));
770 CScript redeemScript
= CScript(vData
.begin(), vData
.end());
772 // Invalid P2SH address
773 if (!script
.IsPayToScriptHash()) {
774 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid P2SH address / script");
777 pwallet
->MarkDirty();
779 if (!pwallet
->AddWatchOnly(redeemScript
, timestamp
)) {
780 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
783 if (!pwallet
->HaveCScript(redeemScript
) && !pwallet
->AddCScript(redeemScript
)) {
784 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding p2sh redeemScript to wallet");
787 CBitcoinAddress redeemAddress
= CBitcoinAddress(CScriptID(redeemScript
));
788 CScript redeemDestination
= GetScriptForDestination(redeemAddress
.Get());
790 if (::IsMine(*pwallet
, redeemDestination
) == ISMINE_SPENDABLE
) {
791 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
794 pwallet
->MarkDirty();
796 if (!pwallet
->AddWatchOnly(redeemDestination
, timestamp
)) {
797 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
800 // add to address book or update label
801 if (address
.IsValid()) {
802 pwallet
->SetAddressBook(address
.Get(), label
, "receive");
805 // Import private keys.
807 for (size_t i
= 0; i
< keys
.size(); i
++) {
808 const std::string
& privkey
= keys
[i
].get_str();
810 CBitcoinSecret vchSecret
;
811 bool fGood
= vchSecret
.SetString(privkey
);
814 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
817 CKey key
= vchSecret
.GetKey();
819 if (!key
.IsValid()) {
820 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
823 CPubKey pubkey
= key
.GetPubKey();
824 assert(key
.VerifyPubKey(pubkey
));
826 CKeyID vchAddress
= pubkey
.GetID();
827 pwallet
->MarkDirty();
828 pwallet
->SetAddressBook(vchAddress
, label
, "receive");
830 if (pwallet
->HaveKey(vchAddress
)) {
831 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Already have this key");
834 pwallet
->mapKeyMetadata
[vchAddress
].nCreateTime
= timestamp
;
836 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
837 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
840 pwallet
->UpdateTimeFirstKey(timestamp
);
846 // Import public keys.
847 if (pubKeys
.size() && keys
.size() == 0) {
848 const std::string
& strPubKey
= pubKeys
[0].get_str();
850 if (!IsHex(strPubKey
)) {
851 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey must be a hex string");
854 std::vector
<unsigned char> vData(ParseHex(strPubKey
));
855 CPubKey
pubKey(vData
.begin(), vData
.end());
857 if (!pubKey
.IsFullyValid()) {
858 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey is not a valid public key");
861 CBitcoinAddress pubKeyAddress
= CBitcoinAddress(pubKey
.GetID());
863 // Consistency check.
864 if (!isScript
&& !(pubKeyAddress
.Get() == address
.Get())) {
865 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
868 // Consistency check.
870 CBitcoinAddress scriptAddress
;
871 CTxDestination destination
;
873 if (ExtractDestination(script
, destination
)) {
874 scriptAddress
= CBitcoinAddress(destination
);
875 if (!(scriptAddress
.Get() == pubKeyAddress
.Get())) {
876 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
881 CScript pubKeyScript
= GetScriptForDestination(pubKeyAddress
.Get());
883 if (::IsMine(*pwallet
, pubKeyScript
) == ISMINE_SPENDABLE
) {
884 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
887 pwallet
->MarkDirty();
889 if (!pwallet
->AddWatchOnly(pubKeyScript
, timestamp
)) {
890 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
893 // add to address book or update label
894 if (pubKeyAddress
.IsValid()) {
895 pwallet
->SetAddressBook(pubKeyAddress
.Get(), label
, "receive");
898 // TODO Is this necessary?
899 CScript scriptRawPubKey
= GetScriptForRawPubKey(pubKey
);
901 if (::IsMine(*pwallet
, scriptRawPubKey
) == ISMINE_SPENDABLE
) {
902 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
905 pwallet
->MarkDirty();
907 if (!pwallet
->AddWatchOnly(scriptRawPubKey
, timestamp
)) {
908 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
914 // Import private keys.
916 const std::string
& strPrivkey
= keys
[0].get_str();
919 CBitcoinSecret vchSecret
;
920 bool fGood
= vchSecret
.SetString(strPrivkey
);
923 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
926 CKey key
= vchSecret
.GetKey();
927 if (!key
.IsValid()) {
928 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
931 CPubKey pubKey
= key
.GetPubKey();
932 assert(key
.VerifyPubKey(pubKey
));
934 CBitcoinAddress pubKeyAddress
= CBitcoinAddress(pubKey
.GetID());
936 // Consistency check.
937 if (!isScript
&& !(pubKeyAddress
.Get() == address
.Get())) {
938 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
941 // Consistency check.
943 CBitcoinAddress scriptAddress
;
944 CTxDestination destination
;
946 if (ExtractDestination(script
, destination
)) {
947 scriptAddress
= CBitcoinAddress(destination
);
948 if (!(scriptAddress
.Get() == pubKeyAddress
.Get())) {
949 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
954 CKeyID vchAddress
= pubKey
.GetID();
955 pwallet
->MarkDirty();
956 pwallet
->SetAddressBook(vchAddress
, label
, "receive");
958 if (pwallet
->HaveKey(vchAddress
)) {
962 pwallet
->mapKeyMetadata
[vchAddress
].nCreateTime
= timestamp
;
964 if (!pwallet
->AddKeyPubKey(key
, pubKey
)) {
965 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
968 pwallet
->UpdateTimeFirstKey(timestamp
);
973 // Import scriptPubKey only.
974 if (pubKeys
.size() == 0 && keys
.size() == 0) {
975 if (::IsMine(*pwallet
, script
) == ISMINE_SPENDABLE
) {
976 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
979 pwallet
->MarkDirty();
981 if (!pwallet
->AddWatchOnly(script
, timestamp
)) {
982 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
985 if (scriptPubKey
.getType() == UniValue::VOBJ
) {
986 // add to address book or update label
987 if (address
.IsValid()) {
988 pwallet
->SetAddressBook(address
.Get(), label
, "receive");
996 UniValue result
= UniValue(UniValue::VOBJ
);
997 result
.pushKV("success", UniValue(success
));
999 } catch (const UniValue
& e
) {
1000 UniValue result
= UniValue(UniValue::VOBJ
);
1001 result
.pushKV("success", UniValue(false));
1002 result
.pushKV("error", e
);
1005 UniValue result
= UniValue(UniValue::VOBJ
);
1006 result
.pushKV("success", UniValue(false));
1007 result
.pushKV("error", JSONRPCError(RPC_MISC_ERROR
, "Missing required fields"));
1012 int64_t GetImportTimestamp(const UniValue
& data
, int64_t now
)
1014 if (data
.exists("timestamp")) {
1015 const UniValue
& timestamp
= data
["timestamp"];
1016 if (timestamp
.isNum()) {
1017 return timestamp
.get_int64();
1018 } else if (timestamp
.isStr() && timestamp
.get_str() == "now") {
1021 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp
.type())));
1023 throw JSONRPCError(RPC_TYPE_ERROR
, "Missing required timestamp field for key");
1026 UniValue
importmulti(const JSONRPCRequest
& mainRequest
)
1028 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(mainRequest
);
1029 if (!EnsureWalletIsAvailable(pwallet
, mainRequest
.fHelp
)) {
1030 return NullUniValue
;
1034 if (mainRequest
.fHelp
|| mainRequest
.params
.size() < 1 || mainRequest
.params
.size() > 2)
1035 throw std::runtime_error(
1036 "importmulti \"requests\" \"options\"\n\n"
1037 "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"
1039 "1. requests (array, required) Data to be imported\n"
1040 " [ (array of json objects)\n"
1042 " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
1043 " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
1044 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1045 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1046 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1047 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1048 " creation time of all keys being imported by the importmulti call will be scanned.\n"
1049 " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
1050 " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
1051 " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
1052 " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n"
1053 " \"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"
1054 " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
1058 "2. options (json, optional)\n"
1060 " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
1063 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1064 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1065 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
1067 "\nResponse is an array with the same size as the input that has the execution result :\n"
1068 " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
1072 RPCTypeCheck(mainRequest
.params
, {UniValue::VARR
, UniValue::VOBJ
});
1074 const UniValue
& requests
= mainRequest
.params
[0];
1077 bool fRescan
= true;
1079 if (mainRequest
.params
.size() > 1) {
1080 const UniValue
& options
= mainRequest
.params
[1];
1082 if (options
.exists("rescan")) {
1083 fRescan
= options
["rescan"].get_bool();
1087 LOCK2(cs_main
, pwallet
->cs_wallet
);
1088 EnsureWalletIsUnlocked(pwallet
);
1090 // Verify all timestamps are present before importing any keys.
1091 const int64_t now
= chainActive
.Tip() ? chainActive
.Tip()->GetMedianTimePast() : 0;
1092 for (const UniValue
& data
: requests
.getValues()) {
1093 GetImportTimestamp(data
, now
);
1096 bool fRunScan
= false;
1097 const int64_t minimumTimestamp
= 1;
1098 int64_t nLowestTimestamp
= 0;
1100 if (fRescan
&& chainActive
.Tip()) {
1101 nLowestTimestamp
= chainActive
.Tip()->GetBlockTime();
1106 UniValue
response(UniValue::VARR
);
1108 BOOST_FOREACH (const UniValue
& data
, requests
.getValues()) {
1109 const int64_t timestamp
= std::max(GetImportTimestamp(data
, now
), minimumTimestamp
);
1110 const UniValue result
= ProcessImport(pwallet
, data
, timestamp
);
1111 response
.push_back(result
);
1117 // If at least one request was successful then allow rescan.
1118 if (result
["success"].get_bool()) {
1122 // Get the lowest timestamp.
1123 if (timestamp
< nLowestTimestamp
) {
1124 nLowestTimestamp
= timestamp
;
1128 if (fRescan
&& fRunScan
&& requests
.size()) {
1129 CBlockIndex
* pindex
= nLowestTimestamp
> minimumTimestamp
? chainActive
.FindEarliestAtLeast(std::max
<int64_t>(nLowestTimestamp
- TIMESTAMP_WINDOW
, 0)) : chainActive
.Genesis();
1130 CBlockIndex
* scanFailed
= nullptr;
1132 scanFailed
= pwallet
->ScanForWalletTransactions(pindex
, true);
1133 pwallet
->ReacceptWalletTransactions();
1137 std::vector
<UniValue
> results
= response
.getValues();
1139 response
.setArray();
1141 for (const UniValue
& request
: requests
.getValues()) {
1142 // If key creation date is within the successfully scanned
1143 // range, or if the import result already has an error set, let
1144 // the result stand unmodified. Otherwise replace the result
1145 // with an error message.
1146 if (GetImportTimestamp(request
, now
) - TIMESTAMP_WINDOW
> scanFailed
->GetBlockTimeMax() || results
.at(i
).exists("error")) {
1147 response
.push_back(results
.at(i
));
1149 UniValue result
= UniValue(UniValue::VOBJ
);
1150 result
.pushKV("success", UniValue(false));
1155 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1156 "block from time %d, which is after or within %d seconds of key creation, and "
1157 "could contain transactions pertaining to the key. As a result, transactions "
1158 "and coins using this key may not appear in the wallet. This error could be "
1159 "caused by pruning or data corruption (see bitcoind log for details) and could "
1160 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1161 "and -rescan options).",
1162 GetImportTimestamp(request
, now
), scanFailed
->GetBlockTimeMax(), TIMESTAMP_WINDOW
)));
1163 response
.push_back(std::move(result
));