1 // Copyright (c) 2009-2014 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.
9 #include "script/script.h"
10 #include "script/standard.h"
19 #include <boost/algorithm/string.hpp>
20 #include <boost/date_time/posix_time/posix_time.hpp>
22 #include "json/json_spirit_value.h"
24 using namespace json_spirit;
27 void EnsureWalletIsUnlocked();
28 bool EnsureWalletIsAvailable(bool avoidException);
30 Value dumpwallet_impl(const Array& params, bool fHelp, bool fDumpZKeys);
31 Value importwallet_impl(const Array& params, bool fHelp, bool fImportZKeys);
34 std::string static EncodeDumpTime(int64_t nTime) {
35 return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
38 int64_t static DecodeDumpTime(const std::string &str) {
39 static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
40 static const std::locale loc(std::locale::classic(),
41 new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
42 std::istringstream iss(str);
44 boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
46 if (ptime.is_not_a_date_time())
48 return (ptime - epoch).total_seconds();
51 std::string static EncodeDumpString(const std::string &str) {
52 std::stringstream ret;
53 BOOST_FOREACH(unsigned char c, str) {
54 if (c <= 32 || c >= 128 || c == '%') {
55 ret << '%' << HexStr(&c, &c + 1);
63 std::string DecodeDumpString(const std::string &str) {
64 std::stringstream ret;
65 for (unsigned int pos = 0; pos < str.length(); pos++) {
66 unsigned char c = str[pos];
67 if (c == '%' && pos+2 < str.length()) {
68 c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
69 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
77 Value importprivkey(const Array& params, bool fHelp)
79 if (!EnsureWalletIsAvailable(fHelp))
82 if (fHelp || params.size() < 1 || params.size() > 3)
84 "importprivkey \"zcashprivkey\" ( \"label\" rescan )\n"
85 "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
87 "1. \"zcashprivkey\" (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")
102 LOCK2(cs_main, pwalletMain->cs_wallet);
104 EnsureWalletIsUnlocked();
106 string strSecret = params[0].get_str();
107 string strLabel = "";
108 if (params.size() > 1)
109 strLabel = params[1].get_str();
111 // Whether to perform rescan after import
113 if (params.size() > 2)
114 fRescan = params[2].get_bool();
116 CBitcoinSecret vchSecret;
117 bool fGood = vchSecret.SetString(strSecret);
119 if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
121 CKey key = vchSecret.GetKey();
122 if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
124 CPubKey pubkey = key.GetPubKey();
125 assert(key.VerifyPubKey(pubkey));
126 CKeyID vchAddress = pubkey.GetID();
128 pwalletMain->MarkDirty();
129 pwalletMain->SetAddressBook(vchAddress, strLabel, "receive");
131 // Don't throw error in case a key is already there
132 if (pwalletMain->HaveKey(vchAddress))
135 pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;
137 if (!pwalletMain->AddKeyPubKey(key, pubkey))
138 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
140 // whenever a key is imported, we need to scan the whole chain
141 pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
144 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
151 Value importaddress(const Array& params, bool fHelp)
153 if (!EnsureWalletIsAvailable(fHelp))
156 if (fHelp || params.size() < 1 || params.size() > 3)
158 "importaddress \"address\" ( \"label\" rescan )\n"
159 "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
161 "1. \"address\" (string, required) The address\n"
162 "2. \"label\" (string, optional, default=\"\") An optional label\n"
163 "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
164 "\nNote: This call can take minutes to complete if rescan is true.\n"
166 "\nImport an address with rescan\n"
167 + HelpExampleCli("importaddress", "\"myaddress\"") +
168 "\nImport using a label without rescan\n"
169 + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
170 "\nAs a JSON-RPC call\n"
171 + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
174 LOCK2(cs_main, pwalletMain->cs_wallet);
178 CBitcoinAddress address(params[0].get_str());
179 if (address.IsValid()) {
180 script = GetScriptForDestination(address.Get());
181 } else if (IsHex(params[0].get_str())) {
182 std::vector<unsigned char> data(ParseHex(params[0].get_str()));
183 script = CScript(data.begin(), data.end());
185 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Komodo address or script");
188 string strLabel = "";
189 if (params.size() > 1)
190 strLabel = params[1].get_str();
192 // Whether to perform rescan after import
194 if (params.size() > 2)
195 fRescan = params[2].get_bool();
198 if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE)
199 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
201 // add to address book or update label
202 if (address.IsValid())
203 pwalletMain->SetAddressBook(address.Get(), strLabel, "receive");
205 // Don't throw error in case an address is already there
206 if (pwalletMain->HaveWatchOnly(script))
209 pwalletMain->MarkDirty();
211 if (!pwalletMain->AddWatchOnly(script))
212 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
216 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
217 pwalletMain->ReacceptWalletTransactions();
224 Value z_importwallet(const Array& params, bool fHelp)
226 if (!EnsureWalletIsAvailable(fHelp))
229 if (fHelp || params.size() != 1)
231 "z_importwallet \"filename\"\n"
232 "\nImports taddr and zaddr keys from a wallet export file (see z_exportwallet).\n"
234 "1. \"filename\" (string, required) The wallet file\n"
236 "\nDump the wallet\n"
237 + HelpExampleCli("z_exportwallet", "\"test\"") +
238 "\nImport the wallet\n"
239 + HelpExampleCli("z_importwallet", "\"test\"") +
240 "\nImport using the json rpc call\n"
241 + HelpExampleRpc("z_importwallet", "\"test\"")
244 return importwallet_impl(params, fHelp, true);
247 Value importwallet(const Array& params, bool fHelp)
249 if (!EnsureWalletIsAvailable(fHelp))
252 if (fHelp || params.size() != 1)
254 "importwallet \"filename\"\n"
255 "\nImports taddr keys from a wallet dump file (see dumpwallet).\n"
257 "1. \"filename\" (string, required) The wallet file\n"
259 "\nDump the wallet\n"
260 + HelpExampleCli("dumpwallet", "\"test\"") +
261 "\nImport the wallet\n"
262 + HelpExampleCli("importwallet", "\"test\"") +
263 "\nImport using the json rpc call\n"
264 + HelpExampleRpc("importwallet", "\"test\"")
267 return importwallet_impl(params, fHelp, false);
270 Value importwallet_impl(const Array& params, bool fHelp, bool fImportZKeys)
272 LOCK2(cs_main, pwalletMain->cs_wallet);
274 EnsureWalletIsUnlocked();
277 file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate);
279 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
281 int64_t nTimeBegin = chainActive.Tip()->GetBlockTime();
285 int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
286 file.seekg(0, file.beg);
288 pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
289 while (file.good()) {
290 pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
292 std::getline(file, line);
293 if (line.empty() || line[0] == '#')
296 std::vector<std::string> vstr;
297 boost::split(vstr, line, boost::is_any_of(" "));
301 // Let's see if the address is a valid Zcash spending key
304 CZCSpendingKey spendingkey(vstr[0]);
305 libzcash::SpendingKey key = spendingkey.Get();
306 libzcash::PaymentAddress addr = key.address();
307 if (pwalletMain->HaveSpendingKey(addr)) {
308 LogPrint("zrpc", "Skipping import of zaddr %s (key already present)\n", CZCPaymentAddress(addr).ToString());
311 int64_t nTime = DecodeDumpTime(vstr[1]);
312 LogPrint("zrpc", "Importing zaddr %s...\n", CZCPaymentAddress(addr).ToString());
313 if (!pwalletMain->AddZKey(key)) {
314 // Something went wrong
318 // Successfully imported zaddr. Now import the metadata.
319 pwalletMain->mapZKeyMetadata[addr].nCreateTime = nTime;
322 catch (const std::runtime_error &e) {
323 LogPrint("zrpc","Importing detected an error: %s\n", e.what());
324 // Not a valid spending key, so carry on and see if it's a Zcash style address.
328 CBitcoinSecret vchSecret;
329 if (!vchSecret.SetString(vstr[0]))
331 CKey key = vchSecret.GetKey();
332 CPubKey pubkey = key.GetPubKey();
333 assert(key.VerifyPubKey(pubkey));
334 CKeyID keyid = pubkey.GetID();
335 if (pwalletMain->HaveKey(keyid)) {
336 LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
339 int64_t nTime = DecodeDumpTime(vstr[1]);
340 std::string strLabel;
342 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
343 if (boost::algorithm::starts_with(vstr[nStr], "#"))
345 if (vstr[nStr] == "change=1")
347 if (vstr[nStr] == "reserve=1")
349 if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
350 strLabel = DecodeDumpString(vstr[nStr].substr(6));
354 LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
355 if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
359 pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
361 pwalletMain->SetAddressBook(keyid, strLabel, "receive");
362 nTimeBegin = std::min(nTimeBegin, nTime);
365 pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI
367 CBlockIndex *pindex = chainActive.Tip();
368 while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200)
369 pindex = pindex->pprev;
371 if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
372 pwalletMain->nTimeFirstKey = nTimeBegin;
374 LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
375 pwalletMain->ScanForWalletTransactions(pindex);
376 pwalletMain->MarkDirty();
379 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
384 Value dumpprivkey(const Array& params, bool fHelp)
386 if (!EnsureWalletIsAvailable(fHelp))
389 if (fHelp || params.size() != 1)
391 "dumpprivkey \"zcashaddress\"\n"
392 "\nReveals the private key corresponding to 'zcashaddress'.\n"
393 "Then the importprivkey can be used with this output\n"
395 "1. \"zcashaddress\" (string, required) The zcash address for the private key\n"
397 "\"key\" (string) The private key\n"
399 + HelpExampleCli("dumpprivkey", "\"myaddress\"")
400 + HelpExampleCli("importprivkey", "\"mykey\"")
401 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
404 LOCK2(cs_main, pwalletMain->cs_wallet);
406 EnsureWalletIsUnlocked();
408 string strAddress = params[0].get_str();
409 CBitcoinAddress address;
410 if (!address.SetString(strAddress))
411 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Komodo address");
413 if (!address.GetKeyID(keyID))
414 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
416 if (!pwalletMain->GetKey(keyID, vchSecret))
417 throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
418 return CBitcoinSecret(vchSecret).ToString();
423 Value z_exportwallet(const Array& params, bool fHelp)
425 if (!EnsureWalletIsAvailable(fHelp))
428 if (fHelp || params.size() != 1)
430 "z_exportwallet \"filename\"\n"
431 "\nExports all wallet keys, for taddr and zaddr, in a human-readable format.\n"
433 "1. \"filename\" (string, required) The filename\n"
435 + HelpExampleCli("z_exportwallet", "\"test\"")
436 + HelpExampleRpc("z_exportwallet", "\"test\"")
439 return dumpwallet_impl(params, fHelp, true);
442 Value dumpwallet(const Array& params, bool fHelp)
444 if (!EnsureWalletIsAvailable(fHelp))
447 if (fHelp || params.size() != 1)
449 "dumpwallet \"filename\"\n"
450 "\nDumps taddr wallet keys in a human-readable format.\n"
452 "1. \"filename\" (string, required) The filename\n"
454 + HelpExampleCli("dumpwallet", "\"test\"")
455 + HelpExampleRpc("dumpwallet", "\"test\"")
458 return dumpwallet_impl(params, fHelp, false);
461 Value dumpwallet_impl(const Array& params, bool fHelp, bool fDumpZKeys)
463 LOCK2(cs_main, pwalletMain->cs_wallet);
465 EnsureWalletIsUnlocked();
468 file.open(params[0].get_str().c_str());
470 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
472 std::map<CKeyID, int64_t> mapKeyBirth;
473 std::set<CKeyID> setKeyPool;
474 pwalletMain->GetKeyBirthTimes(mapKeyBirth);
475 pwalletMain->GetAllReserveKeys(setKeyPool);
477 // sort time/key pairs
478 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
479 for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
480 vKeyBirth.push_back(std::make_pair(it->second, it->first));
483 std::sort(vKeyBirth.begin(), vKeyBirth.end());
486 file << strprintf("# Wallet dump created by Komodo %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
487 file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
488 file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
489 file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
491 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
492 const CKeyID &keyid = it->second;
493 std::string strTime = EncodeDumpTime(it->first);
494 std::string strAddr = CBitcoinAddress(keyid).ToString();
496 if (pwalletMain->GetKey(keyid, key)) {
497 if (pwalletMain->mapAddressBook.count(keyid)) {
498 file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
499 } else if (setKeyPool.count(keyid)) {
500 file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
502 file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
509 std::set<libzcash::PaymentAddress> addresses;
510 pwalletMain->GetPaymentAddresses(addresses);
514 for (auto addr : addresses ) {
515 libzcash::SpendingKey key;
516 if (pwalletMain->GetSpendingKey(addr, key)) {
517 std::string strTime = EncodeDumpTime(pwalletMain->mapZKeyMetadata[addr].nCreateTime);
518 file << strprintf("%s %s # zaddr=%s\n", CZCSpendingKey(key).ToString(), strTime, CZCPaymentAddress(addr).ToString());
524 file << "# End of dump\n";
530 Value z_importkey(const Array& params, bool fHelp)
532 if (!EnsureWalletIsAvailable(fHelp))
535 if (fHelp || params.size() < 1 || params.size() > 2)
537 "z_importkey \"zkey\" ( rescan )\n"
538 "\nAdds a zkey (as returned by z_exportkey) to your wallet.\n"
540 "1. \"zkey\" (string, required) The zkey (see z_exportkey)\n"
541 "2. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
542 "\nNote: This call can take minutes to complete if rescan is true.\n"
545 + HelpExampleCli("z_exportkey", "\"myaddress\"") +
546 "\nImport the zkey with rescan\n"
547 + HelpExampleCli("z_importkey", "\"mykey\"") +
548 "\nAs a JSON-RPC call\n"
549 + HelpExampleRpc("z_importkey", "\"mykey\", false")
552 LOCK2(cs_main, pwalletMain->cs_wallet);
554 EnsureWalletIsUnlocked();
556 // Whether to perform rescan after import
558 if (params.size() > 1)
559 fRescan = params[1].get_bool();
561 string strSecret = params[0].get_str();
562 CZCSpendingKey spendingkey(strSecret);
563 auto key = spendingkey.Get();
564 auto addr = key.address();
567 // Don't throw error in case a key is already there
568 if (pwalletMain->HaveSpendingKey(addr))
571 pwalletMain->MarkDirty();
573 if (!pwalletMain-> AddZKey(key))
574 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
576 pwalletMain->mapZKeyMetadata[addr].nCreateTime = 1;
578 // We want to scan for transactions and notes
580 pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
588 Value z_exportkey(const Array& params, bool fHelp)
590 if (!EnsureWalletIsAvailable(fHelp))
593 if (fHelp || params.size() != 1)
595 "z_exportkey \"zaddr\"\n"
596 "\nReveals the zkey corresponding to 'zaddr'.\n"
597 "Then the z_importkey can be used with this output\n"
599 "1. \"zaddr\" (string, required) The zaddr for the private key\n"
601 "\"key\" (string) The private key\n"
603 + HelpExampleCli("z_exportkey", "\"myaddress\"")
604 + HelpExampleCli("z_importkey", "\"mykey\"")
605 + HelpExampleRpc("z_exportkey", "\"myaddress\"")
608 LOCK2(cs_main, pwalletMain->cs_wallet);
610 EnsureWalletIsUnlocked();
612 string strAddress = params[0].get_str();
614 CZCPaymentAddress address(strAddress);
615 auto addr = address.Get();
617 libzcash::SpendingKey k;
618 if (!pwalletMain->GetSpendingKey(addr, k))
619 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
621 CZCSpendingKey spendingkey(k);
622 return spendingkey.ToString();