Key metadata update + new timestamp conversion function
[novacoin.git] / src / rpcdump.cpp
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.
4
5 #include <iostream>
6 #include <fstream>
7
8 #include "init.h" // for pwalletMain
9 #include "bitcoinrpc.h"
10 #include "ui_interface.h"
11 #include "base58.h"
12
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>
17
18 #define printf OutputDebugStringF
19
20 using namespace json_spirit;
21 using namespace std;
22
23 void EnsureWalletIsUnlocked();
24
25 namespace bt = boost::posix_time;
26
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"))
35 };
36
37 const size_t formats_n = sizeof(formats)/sizeof(formats[0]);
38
39 std::time_t pt_to_time_t(const bt::ptime& pt)
40 {
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;
44 }
45
46 int64 DecodeDumpTime(const std::string& s)
47 {
48     bt::ptime pt;
49
50     for(size_t i=0; i<formats_n; ++i)
51     {
52         std::istringstream is(s);
53         is.imbue(formats[i]);
54         is >> pt;
55         if(pt != bt::ptime()) break;
56     }
57
58     return pt_to_time_t(pt);
59 }
60
61 std::string static EncodeDumpTime(int64 nTime) {
62     return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
63 }
64
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);
70         } else {
71             ret << c;
72         }
73     }
74     return ret.str();
75 }
76
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));
84             pos += 2;
85         }
86         ret << c;
87     }
88     return ret.str();
89 }
90
91 class CTxDump
92 {
93 public:
94     CBlockIndex *pindex;
95     int64 nValue;
96     bool fSpent;
97     CWalletTx* ptx;
98     int nOut;
99     CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
100     {
101         pindex = NULL;
102         nValue = 0;
103         fSpent = false;
104         this->ptx = ptx;
105         this->nOut = nOut;
106     }
107 };
108
109 Value importprivkey(const Array& params, bool fHelp)
110 {
111     if (fHelp || params.size() < 1 || params.size() > 2)
112         throw runtime_error(
113             "importprivkey <novacoinprivkey> [label]\n"
114             "Adds a private key (as returned by dumpprivkey) to your wallet.");
115
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);
122
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.");
126
127     CKey key;
128     bool fCompressed;
129     CSecret secret = vchSecret.GetSecret(fCompressed);
130     key.SetSecret(secret, fCompressed);
131     CKeyID vchAddress = key.GetPubKey().GetID();
132     {
133         LOCK2(cs_main, pwalletMain->cs_wallet);
134
135         pwalletMain->MarkDirty();
136         pwalletMain->SetAddressBookName(vchAddress, strLabel);
137
138         if (!pwalletMain->AddKey(key))
139             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
140
141         pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
142         pwalletMain->ReacceptWalletTransactions();
143     }
144
145     return Value::null;
146 }
147
148 Value importwallet(const Array& params, bool fHelp)
149 {
150     if (fHelp || params.size() != 1)
151         throw runtime_error(
152             "importwallet <filename>\n"
153             "Imports keys from a wallet dump file (see dumpwallet).");
154
155     EnsureWalletIsUnlocked();
156
157     ifstream file;
158     file.open(params[0].get_str().c_str());
159     if (!file.is_open())
160         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
161
162     int64 nTimeBegin = pindexBest->nTime;
163
164     bool fGood = true;
165
166     while (file.good()) {
167         std::string line;
168         std::getline(file, line);
169         if (line.empty() || line[0] == '#')
170             continue;
171
172         std::vector<std::string> vstr;
173         boost::split(vstr, line, boost::is_any_of(" "));
174         if (vstr.size() < 2)
175             continue;
176         CBitcoinSecret vchSecret;
177         if (!vchSecret.SetString(vstr[0]))
178             continue;
179
180         bool fCompressed;
181         CKey key;
182         CSecret secret = vchSecret.GetSecret(fCompressed);
183         key.SetSecret(secret, fCompressed);
184         CKeyID keyid = key.GetPubKey().GetID();
185
186         if (pwalletMain->HaveKey(keyid)) {
187             printf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str());
188             continue;
189         }
190         int64 nTime = DecodeDumpTime(vstr[1]);
191         std::string strLabel;
192         bool fLabel = true;
193         for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
194             if (boost::algorithm::starts_with(vstr[nStr], "#"))
195                 break;
196             if (vstr[nStr] == "change=1")
197                 fLabel = false;
198             if (vstr[nStr] == "reserve=1")
199                 fLabel = false;
200             if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
201                 strLabel = DecodeDumpString(vstr[nStr].substr(6));
202                 fLabel = true;
203             }
204         }
205         printf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str());
206         if (!pwalletMain->AddKey(key)) {
207             fGood = false;
208             continue;
209         }
210         pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
211         if (fLabel)
212             pwalletMain->SetAddressBookName(keyid, strLabel);
213         nTimeBegin = std::min(nTimeBegin, nTime);
214     }
215     file.close();
216
217     CBlockIndex *pindex = pindexBest;
218     while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
219         pindex = pindex->pprev;
220
221     printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
222     pwalletMain->ScanForWalletTransactions(pindex);
223     pwalletMain->ReacceptWalletTransactions();
224     pwalletMain->MarkDirty();
225
226     if (!fGood)
227         throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
228
229     return Value::null;
230 }
231
232
233 Value dumpprivkey(const Array& params, bool fHelp)
234 {
235     if (fHelp || params.size() != 1)
236         throw runtime_error(
237             "dumpprivkey <novacoinaddress>\n"
238             "Reveals the private key corresponding to <novacoinaddress>.");
239
240     EnsureWalletIsUnlocked();
241
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.");
248     CKeyID keyID;
249     if (!address.GetKeyID(keyID))
250         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
251     CSecret vchSecret;
252     bool fCompressed;
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();
256 }
257
258 Value dumpwallet(const Array& params, bool fHelp)
259 {
260     if (fHelp || params.size() != 1)
261         throw runtime_error(
262             "dumpwallet <filename>\n"
263             "Dumps all wallet keys in a human-readable format.");
264
265     EnsureWalletIsUnlocked();
266
267     ofstream file;
268     file.open(params[0].get_str().c_str());
269     if (!file.is_open())
270         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
271
272     std::map<CKeyID, int64> mapKeyBirth;
273
274     std::set<CKeyID> setKeyPool;
275
276     pwalletMain->GetKeyBirthTimes(mapKeyBirth);
277
278     pwalletMain->GetAllReserveKeys(setKeyPool);
279
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));
284     }
285     mapKeyBirth.clear();
286     std::sort(vKeyBirth.begin(), vKeyBirth.end());
287
288     // produce output
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());
293     file << "\n";
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();
298         bool IsCompressed;
299
300         CKey key;
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());
308             } else {
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());
311             }
312         }
313     }
314     file << "\n";
315     file << "# End of dump\n";
316     file.close();
317     return Value::null;
318 }