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/assign/list_of.hpp>
30 #include <boost/foreach.hpp>
32 std::string
static EncodeDumpTime(int64_t nTime
) {
33 return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime
);
36 int64_t static DecodeDumpTime(const std::string
&str
) {
37 static const boost::posix_time::ptime epoch
= boost::posix_time::from_time_t(0);
38 static const std::locale
loc(std::locale::classic(),
39 new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
40 std::istringstream
iss(str
);
42 boost::posix_time::ptime
ptime(boost::date_time::not_a_date_time
);
44 if (ptime
.is_not_a_date_time())
46 return (ptime
- epoch
).total_seconds();
49 std::string
static EncodeDumpString(const std::string
&str
) {
50 std::stringstream ret
;
51 BOOST_FOREACH(unsigned char c
, str
) {
52 if (c
<= 32 || c
>= 128 || c
== '%') {
53 ret
<< '%' << HexStr(&c
, &c
+ 1);
61 std::string
DecodeDumpString(const std::string
&str
) {
62 std::stringstream ret
;
63 for (unsigned int pos
= 0; pos
< str
.length(); pos
++) {
64 unsigned char c
= str
[pos
];
65 if (c
== '%' && pos
+2 < str
.length()) {
66 c
= (((str
[pos
+1]>>6)*9+((str
[pos
+1]-'0')&15)) << 4) |
67 ((str
[pos
+2]>>6)*9+((str
[pos
+2]-'0')&15));
75 UniValue
importprivkey(const JSONRPCRequest
& request
)
77 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
78 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
82 if (request
.fHelp
|| request
.params
.size() < 1 || request
.params
.size() > 3)
83 throw std::runtime_error(
84 "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n"
85 "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
87 "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
88 "2. \"label\" (string, optional, default=\"\") An optional label\n"
89 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
90 "\nNote: This call can take minutes to complete if rescan is true.\n"
92 "\nDump a private key\n"
93 + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
94 "\nImport the private key with rescan\n"
95 + HelpExampleCli("importprivkey", "\"mykey\"") +
96 "\nImport using a label and without rescan\n"
97 + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
98 "\nAs a JSON-RPC call\n"
99 + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
103 LOCK2(cs_main
, pwallet
->cs_wallet
);
105 EnsureWalletIsUnlocked(pwallet
);
107 std::string strSecret
= request
.params
[0].get_str();
108 std::string strLabel
= "";
109 if (request
.params
.size() > 1)
110 strLabel
= request
.params
[1].get_str();
112 // Whether to perform rescan after import
114 if (request
.params
.size() > 2)
115 fRescan
= request
.params
[2].get_bool();
117 if (fRescan
&& fPruneMode
)
118 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
120 CBitcoinSecret vchSecret
;
121 bool fGood
= vchSecret
.SetString(strSecret
);
123 if (!fGood
) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
125 CKey key
= vchSecret
.GetKey();
126 if (!key
.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
128 CPubKey pubkey
= key
.GetPubKey();
129 assert(key
.VerifyPubKey(pubkey
));
130 CKeyID vchAddress
= pubkey
.GetID();
132 pwallet
->MarkDirty();
133 pwallet
->SetAddressBook(vchAddress
, strLabel
, "receive");
135 // Don't throw error in case a key is already there
136 if (pwallet
->HaveKey(vchAddress
)) {
140 pwallet
->mapKeyMetadata
[vchAddress
].nCreateTime
= 1;
142 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
143 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
146 // whenever a key is imported, we need to scan the whole chain
147 pwallet
->UpdateTimeFirstKey(1);
150 pwallet
->ScanForWalletTransactions(chainActive
.Genesis(), true);
157 void ImportAddress(CWallet
*, const CBitcoinAddress
& address
, const std::string
& strLabel
);
158 void ImportScript(CWallet
* const pwallet
, const CScript
& script
, const std::string
& strLabel
, bool isRedeemScript
)
160 if (!isRedeemScript
&& ::IsMine(*pwallet
, script
) == ISMINE_SPENDABLE
) {
161 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
164 pwallet
->MarkDirty();
166 if (!pwallet
->HaveWatchOnly(script
) && !pwallet
->AddWatchOnly(script
, 0 /* nCreateTime */)) {
167 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
170 if (isRedeemScript
) {
171 if (!pwallet
->HaveCScript(script
) && !pwallet
->AddCScript(script
)) {
172 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding p2sh redeemScript to wallet");
174 ImportAddress(pwallet
, CBitcoinAddress(CScriptID(script
)), strLabel
);
176 CTxDestination destination
;
177 if (ExtractDestination(script
, destination
)) {
178 pwallet
->SetAddressBook(destination
, strLabel
, "receive");
183 void ImportAddress(CWallet
* const pwallet
, const CBitcoinAddress
& address
, const std::string
& strLabel
)
185 CScript script
= GetScriptForDestination(address
.Get());
186 ImportScript(pwallet
, script
, strLabel
, false);
187 // add to address book or update label
188 if (address
.IsValid())
189 pwallet
->SetAddressBook(address
.Get(), strLabel
, "receive");
192 UniValue
importaddress(const JSONRPCRequest
& request
)
194 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
195 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
199 if (request
.fHelp
|| request
.params
.size() < 1 || request
.params
.size() > 4)
200 throw std::runtime_error(
201 "importaddress \"address\" ( \"label\" rescan p2sh )\n"
202 "\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"
204 "1. \"script\" (string, required) The hex-encoded script (or address)\n"
205 "2. \"label\" (string, optional, default=\"\") An optional label\n"
206 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
207 "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
208 "\nNote: This call can take minutes to complete if rescan is true.\n"
209 "If you have the full public key, you should call importpubkey instead of this.\n"
210 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
211 "as change, and not show up in many RPCs.\n"
213 "\nImport a script with rescan\n"
214 + HelpExampleCli("importaddress", "\"myscript\"") +
215 "\nImport using a label without rescan\n"
216 + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
217 "\nAs a JSON-RPC call\n"
218 + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")
222 std::string strLabel
= "";
223 if (request
.params
.size() > 1)
224 strLabel
= request
.params
[1].get_str();
226 // Whether to perform rescan after import
228 if (request
.params
.size() > 2)
229 fRescan
= request
.params
[2].get_bool();
231 if (fRescan
&& fPruneMode
)
232 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
234 // Whether to import a p2sh version, too
236 if (request
.params
.size() > 3)
237 fP2SH
= request
.params
[3].get_bool();
239 LOCK2(cs_main
, pwallet
->cs_wallet
);
241 CBitcoinAddress
address(request
.params
[0].get_str());
242 if (address
.IsValid()) {
244 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Cannot use the p2sh flag with an address - use a script instead");
245 ImportAddress(pwallet
, address
, strLabel
);
246 } else if (IsHex(request
.params
[0].get_str())) {
247 std::vector
<unsigned char> data(ParseHex(request
.params
[0].get_str()));
248 ImportScript(pwallet
, CScript(data
.begin(), data
.end()), strLabel
, fP2SH
);
250 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address or script");
255 pwallet
->ScanForWalletTransactions(chainActive
.Genesis(), true);
256 pwallet
->ReacceptWalletTransactions();
262 UniValue
importprunedfunds(const JSONRPCRequest
& request
)
264 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
265 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
269 if (request
.fHelp
|| request
.params
.size() != 2)
270 throw std::runtime_error(
271 "importprunedfunds\n"
272 "\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"
274 "1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
275 "2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
278 CMutableTransaction tx
;
279 if (!DecodeHexTx(tx
, request
.params
[0].get_str()))
280 throw JSONRPCError(RPC_DESERIALIZATION_ERROR
, "TX decode failed");
281 uint256 hashTx
= tx
.GetHash();
282 CWalletTx
wtx(pwallet
, MakeTransactionRef(std::move(tx
)));
284 CDataStream
ssMB(ParseHexV(request
.params
[1], "proof"), SER_NETWORK
, PROTOCOL_VERSION
);
285 CMerkleBlock merkleBlock
;
288 //Search partial merkle tree in proof for our transaction and index in valid block
289 std::vector
<uint256
> vMatch
;
290 std::vector
<unsigned int> vIndex
;
291 unsigned int txnIndex
= 0;
292 if (merkleBlock
.txn
.ExtractMatches(vMatch
, vIndex
) == merkleBlock
.header
.hashMerkleRoot
) {
296 if (!mapBlockIndex
.count(merkleBlock
.header
.GetHash()) || !chainActive
.Contains(mapBlockIndex
[merkleBlock
.header
.GetHash()]))
297 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Block not found in chain");
299 std::vector
<uint256
>::const_iterator it
;
300 if ((it
= std::find(vMatch
.begin(), vMatch
.end(), hashTx
))==vMatch
.end()) {
301 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Transaction given doesn't exist in proof");
304 txnIndex
= vIndex
[it
- vMatch
.begin()];
307 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Something wrong with merkleblock");
310 wtx
.nIndex
= txnIndex
;
311 wtx
.hashBlock
= merkleBlock
.header
.GetHash();
313 LOCK2(cs_main
, pwallet
->cs_wallet
);
315 if (pwallet
->IsMine(wtx
)) {
316 pwallet
->AddToWallet(wtx
, false);
320 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "No addresses in wallet correspond to included transaction");
323 UniValue
removeprunedfunds(const JSONRPCRequest
& request
)
325 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
326 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
330 if (request
.fHelp
|| request
.params
.size() != 1)
331 throw std::runtime_error(
332 "removeprunedfunds \"txid\"\n"
333 "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will effect wallet balances.\n"
335 "1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
337 + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
338 "\nAs a JSON-RPC call\n"
339 + HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
342 LOCK2(cs_main
, pwallet
->cs_wallet
);
345 hash
.SetHex(request
.params
[0].get_str());
346 std::vector
<uint256
> vHash
;
347 vHash
.push_back(hash
);
348 std::vector
<uint256
> vHashOut
;
350 if (pwallet
->ZapSelectTx(vHash
, vHashOut
) != DB_LOAD_OK
) {
351 throw JSONRPCError(RPC_WALLET_ERROR
, "Could not properly delete the transaction.");
354 if(vHashOut
.empty()) {
355 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Transaction does not exist in wallet.");
361 UniValue
importpubkey(const JSONRPCRequest
& request
)
363 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
364 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
368 if (request
.fHelp
|| request
.params
.size() < 1 || request
.params
.size() > 4)
369 throw std::runtime_error(
370 "importpubkey \"pubkey\" ( \"label\" rescan )\n"
371 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
373 "1. \"pubkey\" (string, required) The hex-encoded public key\n"
374 "2. \"label\" (string, optional, default=\"\") An optional label\n"
375 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
376 "\nNote: This call can take minutes to complete if rescan is true.\n"
378 "\nImport a public key with rescan\n"
379 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
380 "\nImport using a label without rescan\n"
381 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
382 "\nAs a JSON-RPC call\n"
383 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
387 std::string strLabel
= "";
388 if (request
.params
.size() > 1)
389 strLabel
= request
.params
[1].get_str();
391 // Whether to perform rescan after import
393 if (request
.params
.size() > 2)
394 fRescan
= request
.params
[2].get_bool();
396 if (fRescan
&& fPruneMode
)
397 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
399 if (!IsHex(request
.params
[0].get_str()))
400 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey must be a hex string");
401 std::vector
<unsigned char> data(ParseHex(request
.params
[0].get_str()));
402 CPubKey
pubKey(data
.begin(), data
.end());
403 if (!pubKey
.IsFullyValid())
404 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey is not a valid public key");
406 LOCK2(cs_main
, pwallet
->cs_wallet
);
408 ImportAddress(pwallet
, CBitcoinAddress(pubKey
.GetID()), strLabel
);
409 ImportScript(pwallet
, GetScriptForRawPubKey(pubKey
), strLabel
, false);
413 pwallet
->ScanForWalletTransactions(chainActive
.Genesis(), true);
414 pwallet
->ReacceptWalletTransactions();
421 UniValue
importwallet(const JSONRPCRequest
& request
)
423 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
424 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
428 if (request
.fHelp
|| request
.params
.size() != 1)
429 throw std::runtime_error(
430 "importwallet \"filename\"\n"
431 "\nImports keys from a wallet dump file (see dumpwallet).\n"
433 "1. \"filename\" (string, required) The wallet file\n"
435 "\nDump the wallet\n"
436 + HelpExampleCli("dumpwallet", "\"test\"") +
437 "\nImport the wallet\n"
438 + HelpExampleCli("importwallet", "\"test\"") +
439 "\nImport using the json rpc call\n"
440 + HelpExampleRpc("importwallet", "\"test\"")
444 throw JSONRPCError(RPC_WALLET_ERROR
, "Importing wallets is disabled in pruned mode");
446 LOCK2(cs_main
, pwallet
->cs_wallet
);
448 EnsureWalletIsUnlocked(pwallet
);
451 file
.open(request
.params
[0].get_str().c_str(), std::ios::in
| std::ios::ate
);
453 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
455 int64_t nTimeBegin
= chainActive
.Tip()->GetBlockTime();
459 int64_t nFilesize
= std::max((int64_t)1, (int64_t)file
.tellg());
460 file
.seekg(0, file
.beg
);
462 pwallet
->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
463 while (file
.good()) {
464 pwallet
->ShowProgress("", std::max(1, std::min(99, (int)(((double)file
.tellg() / (double)nFilesize
) * 100))));
466 std::getline(file
, line
);
467 if (line
.empty() || line
[0] == '#')
470 std::vector
<std::string
> vstr
;
471 boost::split(vstr
, line
, boost::is_any_of(" "));
474 CBitcoinSecret vchSecret
;
475 if (!vchSecret
.SetString(vstr
[0]))
477 CKey key
= vchSecret
.GetKey();
478 CPubKey pubkey
= key
.GetPubKey();
479 assert(key
.VerifyPubKey(pubkey
));
480 CKeyID keyid
= pubkey
.GetID();
481 if (pwallet
->HaveKey(keyid
)) {
482 LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid
).ToString());
485 int64_t nTime
= DecodeDumpTime(vstr
[1]);
486 std::string strLabel
;
488 for (unsigned int nStr
= 2; nStr
< vstr
.size(); nStr
++) {
489 if (boost::algorithm::starts_with(vstr
[nStr
], "#"))
491 if (vstr
[nStr
] == "change=1")
493 if (vstr
[nStr
] == "reserve=1")
495 if (boost::algorithm::starts_with(vstr
[nStr
], "label=")) {
496 strLabel
= DecodeDumpString(vstr
[nStr
].substr(6));
500 LogPrintf("Importing %s...\n", CBitcoinAddress(keyid
).ToString());
501 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
505 pwallet
->mapKeyMetadata
[keyid
].nCreateTime
= nTime
;
507 pwallet
->SetAddressBook(keyid
, strLabel
, "receive");
508 nTimeBegin
= std::min(nTimeBegin
, nTime
);
511 pwallet
->ShowProgress("", 100); // hide progress dialog in GUI
513 CBlockIndex
*pindex
= chainActive
.Tip();
514 while (pindex
&& pindex
->pprev
&& pindex
->GetBlockTime() > nTimeBegin
- TIMESTAMP_WINDOW
)
515 pindex
= pindex
->pprev
;
517 pwallet
->UpdateTimeFirstKey(nTimeBegin
);
519 LogPrintf("Rescanning last %i blocks\n", chainActive
.Height() - pindex
->nHeight
+ 1);
520 pwallet
->ScanForWalletTransactions(pindex
);
521 pwallet
->MarkDirty();
524 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding some keys to wallet");
529 UniValue
dumpprivkey(const JSONRPCRequest
& request
)
531 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
532 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
536 if (request
.fHelp
|| request
.params
.size() != 1)
537 throw std::runtime_error(
538 "dumpprivkey \"address\"\n"
539 "\nReveals the private key corresponding to 'address'.\n"
540 "Then the importprivkey can be used with this output\n"
542 "1. \"address\" (string, required) The bitcoin address for the private key\n"
544 "\"key\" (string) The private key\n"
546 + HelpExampleCli("dumpprivkey", "\"myaddress\"")
547 + HelpExampleCli("importprivkey", "\"mykey\"")
548 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
551 LOCK2(cs_main
, pwallet
->cs_wallet
);
553 EnsureWalletIsUnlocked(pwallet
);
555 std::string strAddress
= request
.params
[0].get_str();
556 CBitcoinAddress address
;
557 if (!address
.SetString(strAddress
))
558 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address");
560 if (!address
.GetKeyID(keyID
))
561 throw JSONRPCError(RPC_TYPE_ERROR
, "Address does not refer to a key");
563 if (!pwallet
->GetKey(keyID
, vchSecret
)) {
564 throw JSONRPCError(RPC_WALLET_ERROR
, "Private key for address " + strAddress
+ " is not known");
566 return CBitcoinSecret(vchSecret
).ToString();
570 UniValue
dumpwallet(const JSONRPCRequest
& request
)
572 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(request
);
573 if (!EnsureWalletIsAvailable(pwallet
, request
.fHelp
)) {
577 if (request
.fHelp
|| request
.params
.size() != 1)
578 throw std::runtime_error(
579 "dumpwallet \"filename\"\n"
580 "\nDumps all wallet keys in a human-readable format.\n"
582 "1. \"filename\" (string, required) The filename\n"
584 + HelpExampleCli("dumpwallet", "\"test\"")
585 + HelpExampleRpc("dumpwallet", "\"test\"")
588 LOCK2(cs_main
, pwallet
->cs_wallet
);
590 EnsureWalletIsUnlocked(pwallet
);
593 file
.open(request
.params
[0].get_str().c_str());
595 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
597 std::map
<CTxDestination
, int64_t> mapKeyBirth
;
598 std::set
<CKeyID
> setKeyPool
;
599 pwallet
->GetKeyBirthTimes(mapKeyBirth
);
600 pwallet
->GetAllReserveKeys(setKeyPool
);
602 // sort time/key pairs
603 std::vector
<std::pair
<int64_t, CKeyID
> > vKeyBirth
;
604 for (const auto& entry
: mapKeyBirth
) {
605 if (const CKeyID
* keyID
= boost::get
<CKeyID
>(&entry
.first
)) { // set and test
606 vKeyBirth
.push_back(std::make_pair(entry
.second
, *keyID
));
610 std::sort(vKeyBirth
.begin(), vKeyBirth
.end());
613 file
<< strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD
);
614 file
<< strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
615 file
<< strprintf("# * Best block at time of backup was %i (%s),\n", chainActive
.Height(), chainActive
.Tip()->GetBlockHash().ToString());
616 file
<< strprintf("# mined on %s\n", EncodeDumpTime(chainActive
.Tip()->GetBlockTime()));
619 // add the base58check encoded extended master if the wallet uses HD
620 CKeyID masterKeyID
= pwallet
->GetHDChain().masterKeyID
;
621 if (!masterKeyID
.IsNull())
624 if (pwallet
->GetKey(masterKeyID
, key
)) {
626 masterKey
.SetMaster(key
.begin(), key
.size());
628 CBitcoinExtKey b58extkey
;
629 b58extkey
.SetKey(masterKey
);
631 file
<< "# extended private masterkey: " << b58extkey
.ToString() << "\n\n";
634 for (std::vector
<std::pair
<int64_t, CKeyID
> >::const_iterator it
= vKeyBirth
.begin(); it
!= vKeyBirth
.end(); it
++) {
635 const CKeyID
&keyid
= it
->second
;
636 std::string strTime
= EncodeDumpTime(it
->first
);
637 std::string strAddr
= CBitcoinAddress(keyid
).ToString();
639 if (pwallet
->GetKey(keyid
, key
)) {
640 file
<< strprintf("%s %s ", CBitcoinSecret(key
).ToString(), strTime
);
641 if (pwallet
->mapAddressBook
.count(keyid
)) {
642 file
<< strprintf("label=%s", EncodeDumpString(pwallet
->mapAddressBook
[keyid
].name
));
643 } else if (keyid
== masterKeyID
) {
644 file
<< "hdmaster=1";
645 } else if (setKeyPool
.count(keyid
)) {
647 } else if (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
== "m") {
648 file
<< "inactivehdmaster=1";
652 file
<< strprintf(" # addr=%s%s\n", strAddr
, (pwallet
->mapKeyMetadata
[keyid
].hdKeypath
.size() > 0 ? " hdkeypath="+pwallet
->mapKeyMetadata
[keyid
].hdKeypath
: ""));
656 file
<< "# End of dump\n";
662 UniValue
ProcessImport(CWallet
* const pwallet
, const UniValue
& data
, const int64_t timestamp
)
665 bool success
= false;
668 const UniValue
& scriptPubKey
= data
["scriptPubKey"];
670 // Should have script or JSON with "address".
671 if (!(scriptPubKey
.getType() == UniValue::VOBJ
&& scriptPubKey
.exists("address")) && !(scriptPubKey
.getType() == UniValue::VSTR
)) {
672 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Invalid scriptPubKey");
676 const std::string
& strRedeemScript
= data
.exists("redeemscript") ? data
["redeemscript"].get_str() : "";
677 const UniValue
& pubKeys
= data
.exists("pubkeys") ? data
["pubkeys"].get_array() : UniValue();
678 const UniValue
& keys
= data
.exists("keys") ? data
["keys"].get_array() : UniValue();
679 const bool& internal
= data
.exists("internal") ? data
["internal"].get_bool() : false;
680 const bool& watchOnly
= data
.exists("watchonly") ? data
["watchonly"].get_bool() : false;
681 const std::string
& label
= data
.exists("label") && !internal
? data
["label"].get_str() : "";
683 bool isScript
= scriptPubKey
.getType() == UniValue::VSTR
;
684 bool isP2SH
= strRedeemScript
.length() > 0;
685 const std::string
& output
= isScript
? scriptPubKey
.get_str() : scriptPubKey
["address"].get_str();
689 CBitcoinAddress address
;
692 address
= CBitcoinAddress(output
);
693 if (!address
.IsValid()) {
694 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid address");
696 script
= GetScriptForDestination(address
.Get());
698 if (!IsHex(output
)) {
699 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid scriptPubKey");
702 std::vector
<unsigned char> vData(ParseHex(output
));
703 script
= CScript(vData
.begin(), vData
.end());
706 // Watchonly and private keys
707 if (watchOnly
&& keys
.size()) {
708 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Incompatibility found between watchonly and keys");
712 if (internal
&& data
.exists("label")) {
713 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Incompatibility found between internal and label");
716 // Not having Internal + Script
717 if (!internal
&& isScript
) {
718 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Internal must be set for hex scriptPubKey");
721 // Keys / PubKeys size check.
722 if (!isP2SH
&& (keys
.size() > 1 || pubKeys
.size() > 1)) { // Address / scriptPubKey
723 throw JSONRPCError(RPC_INVALID_PARAMETER
, "More than private key given for one address");
726 // Invalid P2SH redeemScript
727 if (isP2SH
&& !IsHex(strRedeemScript
)) {
728 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid redeem script");
735 // Import redeem script.
736 std::vector
<unsigned char> vData(ParseHex(strRedeemScript
));
737 CScript redeemScript
= CScript(vData
.begin(), vData
.end());
739 // Invalid P2SH address
740 if (!script
.IsPayToScriptHash()) {
741 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid P2SH address / script");
744 pwallet
->MarkDirty();
746 if (!pwallet
->AddWatchOnly(redeemScript
, timestamp
)) {
747 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
750 if (!pwallet
->HaveCScript(redeemScript
) && !pwallet
->AddCScript(redeemScript
)) {
751 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding p2sh redeemScript to wallet");
754 CBitcoinAddress redeemAddress
= CBitcoinAddress(CScriptID(redeemScript
));
755 CScript redeemDestination
= GetScriptForDestination(redeemAddress
.Get());
757 if (::IsMine(*pwallet
, redeemDestination
) == ISMINE_SPENDABLE
) {
758 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
761 pwallet
->MarkDirty();
763 if (!pwallet
->AddWatchOnly(redeemDestination
, timestamp
)) {
764 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
767 // add to address book or update label
768 if (address
.IsValid()) {
769 pwallet
->SetAddressBook(address
.Get(), label
, "receive");
772 // Import private keys.
774 for (size_t i
= 0; i
< keys
.size(); i
++) {
775 const std::string
& privkey
= keys
[i
].get_str();
777 CBitcoinSecret vchSecret
;
778 bool fGood
= vchSecret
.SetString(privkey
);
781 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
784 CKey key
= vchSecret
.GetKey();
786 if (!key
.IsValid()) {
787 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
790 CPubKey pubkey
= key
.GetPubKey();
791 assert(key
.VerifyPubKey(pubkey
));
793 CKeyID vchAddress
= pubkey
.GetID();
794 pwallet
->MarkDirty();
795 pwallet
->SetAddressBook(vchAddress
, label
, "receive");
797 if (pwallet
->HaveKey(vchAddress
)) {
798 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Already have this key");
801 pwallet
->mapKeyMetadata
[vchAddress
].nCreateTime
= timestamp
;
803 if (!pwallet
->AddKeyPubKey(key
, pubkey
)) {
804 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
807 pwallet
->UpdateTimeFirstKey(timestamp
);
813 // Import public keys.
814 if (pubKeys
.size() && keys
.size() == 0) {
815 const std::string
& strPubKey
= pubKeys
[0].get_str();
817 if (!IsHex(strPubKey
)) {
818 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey must be a hex string");
821 std::vector
<unsigned char> vData(ParseHex(strPubKey
));
822 CPubKey
pubKey(vData
.begin(), vData
.end());
824 if (!pubKey
.IsFullyValid()) {
825 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey is not a valid public key");
828 CBitcoinAddress pubKeyAddress
= CBitcoinAddress(pubKey
.GetID());
830 // Consistency check.
831 if (!isScript
&& !(pubKeyAddress
.Get() == address
.Get())) {
832 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
835 // Consistency check.
837 CBitcoinAddress scriptAddress
;
838 CTxDestination destination
;
840 if (ExtractDestination(script
, destination
)) {
841 scriptAddress
= CBitcoinAddress(destination
);
842 if (!(scriptAddress
.Get() == pubKeyAddress
.Get())) {
843 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
848 CScript pubKeyScript
= GetScriptForDestination(pubKeyAddress
.Get());
850 if (::IsMine(*pwallet
, pubKeyScript
) == ISMINE_SPENDABLE
) {
851 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
854 pwallet
->MarkDirty();
856 if (!pwallet
->AddWatchOnly(pubKeyScript
, timestamp
)) {
857 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
860 // add to address book or update label
861 if (pubKeyAddress
.IsValid()) {
862 pwallet
->SetAddressBook(pubKeyAddress
.Get(), label
, "receive");
865 // TODO Is this necessary?
866 CScript scriptRawPubKey
= GetScriptForRawPubKey(pubKey
);
868 if (::IsMine(*pwallet
, scriptRawPubKey
) == ISMINE_SPENDABLE
) {
869 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
872 pwallet
->MarkDirty();
874 if (!pwallet
->AddWatchOnly(scriptRawPubKey
, timestamp
)) {
875 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
881 // Import private keys.
883 const std::string
& strPrivkey
= keys
[0].get_str();
886 CBitcoinSecret vchSecret
;
887 bool fGood
= vchSecret
.SetString(strPrivkey
);
890 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
893 CKey key
= vchSecret
.GetKey();
894 if (!key
.IsValid()) {
895 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
898 CPubKey pubKey
= key
.GetPubKey();
899 assert(key
.VerifyPubKey(pubKey
));
901 CBitcoinAddress pubKeyAddress
= CBitcoinAddress(pubKey
.GetID());
903 // Consistency check.
904 if (!isScript
&& !(pubKeyAddress
.Get() == address
.Get())) {
905 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
908 // Consistency check.
910 CBitcoinAddress scriptAddress
;
911 CTxDestination destination
;
913 if (ExtractDestination(script
, destination
)) {
914 scriptAddress
= CBitcoinAddress(destination
);
915 if (!(scriptAddress
.Get() == pubKeyAddress
.Get())) {
916 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Consistency check failed");
921 CKeyID vchAddress
= pubKey
.GetID();
922 pwallet
->MarkDirty();
923 pwallet
->SetAddressBook(vchAddress
, label
, "receive");
925 if (pwallet
->HaveKey(vchAddress
)) {
929 pwallet
->mapKeyMetadata
[vchAddress
].nCreateTime
= timestamp
;
931 if (!pwallet
->AddKeyPubKey(key
, pubKey
)) {
932 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
935 pwallet
->UpdateTimeFirstKey(timestamp
);
940 // Import scriptPubKey only.
941 if (pubKeys
.size() == 0 && keys
.size() == 0) {
942 if (::IsMine(*pwallet
, script
) == ISMINE_SPENDABLE
) {
943 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
946 pwallet
->MarkDirty();
948 if (!pwallet
->AddWatchOnly(script
, timestamp
)) {
949 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
952 if (scriptPubKey
.getType() == UniValue::VOBJ
) {
953 // add to address book or update label
954 if (address
.IsValid()) {
955 pwallet
->SetAddressBook(address
.Get(), label
, "receive");
963 UniValue result
= UniValue(UniValue::VOBJ
);
964 result
.pushKV("success", UniValue(success
));
966 } catch (const UniValue
& e
) {
967 UniValue result
= UniValue(UniValue::VOBJ
);
968 result
.pushKV("success", UniValue(false));
969 result
.pushKV("error", e
);
972 UniValue result
= UniValue(UniValue::VOBJ
);
973 result
.pushKV("success", UniValue(false));
974 result
.pushKV("error", JSONRPCError(RPC_MISC_ERROR
, "Missing required fields"));
979 int64_t GetImportTimestamp(const UniValue
& data
, int64_t now
)
981 if (data
.exists("timestamp")) {
982 const UniValue
& timestamp
= data
["timestamp"];
983 if (timestamp
.isNum()) {
984 return timestamp
.get_int64();
985 } else if (timestamp
.isStr() && timestamp
.get_str() == "now") {
988 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp
.type())));
990 throw JSONRPCError(RPC_TYPE_ERROR
, "Missing required timestamp field for key");
993 UniValue
importmulti(const JSONRPCRequest
& mainRequest
)
995 CWallet
* const pwallet
= GetWalletForJSONRPCRequest(mainRequest
);
996 if (!EnsureWalletIsAvailable(pwallet
, mainRequest
.fHelp
)) {
1001 if (mainRequest
.fHelp
|| mainRequest
.params
.size() < 1 || mainRequest
.params
.size() > 2)
1002 throw std::runtime_error(
1003 "importmulti \"requests\" \"options\"\n\n"
1004 "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"
1006 "1. requests (array, required) Data to be imported\n"
1007 " [ (array of json objects)\n"
1009 " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
1010 " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
1011 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1012 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1013 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1014 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1015 " creation time of all keys being imported by the importmulti call will be scanned.\n"
1016 " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
1017 " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
1018 " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
1019 " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n"
1020 " \"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"
1021 " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
1025 "2. options (json, optional)\n"
1027 " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
1030 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1031 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1032 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
1034 "\nResponse is an array with the same size as the input that has the execution result :\n"
1035 " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
1039 RPCTypeCheck(mainRequest
.params
, boost::assign::list_of(UniValue::VARR
)(UniValue::VOBJ
));
1041 const UniValue
& requests
= mainRequest
.params
[0];
1044 bool fRescan
= true;
1046 if (mainRequest
.params
.size() > 1) {
1047 const UniValue
& options
= mainRequest
.params
[1];
1049 if (options
.exists("rescan")) {
1050 fRescan
= options
["rescan"].get_bool();
1054 LOCK2(cs_main
, pwallet
->cs_wallet
);
1055 EnsureWalletIsUnlocked(pwallet
);
1057 // Verify all timestamps are present before importing any keys.
1058 const int64_t now
= chainActive
.Tip() ? chainActive
.Tip()->GetMedianTimePast() : 0;
1059 for (const UniValue
& data
: requests
.getValues()) {
1060 GetImportTimestamp(data
, now
);
1063 bool fRunScan
= false;
1064 const int64_t minimumTimestamp
= 1;
1065 int64_t nLowestTimestamp
= 0;
1067 if (fRescan
&& chainActive
.Tip()) {
1068 nLowestTimestamp
= chainActive
.Tip()->GetBlockTime();
1073 UniValue
response(UniValue::VARR
);
1075 BOOST_FOREACH (const UniValue
& data
, requests
.getValues()) {
1076 const int64_t timestamp
= std::max(GetImportTimestamp(data
, now
), minimumTimestamp
);
1077 const UniValue result
= ProcessImport(pwallet
, data
, timestamp
);
1078 response
.push_back(result
);
1084 // If at least one request was successful then allow rescan.
1085 if (result
["success"].get_bool()) {
1089 // Get the lowest timestamp.
1090 if (timestamp
< nLowestTimestamp
) {
1091 nLowestTimestamp
= timestamp
;
1095 if (fRescan
&& fRunScan
&& requests
.size()) {
1096 CBlockIndex
* pindex
= nLowestTimestamp
> minimumTimestamp
? chainActive
.FindEarliestAtLeast(std::max
<int64_t>(nLowestTimestamp
- TIMESTAMP_WINDOW
, 0)) : chainActive
.Genesis();
1097 CBlockIndex
* scannedRange
= nullptr;
1099 scannedRange
= pwallet
->ScanForWalletTransactions(pindex
, true);
1100 pwallet
->ReacceptWalletTransactions();
1103 if (!scannedRange
|| scannedRange
->nHeight
> pindex
->nHeight
) {
1104 std::vector
<UniValue
> results
= response
.getValues();
1106 response
.setArray();
1108 for (const UniValue
& request
: requests
.getValues()) {
1109 // If key creation date is within the successfully scanned
1110 // range, or if the import result already has an error set, let
1111 // the result stand unmodified. Otherwise replace the result
1112 // with an error message.
1113 if (GetImportTimestamp(request
, now
) - TIMESTAMP_WINDOW
>= scannedRange
->GetBlockTimeMax() || results
.at(i
).exists("error")) {
1114 response
.push_back(results
.at(i
));
1116 UniValue result
= UniValue(UniValue::VOBJ
);
1117 result
.pushKV("success", UniValue(false));
1118 result
.pushKV("error", JSONRPCError(RPC_MISC_ERROR
, strprintf("Failed to rescan before time %d, transactions may be missing.", scannedRange
->GetBlockTimeMax())));
1119 response
.push_back(std::move(result
));