1 // Copyright (c) 2009-2012 Bitcoin Developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "init.h" // for pwalletMain
9 #include "bitcoinrpc.h"
10 #include "ui_interface.h"
13 #include <boost/date_time/posix_time/posix_time.hpp>
14 #include <boost/lexical_cast.hpp>
15 #include <boost/variant/get.hpp>
16 #include <boost/algorithm/string.hpp>
18 #define printf OutputDebugStringF
20 using namespace json_spirit;
23 void EnsureWalletIsUnlocked();
25 namespace bt = boost::posix_time;
27 // Extended DecodeDumpTime implementation, see this page for details:
28 // http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost
29 const std::locale formats[] = {
30 std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
31 std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
32 std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
33 std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
34 std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
37 const size_t formats_n = sizeof(formats)/sizeof(formats[0]);
39 std::time_t pt_to_time_t(const bt::ptime& pt)
41 bt::ptime timet_start(boost::gregorian::date(1970,1,1));
42 bt::time_duration diff = pt - timet_start;
43 return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
46 int64 DecodeDumpTime(const std::string& s)
50 for(size_t i=0; i<formats_n; ++i)
52 std::istringstream is(s);
55 if(pt != bt::ptime()) break;
58 return pt_to_time_t(pt);
61 std::string static EncodeDumpTime(int64 nTime) {
62 return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
65 std::string static EncodeDumpString(const std::string &str) {
66 std::stringstream ret;
67 BOOST_FOREACH(unsigned char c, str) {
68 if (c <= 32 || c >= 128 || c == '%') {
69 ret << '%' << HexStr(&c, &c + 1);
77 std::string DecodeDumpString(const std::string &str) {
78 std::stringstream ret;
79 for (unsigned int pos = 0; pos < str.length(); pos++) {
80 unsigned char c = str[pos];
81 if (c == '%' && pos+2 < str.length()) {
82 c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
83 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
99 CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
109 Value importprivkey(const Array& params, bool fHelp)
111 if (fHelp || params.size() < 1 || params.size() > 2)
113 "importprivkey <novacoinprivkey> [label]\n"
114 "Adds a private key (as returned by dumpprivkey) to your wallet.");
116 string strSecret = params[0].get_str();
117 string strLabel = "";
118 if (params.size() > 1)
119 strLabel = params[1].get_str();
120 CBitcoinSecret vchSecret;
121 bool fGood = vchSecret.SetString(strSecret);
123 if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
124 if (fWalletUnlockMintOnly) // ppcoin: no importprivkey in mint-only mode
125 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only.");
129 CSecret secret = vchSecret.GetSecret(fCompressed);
130 key.SetSecret(secret, fCompressed);
131 CKeyID vchAddress = key.GetPubKey().GetID();
133 LOCK2(cs_main, pwalletMain->cs_wallet);
135 pwalletMain->MarkDirty();
136 pwalletMain->SetAddressBookName(vchAddress, strLabel);
138 if (!pwalletMain->AddKey(key))
139 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
141 pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
142 pwalletMain->ReacceptWalletTransactions();
148 Value importwallet(const Array& params, bool fHelp)
150 if (fHelp || params.size() != 1)
152 "importwallet <filename>\n"
153 "Imports keys from a wallet dump file (see dumpwallet).");
155 EnsureWalletIsUnlocked();
158 file.open(params[0].get_str().c_str());
160 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
162 int64 nTimeBegin = pindexBest->nTime;
166 while (file.good()) {
168 std::getline(file, line);
169 if (line.empty() || line[0] == '#')
172 std::vector<std::string> vstr;
173 boost::split(vstr, line, boost::is_any_of(" "));
176 CBitcoinSecret vchSecret;
177 if (!vchSecret.SetString(vstr[0]))
182 CSecret secret = vchSecret.GetSecret(fCompressed);
183 key.SetSecret(secret, fCompressed);
184 CKeyID keyid = key.GetPubKey().GetID();
186 if (pwalletMain->HaveKey(keyid)) {
187 printf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str());
190 int64 nTime = DecodeDumpTime(vstr[1]);
191 std::string strLabel;
193 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
194 if (boost::algorithm::starts_with(vstr[nStr], "#"))
196 if (vstr[nStr] == "change=1")
198 if (vstr[nStr] == "reserve=1")
200 if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
201 strLabel = DecodeDumpString(vstr[nStr].substr(6));
205 printf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str());
206 if (!pwalletMain->AddKey(key)) {
210 pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
212 pwalletMain->SetAddressBookName(keyid, strLabel);
213 nTimeBegin = std::min(nTimeBegin, nTime);
217 CBlockIndex *pindex = pindexBest;
218 while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
219 pindex = pindex->pprev;
221 printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
222 pwalletMain->ScanForWalletTransactions(pindex);
223 pwalletMain->ReacceptWalletTransactions();
224 pwalletMain->MarkDirty();
227 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
233 Value dumpprivkey(const Array& params, bool fHelp)
235 if (fHelp || params.size() != 1)
237 "dumpprivkey <novacoinaddress>\n"
238 "Reveals the private key corresponding to <novacoinaddress>.");
240 EnsureWalletIsUnlocked();
242 string strAddress = params[0].get_str();
243 CBitcoinAddress address;
244 if (!address.SetString(strAddress))
245 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
246 if (fWalletUnlockMintOnly) // ppcoin: no dumpprivkey in mint-only mode
247 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only.");
249 if (!address.GetKeyID(keyID))
250 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
253 if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed))
254 throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
255 return CBitcoinSecret(vchSecret, fCompressed).ToString();
258 Value dumpwallet(const Array& params, bool fHelp)
260 if (fHelp || params.size() != 1)
262 "dumpwallet <filename>\n"
263 "Dumps all wallet keys in a human-readable format.");
265 EnsureWalletIsUnlocked();
268 file.open(params[0].get_str().c_str());
270 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
272 std::map<CKeyID, int64> mapKeyBirth;
274 std::set<CKeyID> setKeyPool;
276 pwalletMain->GetKeyBirthTimes(mapKeyBirth);
278 pwalletMain->GetAllReserveKeys(setKeyPool);
280 // sort time/key pairs
281 std::vector<std::pair<int64, CKeyID> > vKeyBirth;
282 for (std::map<CKeyID, int64>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
283 vKeyBirth.push_back(std::make_pair(it->second, it->first));
286 std::sort(vKeyBirth.begin(), vKeyBirth.end());
289 file << strprintf("# Wallet dump created by NovaCoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str());
290 file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str());
291 file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str());
292 file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str());
294 for (std::vector<std::pair<int64, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
295 const CKeyID &keyid = it->second;
296 std::string strTime = EncodeDumpTime(it->first);
297 std::string strAddr = CBitcoinAddress(keyid).ToString();
301 if (pwalletMain->GetKey(keyid, key)) {
302 if (pwalletMain->mapAddressBook.count(keyid)) {
303 CSecret secret = key.GetSecret(IsCompressed);
304 file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), EncodeDumpString(pwalletMain->mapAddressBook[keyid]).c_str(), strAddr.c_str());
305 } else if (setKeyPool.count(keyid)) {
306 CSecret secret = key.GetSecret(IsCompressed);
307 file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), strAddr.c_str());
309 CSecret secret = key.GetSecret(IsCompressed);
310 file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), strAddr.c_str());
315 file << "# End of dump\n";