1 // Copyright (c) 2009-2015 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.
10 #include "script/script.h"
11 #include "script/standard.h"
20 #include <boost/algorithm/string.hpp>
21 #include <boost/date_time/posix_time/posix_time.hpp>
25 #include <boost/foreach.hpp>
29 void EnsureWalletIsUnlocked();
30 bool EnsureWalletIsAvailable(bool avoidException
);
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 UniValue
& params
, bool fHelp
)
77 if (!EnsureWalletIsAvailable(fHelp
))
80 if (fHelp
|| params
.size() < 1 || params
.size() > 3)
82 "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
83 "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
85 "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
86 "2. \"label\" (string, optional, default=\"\") An optional label\n"
87 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
88 "\nNote: This call can take minutes to complete if rescan is true.\n"
90 "\nDump a private key\n"
91 + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
92 "\nImport the private key with rescan\n"
93 + HelpExampleCli("importprivkey", "\"mykey\"") +
94 "\nImport using a label and without rescan\n"
95 + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
96 "\nAs a JSON-RPC call\n"
97 + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
101 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
103 EnsureWalletIsUnlocked();
105 string strSecret
= params
[0].get_str();
106 string strLabel
= "";
107 if (params
.size() > 1)
108 strLabel
= params
[1].get_str();
110 // Whether to perform rescan after import
112 if (params
.size() > 2)
113 fRescan
= params
[2].get_bool();
115 if (fRescan
&& fPruneMode
)
116 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
118 CBitcoinSecret vchSecret
;
119 bool fGood
= vchSecret
.SetString(strSecret
);
121 if (!fGood
) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid private key encoding");
123 CKey key
= vchSecret
.GetKey();
124 if (!key
.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Private key outside allowed range");
126 CPubKey pubkey
= key
.GetPubKey();
127 assert(key
.VerifyPubKey(pubkey
));
128 CKeyID vchAddress
= pubkey
.GetID();
130 pwalletMain
->MarkDirty();
131 pwalletMain
->SetAddressBook(vchAddress
, strLabel
, "receive");
133 // Don't throw error in case a key is already there
134 if (pwalletMain
->HaveKey(vchAddress
))
137 pwalletMain
->mapKeyMetadata
[vchAddress
].nCreateTime
= 1;
139 if (!pwalletMain
->AddKeyPubKey(key
, pubkey
))
140 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding key to wallet");
142 // whenever a key is imported, we need to scan the whole chain
143 pwalletMain
->nTimeFirstKey
= 1; // 0 would be considered 'no value'
146 pwalletMain
->ScanForWalletTransactions(chainActive
.Genesis(), true);
153 void ImportAddress(const CBitcoinAddress
& address
, const string
& strLabel
);
154 void ImportScript(const CScript
& script
, const string
& strLabel
, bool isRedeemScript
)
156 if (!isRedeemScript
&& ::IsMine(*pwalletMain
, script
) == ISMINE_SPENDABLE
)
157 throw JSONRPCError(RPC_WALLET_ERROR
, "The wallet already contains the private key for this address or script");
159 pwalletMain
->MarkDirty();
161 if (!pwalletMain
->HaveWatchOnly(script
) && !pwalletMain
->AddWatchOnly(script
))
162 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding address to wallet");
164 if (isRedeemScript
) {
165 if (!pwalletMain
->HaveCScript(script
) && !pwalletMain
->AddCScript(script
))
166 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding p2sh redeemScript to wallet");
167 ImportAddress(CBitcoinAddress(CScriptID(script
)), strLabel
);
171 void ImportAddress(const CBitcoinAddress
& address
, const string
& strLabel
)
173 CScript script
= GetScriptForDestination(address
.Get());
174 ImportScript(script
, strLabel
, false);
175 // add to address book or update label
176 if (address
.IsValid())
177 pwalletMain
->SetAddressBook(address
.Get(), strLabel
, "receive");
180 UniValue
importaddress(const UniValue
& params
, bool fHelp
)
182 if (!EnsureWalletIsAvailable(fHelp
))
185 if (fHelp
|| params
.size() < 1 || params
.size() > 4)
187 "importaddress \"address\" ( \"label\" rescan p2sh )\n"
188 "\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"
190 "1. \"script\" (string, required) The hex-encoded script (or address)\n"
191 "2. \"label\" (string, optional, default=\"\") An optional label\n"
192 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
193 "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
194 "\nNote: This call can take minutes to complete if rescan is true.\n"
195 "If you have the full public key, you should call importpublickey instead of this.\n"
197 "\nImport a script with rescan\n"
198 + HelpExampleCli("importaddress", "\"myscript\"") +
199 "\nImport using a label without rescan\n"
200 + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
201 "\nAs a JSON-RPC call\n"
202 + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")
206 string strLabel
= "";
207 if (params
.size() > 1)
208 strLabel
= params
[1].get_str();
210 // Whether to perform rescan after import
212 if (params
.size() > 2)
213 fRescan
= params
[2].get_bool();
215 if (fRescan
&& fPruneMode
)
216 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
218 // Whether to import a p2sh version, too
220 if (params
.size() > 3)
221 fP2SH
= params
[3].get_bool();
223 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
225 CBitcoinAddress
address(params
[0].get_str());
226 if (address
.IsValid()) {
228 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Cannot use the p2sh flag with an address - use a script instead");
229 ImportAddress(address
, strLabel
);
230 } else if (IsHex(params
[0].get_str())) {
231 std::vector
<unsigned char> data(ParseHex(params
[0].get_str()));
232 ImportScript(CScript(data
.begin(), data
.end()), strLabel
, fP2SH
);
234 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address or script");
239 pwalletMain
->ScanForWalletTransactions(chainActive
.Genesis(), true);
240 pwalletMain
->ReacceptWalletTransactions();
246 UniValue
importpubkey(const UniValue
& params
, bool fHelp
)
248 if (!EnsureWalletIsAvailable(fHelp
))
251 if (fHelp
|| params
.size() < 1 || params
.size() > 4)
253 "importpubkey \"pubkey\" ( \"label\" rescan )\n"
254 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
256 "1. \"pubkey\" (string, required) The hex-encoded public key\n"
257 "2. \"label\" (string, optional, default=\"\") An optional label\n"
258 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
259 "\nNote: This call can take minutes to complete if rescan is true.\n"
261 "\nImport a public key with rescan\n"
262 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
263 "\nImport using a label without rescan\n"
264 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
265 "\nAs a JSON-RPC call\n"
266 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
270 string strLabel
= "";
271 if (params
.size() > 1)
272 strLabel
= params
[1].get_str();
274 // Whether to perform rescan after import
276 if (params
.size() > 2)
277 fRescan
= params
[2].get_bool();
279 if (fRescan
&& fPruneMode
)
280 throw JSONRPCError(RPC_WALLET_ERROR
, "Rescan is disabled in pruned mode");
282 if (!IsHex(params
[0].get_str()))
283 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey must be a hex string");
284 std::vector
<unsigned char> data(ParseHex(params
[0].get_str()));
285 CPubKey
pubKey(data
.begin(), data
.end());
286 if (!pubKey
.IsFullyValid())
287 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Pubkey is not a valid public key");
289 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
291 ImportAddress(CBitcoinAddress(pubKey
.GetID()), strLabel
);
292 ImportScript(GetScriptForRawPubKey(pubKey
), strLabel
, false);
296 pwalletMain
->ScanForWalletTransactions(chainActive
.Genesis(), true);
297 pwalletMain
->ReacceptWalletTransactions();
304 UniValue
importwallet(const UniValue
& params
, bool fHelp
)
306 if (!EnsureWalletIsAvailable(fHelp
))
309 if (fHelp
|| params
.size() != 1)
311 "importwallet \"filename\"\n"
312 "\nImports keys from a wallet dump file (see dumpwallet).\n"
314 "1. \"filename\" (string, required) The wallet file\n"
316 "\nDump the wallet\n"
317 + HelpExampleCli("dumpwallet", "\"test\"") +
318 "\nImport the wallet\n"
319 + HelpExampleCli("importwallet", "\"test\"") +
320 "\nImport using the json rpc call\n"
321 + HelpExampleRpc("importwallet", "\"test\"")
325 throw JSONRPCError(RPC_WALLET_ERROR
, "Importing wallets is disabled in pruned mode");
327 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
329 EnsureWalletIsUnlocked();
332 file
.open(params
[0].get_str().c_str(), std::ios::in
| std::ios::ate
);
334 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
336 int64_t nTimeBegin
= chainActive
.Tip()->GetBlockTime();
340 int64_t nFilesize
= std::max((int64_t)1, (int64_t)file
.tellg());
341 file
.seekg(0, file
.beg
);
343 pwalletMain
->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
344 while (file
.good()) {
345 pwalletMain
->ShowProgress("", std::max(1, std::min(99, (int)(((double)file
.tellg() / (double)nFilesize
) * 100))));
347 std::getline(file
, line
);
348 if (line
.empty() || line
[0] == '#')
351 std::vector
<std::string
> vstr
;
352 boost::split(vstr
, line
, boost::is_any_of(" "));
355 CBitcoinSecret vchSecret
;
356 if (!vchSecret
.SetString(vstr
[0]))
358 CKey key
= vchSecret
.GetKey();
359 CPubKey pubkey
= key
.GetPubKey();
360 assert(key
.VerifyPubKey(pubkey
));
361 CKeyID keyid
= pubkey
.GetID();
362 if (pwalletMain
->HaveKey(keyid
)) {
363 LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid
).ToString());
366 int64_t nTime
= DecodeDumpTime(vstr
[1]);
367 std::string strLabel
;
369 for (unsigned int nStr
= 2; nStr
< vstr
.size(); nStr
++) {
370 if (boost::algorithm::starts_with(vstr
[nStr
], "#"))
372 if (vstr
[nStr
] == "change=1")
374 if (vstr
[nStr
] == "reserve=1")
376 if (boost::algorithm::starts_with(vstr
[nStr
], "label=")) {
377 strLabel
= DecodeDumpString(vstr
[nStr
].substr(6));
381 LogPrintf("Importing %s...\n", CBitcoinAddress(keyid
).ToString());
382 if (!pwalletMain
->AddKeyPubKey(key
, pubkey
)) {
386 pwalletMain
->mapKeyMetadata
[keyid
].nCreateTime
= nTime
;
388 pwalletMain
->SetAddressBook(keyid
, strLabel
, "receive");
389 nTimeBegin
= std::min(nTimeBegin
, nTime
);
392 pwalletMain
->ShowProgress("", 100); // hide progress dialog in GUI
394 CBlockIndex
*pindex
= chainActive
.Tip();
395 while (pindex
&& pindex
->pprev
&& pindex
->GetBlockTime() > nTimeBegin
- 7200)
396 pindex
= pindex
->pprev
;
398 if (!pwalletMain
->nTimeFirstKey
|| nTimeBegin
< pwalletMain
->nTimeFirstKey
)
399 pwalletMain
->nTimeFirstKey
= nTimeBegin
;
401 LogPrintf("Rescanning last %i blocks\n", chainActive
.Height() - pindex
->nHeight
+ 1);
402 pwalletMain
->ScanForWalletTransactions(pindex
);
403 pwalletMain
->MarkDirty();
406 throw JSONRPCError(RPC_WALLET_ERROR
, "Error adding some keys to wallet");
411 UniValue
dumpprivkey(const UniValue
& params
, bool fHelp
)
413 if (!EnsureWalletIsAvailable(fHelp
))
416 if (fHelp
|| params
.size() != 1)
418 "dumpprivkey \"bitcoinaddress\"\n"
419 "\nReveals the private key corresponding to 'bitcoinaddress'.\n"
420 "Then the importprivkey can be used with this output\n"
422 "1. \"bitcoinaddress\" (string, required) The bitcoin address for the private key\n"
424 "\"key\" (string) The private key\n"
426 + HelpExampleCli("dumpprivkey", "\"myaddress\"")
427 + HelpExampleCli("importprivkey", "\"mykey\"")
428 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
431 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
433 EnsureWalletIsUnlocked();
435 string strAddress
= params
[0].get_str();
436 CBitcoinAddress address
;
437 if (!address
.SetString(strAddress
))
438 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY
, "Invalid Bitcoin address");
440 if (!address
.GetKeyID(keyID
))
441 throw JSONRPCError(RPC_TYPE_ERROR
, "Address does not refer to a key");
443 if (!pwalletMain
->GetKey(keyID
, vchSecret
))
444 throw JSONRPCError(RPC_WALLET_ERROR
, "Private key for address " + strAddress
+ " is not known");
445 return CBitcoinSecret(vchSecret
).ToString();
449 UniValue
dumpwallet(const UniValue
& params
, bool fHelp
)
451 if (!EnsureWalletIsAvailable(fHelp
))
454 if (fHelp
|| params
.size() != 1)
456 "dumpwallet \"filename\"\n"
457 "\nDumps all wallet keys in a human-readable format.\n"
459 "1. \"filename\" (string, required) The filename\n"
461 + HelpExampleCli("dumpwallet", "\"test\"")
462 + HelpExampleRpc("dumpwallet", "\"test\"")
465 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
467 EnsureWalletIsUnlocked();
470 file
.open(params
[0].get_str().c_str());
472 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Cannot open wallet dump file");
474 std::map
<CKeyID
, int64_t> mapKeyBirth
;
475 std::set
<CKeyID
> setKeyPool
;
476 pwalletMain
->GetKeyBirthTimes(mapKeyBirth
);
477 pwalletMain
->GetAllReserveKeys(setKeyPool
);
479 // sort time/key pairs
480 std::vector
<std::pair
<int64_t, CKeyID
> > vKeyBirth
;
481 for (std::map
<CKeyID
, int64_t>::const_iterator it
= mapKeyBirth
.begin(); it
!= mapKeyBirth
.end(); it
++) {
482 vKeyBirth
.push_back(std::make_pair(it
->second
, it
->first
));
485 std::sort(vKeyBirth
.begin(), vKeyBirth
.end());
488 file
<< strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD
, CLIENT_DATE
);
489 file
<< strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
490 file
<< strprintf("# * Best block at time of backup was %i (%s),\n", chainActive
.Height(), chainActive
.Tip()->GetBlockHash().ToString());
491 file
<< strprintf("# mined on %s\n", EncodeDumpTime(chainActive
.Tip()->GetBlockTime()));
493 for (std::vector
<std::pair
<int64_t, CKeyID
> >::const_iterator it
= vKeyBirth
.begin(); it
!= vKeyBirth
.end(); it
++) {
494 const CKeyID
&keyid
= it
->second
;
495 std::string strTime
= EncodeDumpTime(it
->first
);
496 std::string strAddr
= CBitcoinAddress(keyid
).ToString();
498 if (pwalletMain
->GetKey(keyid
, key
)) {
499 if (pwalletMain
->mapAddressBook
.count(keyid
)) {
500 file
<< strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key
).ToString(), strTime
, EncodeDumpString(pwalletMain
->mapAddressBook
[keyid
].name
), strAddr
);
501 } else if (setKeyPool
.count(keyid
)) {
502 file
<< strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key
).ToString(), strTime
, strAddr
);
504 file
<< strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key
).ToString(), strTime
, strAddr
);
509 file
<< "# End of dump\n";