Add new rpc call: abandontransaction
[bitcoinplatinum.git] / src / wallet / rpcdump.cpp
blobb025c37459fc331d3b8a1b809d9ad8ec3471d977
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.
5 #include "base58.h"
6 #include "chain.h"
7 #include "rpcserver.h"
8 #include "init.h"
9 #include "main.h"
10 #include "script/script.h"
11 #include "script/standard.h"
12 #include "sync.h"
13 #include "util.h"
14 #include "utiltime.h"
15 #include "wallet.h"
17 #include <fstream>
18 #include <stdint.h>
20 #include <boost/algorithm/string.hpp>
21 #include <boost/date_time/posix_time/posix_time.hpp>
23 #include <univalue.h>
25 #include <boost/foreach.hpp>
27 using namespace std;
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);
41 iss.imbue(loc);
42 boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
43 iss >> ptime;
44 if (ptime.is_not_a_date_time())
45 return 0;
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);
54 } else {
55 ret << c;
58 return ret.str();
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));
68 pos += 2;
70 ret << c;
72 return ret.str();
75 UniValue importprivkey(const UniValue& params, bool fHelp)
77 if (!EnsureWalletIsAvailable(fHelp))
78 return NullUniValue;
80 if (fHelp || params.size() < 1 || params.size() > 3)
81 throw runtime_error(
82 "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
83 "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
84 "\nArguments:\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"
89 "\nExamples:\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
111 bool fRescan = true;
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))
135 return NullUniValue;
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'
145 if (fRescan) {
146 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
150 return NullUniValue;
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))
183 return NullUniValue;
185 if (fHelp || params.size() < 1 || params.size() > 4)
186 throw runtime_error(
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"
189 "\nArguments:\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"
196 "\nExamples:\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
211 bool fRescan = true;
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
219 bool fP2SH = false;
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()) {
227 if (fP2SH)
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);
233 } else {
234 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
237 if (fRescan)
239 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
240 pwalletMain->ReacceptWalletTransactions();
243 return NullUniValue;
246 UniValue importpubkey(const UniValue& params, bool fHelp)
248 if (!EnsureWalletIsAvailable(fHelp))
249 return NullUniValue;
251 if (fHelp || params.size() < 1 || params.size() > 4)
252 throw runtime_error(
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"
255 "\nArguments:\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"
260 "\nExamples:\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
275 bool fRescan = true;
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);
294 if (fRescan)
296 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
297 pwalletMain->ReacceptWalletTransactions();
300 return NullUniValue;
304 UniValue importwallet(const UniValue& params, bool fHelp)
306 if (!EnsureWalletIsAvailable(fHelp))
307 return NullUniValue;
309 if (fHelp || params.size() != 1)
310 throw runtime_error(
311 "importwallet \"filename\"\n"
312 "\nImports keys from a wallet dump file (see dumpwallet).\n"
313 "\nArguments:\n"
314 "1. \"filename\" (string, required) The wallet file\n"
315 "\nExamples:\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\"")
324 if (fPruneMode)
325 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
327 LOCK2(cs_main, pwalletMain->cs_wallet);
329 EnsureWalletIsUnlocked();
331 ifstream file;
332 file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate);
333 if (!file.is_open())
334 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
336 int64_t nTimeBegin = chainActive.Tip()->GetBlockTime();
338 bool fGood = true;
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))));
346 std::string line;
347 std::getline(file, line);
348 if (line.empty() || line[0] == '#')
349 continue;
351 std::vector<std::string> vstr;
352 boost::split(vstr, line, boost::is_any_of(" "));
353 if (vstr.size() < 2)
354 continue;
355 CBitcoinSecret vchSecret;
356 if (!vchSecret.SetString(vstr[0]))
357 continue;
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());
364 continue;
366 int64_t nTime = DecodeDumpTime(vstr[1]);
367 std::string strLabel;
368 bool fLabel = true;
369 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
370 if (boost::algorithm::starts_with(vstr[nStr], "#"))
371 break;
372 if (vstr[nStr] == "change=1")
373 fLabel = false;
374 if (vstr[nStr] == "reserve=1")
375 fLabel = false;
376 if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
377 strLabel = DecodeDumpString(vstr[nStr].substr(6));
378 fLabel = true;
381 LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
382 if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
383 fGood = false;
384 continue;
386 pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
387 if (fLabel)
388 pwalletMain->SetAddressBook(keyid, strLabel, "receive");
389 nTimeBegin = std::min(nTimeBegin, nTime);
391 file.close();
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();
405 if (!fGood)
406 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
408 return NullUniValue;
411 UniValue dumpprivkey(const UniValue& params, bool fHelp)
413 if (!EnsureWalletIsAvailable(fHelp))
414 return NullUniValue;
416 if (fHelp || params.size() != 1)
417 throw runtime_error(
418 "dumpprivkey \"bitcoinaddress\"\n"
419 "\nReveals the private key corresponding to 'bitcoinaddress'.\n"
420 "Then the importprivkey can be used with this output\n"
421 "\nArguments:\n"
422 "1. \"bitcoinaddress\" (string, required) The bitcoin address for the private key\n"
423 "\nResult:\n"
424 "\"key\" (string) The private key\n"
425 "\nExamples:\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");
439 CKeyID keyID;
440 if (!address.GetKeyID(keyID))
441 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
442 CKey vchSecret;
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))
452 return NullUniValue;
454 if (fHelp || params.size() != 1)
455 throw runtime_error(
456 "dumpwallet \"filename\"\n"
457 "\nDumps all wallet keys in a human-readable format.\n"
458 "\nArguments:\n"
459 "1. \"filename\" (string, required) The filename\n"
460 "\nExamples:\n"
461 + HelpExampleCli("dumpwallet", "\"test\"")
462 + HelpExampleRpc("dumpwallet", "\"test\"")
465 LOCK2(cs_main, pwalletMain->cs_wallet);
467 EnsureWalletIsUnlocked();
469 ofstream file;
470 file.open(params[0].get_str().c_str());
471 if (!file.is_open())
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));
484 mapKeyBirth.clear();
485 std::sort(vKeyBirth.begin(), vKeyBirth.end());
487 // produce output
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()));
492 file << "\n";
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();
497 CKey key;
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);
503 } else {
504 file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
508 file << "\n";
509 file << "# End of dump\n";
510 file.close();
511 return NullUniValue;