PPCoin: stakeonly mode for RPC command 'walletpassphrase'
[novacoin.git] / src / bitcoinrpc.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Copyright (c) 2011-2012 The PPCoin developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
6
7 #include "headers.h"
8 #include "db.h"
9 #include "net.h"
10 #include "init.h"
11 #include "checkpoints.h"
12 #undef printf
13 #include <boost/asio.hpp>
14 #include <boost/iostreams/concepts.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #include <boost/algorithm/string.hpp>
17 #ifdef USE_SSL
18 #include <boost/asio/ssl.hpp> 
19 #include <boost/filesystem.hpp>
20 #include <boost/filesystem/fstream.hpp>
21 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
22 #endif
23 #include "json/json_spirit_reader_template.h"
24 #include "json/json_spirit_writer_template.h"
25 #include "json/json_spirit_utils.h"
26 #define printf OutputDebugStringF
27 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
28 // precompiled in headers.h.  The problem might be when the pch file goes over
29 // a certain size around 145MB.  If we need access to json_spirit outside this
30 // file, we could use the compiled json_spirit option.
31
32 using namespace std;
33 using namespace boost;
34 using namespace boost::asio;
35 using namespace json_spirit;
36
37 void ThreadRPCServer2(void* parg);
38 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
39 extern map<string, rpcfn_type> mapCallTable;
40
41 static std::string strRPCUserColonPass;
42
43 static int64 nWalletUnlockTime;
44 static CCriticalSection cs_nWalletUnlockTime;
45
46
47 Object JSONRPCError(int code, const string& message)
48 {
49     Object error;
50     error.push_back(Pair("code", code));
51     error.push_back(Pair("message", message));
52     return error;
53 }
54
55
56 void PrintConsole(const std::string &format, ...)
57 {
58     char buffer[50000];
59     int limit = sizeof(buffer);
60     va_list arg_ptr;
61     va_start(arg_ptr, format);
62     int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
63     va_end(arg_ptr);
64     if (ret < 0 || ret >= limit)
65     {
66         ret = limit - 1;
67         buffer[limit-1] = 0;
68     }
69     printf("%s", buffer);
70     fprintf(stdout, "%s", buffer);
71 }
72
73
74 int64 AmountFromValue(const Value& value)
75 {
76     double dAmount = value.get_real();
77     if (dAmount <= 0.0 || dAmount > MAX_MONEY)
78         throw JSONRPCError(-3, "Invalid amount");
79     int64 nAmount = roundint64(dAmount * COIN);
80     if (!MoneyRange(nAmount))
81         throw JSONRPCError(-3, "Invalid amount");
82     return nAmount;
83 }
84
85 Value ValueFromAmount(int64 amount)
86 {
87     return (double)amount / (double)COIN;
88 }
89
90 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
91 {
92     entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
93     entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
94     entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
95     BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
96         entry.push_back(Pair(item.first, item.second));
97 }
98
99 string AccountFromValue(const Value& value)
100 {
101     string strAccount = value.get_str();
102     if (strAccount == "*")
103         throw JSONRPCError(-11, "Invalid account name");
104     return strAccount;
105 }
106
107
108
109 ///
110 /// Note: This interface may still be subject to change.
111 ///
112
113
114 Value help(const Array& params, bool fHelp)
115 {
116     if (fHelp || params.size() > 1)
117         throw runtime_error(
118             "help [command]\n"
119             "List commands, or get help for a command.");
120
121     string strCommand;
122     if (params.size() > 0)
123         strCommand = params[0].get_str();
124
125     string strRet;
126     set<rpcfn_type> setDone;
127     for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
128     {
129         string strMethod = (*mi).first;
130         // We already filter duplicates, but these deprecated screw up the sort order
131         if (strMethod == "getamountreceived" ||
132             strMethod == "getallreceived" ||
133             strMethod == "getblocknumber" || // deprecated
134             (strMethod.find("label") != string::npos))
135             continue;
136         if (strCommand != "" && strMethod != strCommand)
137             continue;
138         try
139         {
140             Array params;
141             rpcfn_type pfn = (*mi).second;
142             if (setDone.insert(pfn).second)
143                 (*pfn)(params, true);
144         }
145         catch (std::exception& e)
146         {
147             // Help text is returned in an exception
148             string strHelp = string(e.what());
149             if (strCommand == "")
150                 if (strHelp.find('\n') != -1)
151                     strHelp = strHelp.substr(0, strHelp.find('\n'));
152             strRet += strHelp + "\n";
153         }
154     }
155     if (strRet == "")
156         strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
157     strRet = strRet.substr(0,strRet.size()-1);
158     return strRet;
159 }
160
161
162 Value stop(const Array& params, bool fHelp)
163 {
164     if (fHelp || params.size() != 0)
165         throw runtime_error(
166             "stop\n"
167             "Stop ppcoin server.");
168 #ifndef QT_GUI
169     // Shutdown will take long enough that the response should get back
170     CreateThread(Shutdown, NULL);
171     return "ppcoin server stopping";
172 #else
173     throw runtime_error("NYI: cannot shut down GUI with RPC command");
174 #endif
175 }
176
177
178 Value getblockcount(const Array& params, bool fHelp)
179 {
180     if (fHelp || params.size() != 0)
181         throw runtime_error(
182             "getblockcount\n"
183             "Returns the number of blocks in the longest block chain.");
184
185     return nBestHeight;
186 }
187
188
189 // deprecated
190 Value getblocknumber(const Array& params, bool fHelp)
191 {
192     if (fHelp || params.size() != 0)
193         throw runtime_error(
194             "getblocknumber\n"
195             "Deprecated.  Use getblockcount.");
196
197     return nBestHeight;
198 }
199
200
201 Value getconnectioncount(const Array& params, bool fHelp)
202 {
203     if (fHelp || params.size() != 0)
204         throw runtime_error(
205             "getconnectioncount\n"
206             "Returns the number of connections to other nodes.");
207
208     return (int)vNodes.size();
209 }
210
211
212 double GetDifficulty()
213 {
214     // Floating point number that is a multiple of the minimum difficulty,
215     // minimum difficulty = 1.0.
216
217     if (pindexBest == NULL)
218         return 1.0;
219     int nShift = (pindexBest->nBits >> 24) & 0xff;
220
221     double dDiff =
222         (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
223
224     while (nShift < 29)
225     {
226         dDiff *= 256.0;
227         nShift++;
228     }
229     while (nShift > 29)
230     {
231         dDiff /= 256.0;
232         nShift--;
233     }
234
235     return dDiff;
236 }
237
238 Value getdifficulty(const Array& params, bool fHelp)
239 {
240     if (fHelp || params.size() != 0)
241         throw runtime_error(
242             "getdifficulty\n"
243             "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
244
245     return GetDifficulty();
246 }
247
248
249 Value getgenerate(const Array& params, bool fHelp)
250 {
251     if (fHelp || params.size() != 0)
252         throw runtime_error(
253             "getgenerate\n"
254             "Returns true or false.");
255
256     return (bool)fGenerateBitcoins;
257 }
258
259
260 Value setgenerate(const Array& params, bool fHelp)
261 {
262     if (fHelp || params.size() < 1 || params.size() > 2)
263         throw runtime_error(
264             "setgenerate <generate> [genproclimit]\n"
265             "<generate> is true or false to turn generation on or off.\n"
266             "Generation is limited to [genproclimit] processors, -1 is unlimited.");
267
268     bool fGenerate = true;
269     if (params.size() > 0)
270         fGenerate = params[0].get_bool();
271
272     if (params.size() > 1)
273     {
274         int nGenProcLimit = params[1].get_int();
275         fLimitProcessors = (nGenProcLimit != -1);
276         WriteSetting("fLimitProcessors", fLimitProcessors);
277         if (nGenProcLimit != -1)
278             WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
279         if (nGenProcLimit == 0)
280             fGenerate = false;
281     }
282
283     GenerateBitcoins(fGenerate, pwalletMain);
284     return Value::null;
285 }
286
287
288 Value gethashespersec(const Array& params, bool fHelp)
289 {
290     if (fHelp || params.size() != 0)
291         throw runtime_error(
292             "gethashespersec\n"
293             "Returns a recent hashes per second performance measurement while generating.");
294
295     if (GetTimeMillis() - nHPSTimerStart > 8000)
296         return (boost::int64_t)0;
297     return (boost::int64_t)dHashesPerSec;
298 }
299
300
301 Value getinfo(const Array& params, bool fHelp)
302 {
303     if (fHelp || params.size() != 0)
304         throw runtime_error(
305             "getinfo\n"
306             "Returns an object containing various state info.");
307
308     Object obj;
309     obj.push_back(Pair("version",       (int)VERSION));
310     obj.push_back(Pair("balance",       ValueFromAmount(pwalletMain->GetBalance())));
311     obj.push_back(Pair("stake",         ValueFromAmount(pwalletMain->GetStake())));
312     obj.push_back(Pair("blocks",        (int)nBestHeight));
313     obj.push_back(Pair("connections",   (int)vNodes.size()));
314     obj.push_back(Pair("proxy",         (fUseProxy ? addrProxy.ToStringIPPort() : string())));
315     obj.push_back(Pair("generate",      (bool)fGenerateBitcoins));
316     obj.push_back(Pair("genproclimit",  (int)(fLimitProcessors ? nLimitProcessors : -1)));
317     obj.push_back(Pair("difficulty",    (double)GetDifficulty()));
318     obj.push_back(Pair("hashespersec",  gethashespersec(params, false)));
319     obj.push_back(Pair("testnet",       fTestNet));
320     obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
321     obj.push_back(Pair("keypoolsize",   pwalletMain->GetKeyPoolSize()));
322     obj.push_back(Pair("paytxfee",      ValueFromAmount(nTransactionFee)));
323     if (pwalletMain->IsCrypted())
324         obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
325     obj.push_back(Pair("errors",        GetWarnings("statusbar")));
326     return obj;
327 }
328
329
330 Value getnewaddress(const Array& params, bool fHelp)
331 {
332     if (fHelp || params.size() > 1)
333         throw runtime_error(
334             "getnewaddress [account]\n"
335             "Returns a new ppcoin address for receiving payments.  "
336             "If [account] is specified (recommended), it is added to the address book "
337             "so payments received with the address will be credited to [account].");
338
339     // Parse the account first so we don't generate a key if there's an error
340     string strAccount;
341     if (params.size() > 0)
342         strAccount = AccountFromValue(params[0]);
343
344     if (!pwalletMain->IsLocked())
345         pwalletMain->TopUpKeyPool();
346
347     // Generate a new key that is added to wallet
348     std::vector<unsigned char> newKey;
349     if (!pwalletMain->GetKeyFromPool(newKey, false))
350         throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
351     CBitcoinAddress address(newKey);
352
353     pwalletMain->SetAddressBookName(address, strAccount);
354
355     return address.ToString();
356 }
357
358
359 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
360 {
361     CWalletDB walletdb(pwalletMain->strWalletFile);
362
363     CAccount account;
364     walletdb.ReadAccount(strAccount, account);
365
366     bool bKeyUsed = false;
367
368     // Check if the current key has been used
369     if (!account.vchPubKey.empty())
370     {
371         CScript scriptPubKey;
372         scriptPubKey.SetBitcoinAddress(account.vchPubKey);
373         for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
374              it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
375              ++it)
376         {
377             const CWalletTx& wtx = (*it).second;
378             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
379                 if (txout.scriptPubKey == scriptPubKey)
380                     bKeyUsed = true;
381         }
382     }
383
384     // Generate a new key
385     if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
386     {
387         if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
388             throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
389
390         pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
391         walletdb.WriteAccount(strAccount, account);
392     }
393
394     return CBitcoinAddress(account.vchPubKey);
395 }
396
397 Value getaccountaddress(const Array& params, bool fHelp)
398 {
399     if (fHelp || params.size() != 1)
400         throw runtime_error(
401             "getaccountaddress <account>\n"
402             "Returns the current ppcoin address for receiving payments to this account.");
403
404     // Parse the account first so we don't generate a key if there's an error
405     string strAccount = AccountFromValue(params[0]);
406
407     Value ret;
408
409     ret = GetAccountAddress(strAccount).ToString();
410
411     return ret;
412 }
413
414
415
416 Value setaccount(const Array& params, bool fHelp)
417 {
418     if (fHelp || params.size() < 1 || params.size() > 2)
419         throw runtime_error(
420             "setaccount <ppcoinaddress> <account>\n"
421             "Sets the account associated with the given address.");
422
423     CBitcoinAddress address(params[0].get_str());
424     if (!address.IsValid())
425         throw JSONRPCError(-5, "Invalid ppcoin address");
426
427
428     string strAccount;
429     if (params.size() > 1)
430         strAccount = AccountFromValue(params[1]);
431
432     // Detect when changing the account of an address that is the 'unused current key' of another account:
433     if (pwalletMain->mapAddressBook.count(address))
434     {
435         string strOldAccount = pwalletMain->mapAddressBook[address];
436         if (address == GetAccountAddress(strOldAccount))
437             GetAccountAddress(strOldAccount, true);
438     }
439
440     pwalletMain->SetAddressBookName(address, strAccount);
441
442     return Value::null;
443 }
444
445
446 Value getaccount(const Array& params, bool fHelp)
447 {
448     if (fHelp || params.size() != 1)
449         throw runtime_error(
450             "getaccount <ppcoinaddress>\n"
451             "Returns the account associated with the given address.");
452
453     CBitcoinAddress address(params[0].get_str());
454     if (!address.IsValid())
455         throw JSONRPCError(-5, "Invalid ppcoin address");
456
457     string strAccount;
458     map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
459     if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
460         strAccount = (*mi).second;
461     return strAccount;
462 }
463
464
465 Value getaddressesbyaccount(const Array& params, bool fHelp)
466 {
467     if (fHelp || params.size() != 1)
468         throw runtime_error(
469             "getaddressesbyaccount <account>\n"
470             "Returns the list of addresses for the given account.");
471
472     string strAccount = AccountFromValue(params[0]);
473
474     // Find all addresses that have the given account
475     Array ret;
476     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
477     {
478         const CBitcoinAddress& address = item.first;
479         const string& strName = item.second;
480         if (strName == strAccount)
481             ret.push_back(address.ToString());
482     }
483     return ret;
484 }
485
486 Value settxfee(const Array& params, bool fHelp)
487 {
488     if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
489         throw runtime_error(
490             "settxfee <amount>\n"
491             "<amount> is a real and is rounded to 0.01 (cent)\n"
492             "Minimum and default transaction fee per KB is 1 cent");
493
494     nTransactionFee = AmountFromValue(params[0]);
495     nTransactionFee = (nTransactionFee / CENT) * CENT;  // round to cent
496     return true;
497 }
498
499 Value sendtoaddress(const Array& params, bool fHelp)
500 {
501     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
502         throw runtime_error(
503             "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
504             "<amount> is a real and is rounded to the nearest 0.000001\n"
505             "requires wallet passphrase to be set with walletpassphrase first");
506     if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
507         throw runtime_error(
508             "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
509             "<amount> is a real and is rounded to the nearest 0.000001");
510
511     CBitcoinAddress address(params[0].get_str());
512     if (!address.IsValid())
513         throw JSONRPCError(-5, "Invalid ppcoin address");
514
515     // Amount
516     int64 nAmount = AmountFromValue(params[1]);
517
518     // Wallet comments
519     CWalletTx wtx;
520     if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
521         wtx.mapValue["comment"] = params[2].get_str();
522     if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
523         wtx.mapValue["to"]      = params[3].get_str();
524
525     if (pwalletMain->IsLocked())
526         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
527
528     string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
529     if (strError != "")
530         throw JSONRPCError(-4, strError);
531
532     return wtx.GetHash().GetHex();
533 }
534
535 static const string strMessageMagic = "Bitcoin Signed Message:\n";
536
537 Value signmessage(const Array& params, bool fHelp)
538 {
539     if (fHelp || params.size() != 2)
540         throw runtime_error(
541             "signmessage <ppcoinaddress> <message>\n"
542             "Sign a message with the private key of an address");
543
544     if (pwalletMain->IsLocked())
545         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
546
547     string strAddress = params[0].get_str();
548     string strMessage = params[1].get_str();
549
550     CBitcoinAddress addr(strAddress);
551     if (!addr.IsValid())
552         throw JSONRPCError(-3, "Invalid address");
553
554     CKey key;
555     if (!pwalletMain->GetKey(addr, key))
556         throw JSONRPCError(-4, "Private key not available");
557
558     CDataStream ss(SER_GETHASH);
559     ss << strMessageMagic;
560     ss << strMessage;
561
562     vector<unsigned char> vchSig;
563     if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
564         throw JSONRPCError(-5, "Sign failed");
565
566     return EncodeBase64(&vchSig[0], vchSig.size());
567 }
568
569 Value verifymessage(const Array& params, bool fHelp)
570 {
571     if (fHelp || params.size() != 3)
572         throw runtime_error(
573             "verifymessage <ppcoinaddress> <signature> <message>\n"
574             "Verify a signed message");
575
576     string strAddress  = params[0].get_str();
577     string strSign     = params[1].get_str();
578     string strMessage  = params[2].get_str();
579
580     CBitcoinAddress addr(strAddress);
581     if (!addr.IsValid())
582         throw JSONRPCError(-3, "Invalid address");
583
584     bool fInvalid = false;
585     vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
586
587     if (fInvalid)
588         throw JSONRPCError(-5, "Malformed base64 encoding");
589
590     CDataStream ss(SER_GETHASH);
591     ss << strMessageMagic;
592     ss << strMessage;
593
594     CKey key;
595     if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
596         return false;
597
598     return (key.GetAddress() == addr);
599 }
600
601
602 Value getreceivedbyaddress(const Array& params, bool fHelp)
603 {
604     if (fHelp || params.size() < 1 || params.size() > 2)
605         throw runtime_error(
606             "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
607             "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
608
609     // Bitcoin address
610     CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
611     CScript scriptPubKey;
612     if (!address.IsValid())
613         throw JSONRPCError(-5, "Invalid ppcoin address");
614     scriptPubKey.SetBitcoinAddress(address);
615     if (!IsMine(*pwalletMain,scriptPubKey))
616         return (double)0.0;
617
618     // Minimum confirmations
619     int nMinDepth = 1;
620     if (params.size() > 1)
621         nMinDepth = params[1].get_int();
622
623     // Tally
624     int64 nAmount = 0;
625     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
626     {
627         const CWalletTx& wtx = (*it).second;
628         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
629             continue;
630
631         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
632             if (txout.scriptPubKey == scriptPubKey)
633                 if (wtx.GetDepthInMainChain() >= nMinDepth)
634                     nAmount += txout.nValue;
635     }
636
637     return  ValueFromAmount(nAmount);
638 }
639
640
641 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
642 {
643     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
644     {
645         const CBitcoinAddress& address = item.first;
646         const string& strName = item.second;
647         if (strName == strAccount)
648             setAddress.insert(address);
649     }
650 }
651
652
653 Value getreceivedbyaccount(const Array& params, bool fHelp)
654 {
655     if (fHelp || params.size() < 1 || params.size() > 2)
656         throw runtime_error(
657             "getreceivedbyaccount <account> [minconf=1]\n"
658             "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
659
660     // Minimum confirmations
661     int nMinDepth = 1;
662     if (params.size() > 1)
663         nMinDepth = params[1].get_int();
664
665     // Get the set of pub keys that have the label
666     string strAccount = AccountFromValue(params[0]);
667     set<CBitcoinAddress> setAddress;
668     GetAccountAddresses(strAccount, setAddress);
669
670     // Tally
671     int64 nAmount = 0;
672     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
673     {
674         const CWalletTx& wtx = (*it).second;
675         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
676             continue;
677
678         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
679         {
680             CBitcoinAddress address;
681             if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
682                 if (wtx.GetDepthInMainChain() >= nMinDepth)
683                     nAmount += txout.nValue;
684         }
685     }
686
687     return (double)nAmount / (double)COIN;
688 }
689
690
691 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
692 {
693     int64 nBalance = 0;
694
695     // Tally wallet transactions
696     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
697     {
698         const CWalletTx& wtx = (*it).second;
699         if (!wtx.IsFinal())
700             continue;
701
702         int64 nGenerated, nReceived, nSent, nFee;
703         wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
704
705         if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
706             nBalance += nReceived;
707         nBalance += nGenerated - nSent - nFee;
708     }
709
710     // Tally internal accounting entries
711     nBalance += walletdb.GetAccountCreditDebit(strAccount);
712
713     return nBalance;
714 }
715
716 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
717 {
718     CWalletDB walletdb(pwalletMain->strWalletFile);
719     return GetAccountBalance(walletdb, strAccount, nMinDepth);
720 }
721
722
723 Value getbalance(const Array& params, bool fHelp)
724 {
725     if (fHelp || params.size() > 2)
726         throw runtime_error(
727             "getbalance [account] [minconf=1]\n"
728             "If [account] is not specified, returns the server's total available balance.\n"
729             "If [account] is specified, returns the balance in the account.");
730
731     if (params.size() == 0)
732         return  ValueFromAmount(pwalletMain->GetBalance());
733
734     int nMinDepth = 1;
735     if (params.size() > 1)
736         nMinDepth = params[1].get_int();
737
738     if (params[0].get_str() == "*") {
739         // Calculate total balance a different way from GetBalance()
740         // (GetBalance() sums up all unspent TxOuts)
741         // getbalance and getbalance '*' should always return the same number.
742         int64 nBalance = 0;
743         for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
744         {
745             const CWalletTx& wtx = (*it).second;
746             if (!wtx.IsFinal())
747                 continue;
748
749             int64 allGeneratedImmature, allGeneratedMature, allFee;
750             allGeneratedImmature = allGeneratedMature = allFee = 0;
751             string strSentAccount;
752             list<pair<CBitcoinAddress, int64> > listReceived;
753             list<pair<CBitcoinAddress, int64> > listSent;
754             wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
755             if (wtx.GetDepthInMainChain() >= nMinDepth)
756                 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
757                     nBalance += r.second;
758             BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
759                 nBalance -= r.second;
760             nBalance -= allFee;
761             nBalance += allGeneratedMature;
762         }
763         return  ValueFromAmount(nBalance);
764     }
765
766     string strAccount = AccountFromValue(params[0]);
767
768     int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
769
770     return ValueFromAmount(nBalance);
771 }
772
773
774 Value movecmd(const Array& params, bool fHelp)
775 {
776     if (fHelp || params.size() < 3 || params.size() > 5)
777         throw runtime_error(
778             "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
779             "Move from one account in your wallet to another.");
780
781     string strFrom = AccountFromValue(params[0]);
782     string strTo = AccountFromValue(params[1]);
783     int64 nAmount = AmountFromValue(params[2]);
784     if (params.size() > 3)
785         // unused parameter, used to be nMinDepth, keep type-checking it though
786         (void)params[3].get_int();
787     string strComment;
788     if (params.size() > 4)
789         strComment = params[4].get_str();
790
791     CWalletDB walletdb(pwalletMain->strWalletFile);
792     walletdb.TxnBegin();
793
794     int64 nNow = GetAdjustedTime();
795
796     // Debit
797     CAccountingEntry debit;
798     debit.strAccount = strFrom;
799     debit.nCreditDebit = -nAmount;
800     debit.nTime = nNow;
801     debit.strOtherAccount = strTo;
802     debit.strComment = strComment;
803     walletdb.WriteAccountingEntry(debit);
804
805     // Credit
806     CAccountingEntry credit;
807     credit.strAccount = strTo;
808     credit.nCreditDebit = nAmount;
809     credit.nTime = nNow;
810     credit.strOtherAccount = strFrom;
811     credit.strComment = strComment;
812     walletdb.WriteAccountingEntry(credit);
813
814     walletdb.TxnCommit();
815
816     return true;
817 }
818
819
820 Value sendfrom(const Array& params, bool fHelp)
821 {
822     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
823         throw runtime_error(
824             "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
825             "<amount> is a real and is rounded to the nearest 0.000001\n"
826             "requires wallet passphrase to be set with walletpassphrase first");
827     if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
828         throw runtime_error(
829             "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
830             "<amount> is a real and is rounded to the nearest 0.000001");
831
832     string strAccount = AccountFromValue(params[0]);
833     CBitcoinAddress address(params[1].get_str());
834     if (!address.IsValid())
835         throw JSONRPCError(-5, "Invalid ppcoin address");
836     int64 nAmount = AmountFromValue(params[2]);
837     int nMinDepth = 1;
838     if (params.size() > 3)
839         nMinDepth = params[3].get_int();
840
841     CWalletTx wtx;
842     wtx.strFromAccount = strAccount;
843     if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
844         wtx.mapValue["comment"] = params[4].get_str();
845     if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
846         wtx.mapValue["to"]      = params[5].get_str();
847
848     if (pwalletMain->IsLocked())
849         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
850
851     // Check funds
852     int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
853     if (nAmount > nBalance)
854         throw JSONRPCError(-6, "Account has insufficient funds");
855
856     // Send
857     string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
858     if (strError != "")
859         throw JSONRPCError(-4, strError);
860
861     return wtx.GetHash().GetHex();
862 }
863
864
865 Value sendmany(const Array& params, bool fHelp)
866 {
867     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
868         throw runtime_error(
869             "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
870             "amounts are double-precision floating point numbers\n"
871             "requires wallet passphrase to be set with walletpassphrase first");
872     if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
873         throw runtime_error(
874             "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
875             "amounts are double-precision floating point numbers");
876
877     string strAccount = AccountFromValue(params[0]);
878     Object sendTo = params[1].get_obj();
879     int nMinDepth = 1;
880     if (params.size() > 2)
881         nMinDepth = params[2].get_int();
882
883     CWalletTx wtx;
884     wtx.strFromAccount = strAccount;
885     if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
886         wtx.mapValue["comment"] = params[3].get_str();
887
888     set<CBitcoinAddress> setAddress;
889     vector<pair<CScript, int64> > vecSend;
890
891     int64 totalAmount = 0;
892     BOOST_FOREACH(const Pair& s, sendTo)
893     {
894         CBitcoinAddress address(s.name_);
895         if (!address.IsValid())
896             throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
897
898         if (setAddress.count(address))
899             throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
900         setAddress.insert(address);
901
902         CScript scriptPubKey;
903         scriptPubKey.SetBitcoinAddress(address);
904         int64 nAmount = AmountFromValue(s.value_); 
905         totalAmount += nAmount;
906
907         vecSend.push_back(make_pair(scriptPubKey, nAmount));
908     }
909
910     if (pwalletMain->IsLocked())
911         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
912     if (fWalletUnlockStakeOnly)
913         throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
914
915     // Check funds
916     int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
917     if (totalAmount > nBalance)
918         throw JSONRPCError(-6, "Account has insufficient funds");
919
920     // Send
921     CReserveKey keyChange(pwalletMain);
922     int64 nFeeRequired = 0;
923     bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
924     if (!fCreated)
925     {
926         if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
927             throw JSONRPCError(-6, "Insufficient funds");
928         throw JSONRPCError(-4, "Transaction creation failed");
929     }
930     if (!pwalletMain->CommitTransaction(wtx, keyChange))
931         throw JSONRPCError(-4, "Transaction commit failed");
932
933     return wtx.GetHash().GetHex();
934 }
935
936
937 struct tallyitem
938 {
939     int64 nAmount;
940     int nConf;
941     tallyitem()
942     {
943         nAmount = 0;
944         nConf = INT_MAX;
945     }
946 };
947
948 Value ListReceived(const Array& params, bool fByAccounts)
949 {
950     // Minimum confirmations
951     int nMinDepth = 1;
952     if (params.size() > 0)
953         nMinDepth = params[0].get_int();
954
955     // Whether to include empty accounts
956     bool fIncludeEmpty = false;
957     if (params.size() > 1)
958         fIncludeEmpty = params[1].get_bool();
959
960     // Tally
961     map<CBitcoinAddress, tallyitem> mapTally;
962     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
963     {
964         const CWalletTx& wtx = (*it).second;
965         if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
966             continue;
967
968         int nDepth = wtx.GetDepthInMainChain();
969         if (nDepth < nMinDepth)
970             continue;
971
972         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
973         {
974             CBitcoinAddress address;
975             if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
976                 continue;
977
978             tallyitem& item = mapTally[address];
979             item.nAmount += txout.nValue;
980             item.nConf = min(item.nConf, nDepth);
981         }
982     }
983
984     // Reply
985     Array ret;
986     map<string, tallyitem> mapAccountTally;
987     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
988     {
989         const CBitcoinAddress& address = item.first;
990         const string& strAccount = item.second;
991         map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
992         if (it == mapTally.end() && !fIncludeEmpty)
993             continue;
994
995         int64 nAmount = 0;
996         int nConf = INT_MAX;
997         if (it != mapTally.end())
998         {
999             nAmount = (*it).second.nAmount;
1000             nConf = (*it).second.nConf;
1001         }
1002
1003         if (fByAccounts)
1004         {
1005             tallyitem& item = mapAccountTally[strAccount];
1006             item.nAmount += nAmount;
1007             item.nConf = min(item.nConf, nConf);
1008         }
1009         else
1010         {
1011             Object obj;
1012             obj.push_back(Pair("address",       address.ToString()));
1013             obj.push_back(Pair("account",       strAccount));
1014             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
1015             obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1016             ret.push_back(obj);
1017         }
1018     }
1019
1020     if (fByAccounts)
1021     {
1022         for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1023         {
1024             int64 nAmount = (*it).second.nAmount;
1025             int nConf = (*it).second.nConf;
1026             Object obj;
1027             obj.push_back(Pair("account",       (*it).first));
1028             obj.push_back(Pair("amount",        ValueFromAmount(nAmount)));
1029             obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1030             ret.push_back(obj);
1031         }
1032     }
1033
1034     return ret;
1035 }
1036
1037 Value listreceivedbyaddress(const Array& params, bool fHelp)
1038 {
1039     if (fHelp || params.size() > 2)
1040         throw runtime_error(
1041             "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1042             "[minconf] is the minimum number of confirmations before payments are included.\n"
1043             "[includeempty] whether to include addresses that haven't received any payments.\n"
1044             "Returns an array of objects containing:\n"
1045             "  \"address\" : receiving address\n"
1046             "  \"account\" : the account of the receiving address\n"
1047             "  \"amount\" : total amount received by the address\n"
1048             "  \"confirmations\" : number of confirmations of the most recent transaction included");
1049
1050     return ListReceived(params, false);
1051 }
1052
1053 Value listreceivedbyaccount(const Array& params, bool fHelp)
1054 {
1055     if (fHelp || params.size() > 2)
1056         throw runtime_error(
1057             "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1058             "[minconf] is the minimum number of confirmations before payments are included.\n"
1059             "[includeempty] whether to include accounts that haven't received any payments.\n"
1060             "Returns an array of objects containing:\n"
1061             "  \"account\" : the account of the receiving addresses\n"
1062             "  \"amount\" : total amount received by addresses with this account\n"
1063             "  \"confirmations\" : number of confirmations of the most recent transaction included");
1064
1065     return ListReceived(params, true);
1066 }
1067
1068 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1069 {
1070     int64 nGeneratedImmature, nGeneratedMature, nFee;
1071     string strSentAccount;
1072     list<pair<CBitcoinAddress, int64> > listReceived;
1073     list<pair<CBitcoinAddress, int64> > listSent;
1074     wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1075
1076     bool fAllAccounts = (strAccount == string("*"));
1077
1078     // Generated blocks assigned to account ""
1079     if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1080     {
1081         Object entry;
1082         entry.push_back(Pair("account", string("")));
1083         if (nGeneratedImmature)
1084         {
1085             entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1086             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1087         }
1088         else
1089         {
1090             entry.push_back(Pair("category", "generate"));
1091             entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1092         }
1093         if (fLong)
1094             WalletTxToJSON(wtx, entry);
1095         ret.push_back(entry);
1096     }
1097
1098     // Sent
1099     if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1100     {
1101         BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1102         {
1103             Object entry;
1104             entry.push_back(Pair("account", strSentAccount));
1105             entry.push_back(Pair("address", s.first.ToString()));
1106             entry.push_back(Pair("category", "send"));
1107             entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1108             entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1109             if (fLong)
1110                 WalletTxToJSON(wtx, entry);
1111             ret.push_back(entry);
1112         }
1113     }
1114
1115     // Received
1116     if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1117         BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1118         {
1119             string account;
1120             if (pwalletMain->mapAddressBook.count(r.first))
1121                 account = pwalletMain->mapAddressBook[r.first];
1122             if (fAllAccounts || (account == strAccount))
1123             {
1124                 Object entry;
1125                 entry.push_back(Pair("account", account));
1126                 entry.push_back(Pair("address", r.first.ToString()));
1127                 entry.push_back(Pair("category", "receive"));
1128                 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1129                 if (fLong)
1130                     WalletTxToJSON(wtx, entry);
1131                 ret.push_back(entry);
1132             }
1133         }
1134 }
1135
1136 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1137 {
1138     bool fAllAccounts = (strAccount == string("*"));
1139
1140     if (fAllAccounts || acentry.strAccount == strAccount)
1141     {
1142         Object entry;
1143         entry.push_back(Pair("account", acentry.strAccount));
1144         entry.push_back(Pair("category", "move"));
1145         entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1146         entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1147         entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1148         entry.push_back(Pair("comment", acentry.strComment));
1149         ret.push_back(entry);
1150     }
1151 }
1152
1153 Value listtransactions(const Array& params, bool fHelp)
1154 {
1155     if (fHelp || params.size() > 3)
1156         throw runtime_error(
1157             "listtransactions [account] [count=10] [from=0]\n"
1158             "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1159
1160     string strAccount = "*";
1161     if (params.size() > 0)
1162         strAccount = params[0].get_str();
1163     int nCount = 10;
1164     if (params.size() > 1)
1165         nCount = params[1].get_int();
1166     int nFrom = 0;
1167     if (params.size() > 2)
1168         nFrom = params[2].get_int();
1169
1170     Array ret;
1171     CWalletDB walletdb(pwalletMain->strWalletFile);
1172
1173     // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1174     typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1175     typedef multimap<int64, TxPair > TxItems;
1176     TxItems txByTime;
1177
1178     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1179     {
1180         CWalletTx* wtx = &((*it).second);
1181         txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1182     }
1183     list<CAccountingEntry> acentries;
1184     walletdb.ListAccountCreditDebit(strAccount, acentries);
1185     BOOST_FOREACH(CAccountingEntry& entry, acentries)
1186     {
1187         txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1188     }
1189
1190     // Now: iterate backwards until we have nCount items to return:
1191     TxItems::reverse_iterator it = txByTime.rbegin();
1192     if (txByTime.size() > nFrom) std::advance(it, nFrom);
1193     for (; it != txByTime.rend(); ++it)
1194     {
1195         CWalletTx *const pwtx = (*it).second.first;
1196         if (pwtx != 0)
1197             ListTransactions(*pwtx, strAccount, 0, true, ret);
1198         CAccountingEntry *const pacentry = (*it).second.second;
1199         if (pacentry != 0)
1200             AcentryToJSON(*pacentry, strAccount, ret);
1201
1202         if (ret.size() >= nCount) break;
1203     }
1204     // ret is now newest to oldest
1205     
1206     // Make sure we return only last nCount items (sends-to-self might give us an extra):
1207     if (ret.size() > nCount)
1208     {
1209         Array::iterator last = ret.begin();
1210         std::advance(last, nCount);
1211         ret.erase(last, ret.end());
1212     }
1213     std::reverse(ret.begin(), ret.end()); // oldest to newest
1214
1215     return ret;
1216 }
1217
1218 Value listaccounts(const Array& params, bool fHelp)
1219 {
1220     if (fHelp || params.size() > 1)
1221         throw runtime_error(
1222             "listaccounts [minconf=1]\n"
1223             "Returns Object that has account names as keys, account balances as values.");
1224
1225     int nMinDepth = 1;
1226     if (params.size() > 0)
1227         nMinDepth = params[0].get_int();
1228
1229     map<string, int64> mapAccountBalances;
1230     BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1231         if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1232             mapAccountBalances[entry.second] = 0;
1233     }
1234
1235     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1236     {
1237         const CWalletTx& wtx = (*it).second;
1238         int64 nGeneratedImmature, nGeneratedMature, nFee;
1239         string strSentAccount;
1240         list<pair<CBitcoinAddress, int64> > listReceived;
1241         list<pair<CBitcoinAddress, int64> > listSent;
1242         wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1243         mapAccountBalances[strSentAccount] -= nFee;
1244         BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1245             mapAccountBalances[strSentAccount] -= s.second;
1246         if (wtx.GetDepthInMainChain() >= nMinDepth)
1247         {
1248             mapAccountBalances[""] += nGeneratedMature;
1249             BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1250                 if (pwalletMain->mapAddressBook.count(r.first))
1251                     mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1252                 else
1253                     mapAccountBalances[""] += r.second;
1254         }
1255     }
1256
1257     list<CAccountingEntry> acentries;
1258     CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1259     BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1260         mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1261
1262     Object ret;
1263     BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1264         ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1265     }
1266     return ret;
1267 }
1268
1269 Value listsinceblock(const Array& params, bool fHelp)
1270 {
1271     if (fHelp)
1272         throw runtime_error(
1273             "listsinceblock [blockid] [target-confirmations]\n"
1274             "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1275
1276     CBlockIndex *pindex = NULL;
1277     int target_confirms = 1;
1278
1279     if (params.size() > 0)
1280     {
1281         uint256 blockId = 0;
1282
1283         blockId.SetHex(params[0].get_str());
1284         pindex = CBlockLocator(blockId).GetBlockIndex();
1285     }
1286
1287     if (params.size() > 1)
1288     {
1289         target_confirms = params[1].get_int();
1290
1291         if (target_confirms < 1)
1292             throw JSONRPCError(-8, "Invalid parameter");
1293     }
1294
1295     int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1296
1297     Array transactions;
1298
1299     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1300     {
1301         CWalletTx tx = (*it).second;
1302
1303         if (depth == -1 || tx.GetDepthInMainChain() < depth)
1304             ListTransactions(tx, "*", 0, true, transactions);
1305     }
1306
1307     uint256 lastblock;
1308
1309     if (target_confirms == 1)
1310     {
1311         printf("oops!\n");
1312         lastblock = hashBestChain;
1313     }
1314     else
1315     {
1316         int target_height = pindexBest->nHeight + 1 - target_confirms;
1317
1318         CBlockIndex *block;
1319         for (block = pindexBest;
1320              block && block->nHeight > target_height;
1321              block = block->pprev);
1322
1323         lastblock = block ? block->GetBlockHash() : 0;
1324     }
1325
1326     Object ret;
1327     ret.push_back(Pair("transactions", transactions));
1328     ret.push_back(Pair("lastblock", lastblock.GetHex()));
1329
1330     return ret;
1331 }
1332
1333 Value gettransaction(const Array& params, bool fHelp)
1334 {
1335     if (fHelp || params.size() != 1)
1336         throw runtime_error(
1337             "gettransaction <txid>\n"
1338             "Get detailed information about <txid>");
1339
1340     uint256 hash;
1341     hash.SetHex(params[0].get_str());
1342
1343     Object entry;
1344
1345     if (!pwalletMain->mapWallet.count(hash))
1346         throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1347     const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1348
1349     int64 nCredit = wtx.GetCredit();
1350     int64 nDebit = wtx.GetDebit();
1351     int64 nNet = nCredit - nDebit;
1352     int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1353
1354     entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1355     if (wtx.IsFromMe())
1356         entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1357
1358     WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1359
1360     Array details;
1361     ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1362     entry.push_back(Pair("details", details));
1363
1364     return entry;
1365 }
1366
1367
1368 Value backupwallet(const Array& params, bool fHelp)
1369 {
1370     if (fHelp || params.size() != 1)
1371         throw runtime_error(
1372             "backupwallet <destination>\n"
1373             "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1374
1375     string strDest = params[0].get_str();
1376     BackupWallet(*pwalletMain, strDest);
1377
1378     return Value::null;
1379 }
1380
1381
1382 Value keypoolrefill(const Array& params, bool fHelp)
1383 {
1384     if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1385         throw runtime_error(
1386             "keypoolrefill\n"
1387             "Fills the keypool, requires wallet passphrase to be set.");
1388     if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1389         throw runtime_error(
1390             "keypoolrefill\n"
1391             "Fills the keypool.");
1392
1393     if (pwalletMain->IsLocked())
1394         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1395
1396     pwalletMain->TopUpKeyPool();
1397
1398     if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1399         throw JSONRPCError(-4, "Error refreshing keypool.");
1400
1401     return Value::null;
1402 }
1403
1404
1405 void ThreadTopUpKeyPool(void* parg)
1406 {
1407     pwalletMain->TopUpKeyPool();
1408 }
1409
1410 void ThreadCleanWalletPassphrase(void* parg)
1411 {
1412     int64 nMyWakeTime = GetTime() + *((int*)parg);
1413
1414     if (nWalletUnlockTime == 0)
1415     {
1416         CRITICAL_BLOCK(cs_nWalletUnlockTime)
1417         {
1418             nWalletUnlockTime = nMyWakeTime;
1419         }
1420
1421         while (GetTime() < nWalletUnlockTime)
1422             Sleep(GetTime() - nWalletUnlockTime);
1423
1424         CRITICAL_BLOCK(cs_nWalletUnlockTime)
1425         {
1426             nWalletUnlockTime = 0;
1427         }
1428     }
1429     else
1430     {
1431         CRITICAL_BLOCK(cs_nWalletUnlockTime)
1432         {
1433             if (nWalletUnlockTime < nMyWakeTime)
1434                 nWalletUnlockTime = nMyWakeTime;
1435         }
1436         free(parg);
1437         return;
1438     }
1439
1440     pwalletMain->Lock();
1441
1442     delete (int*)parg;
1443 }
1444
1445 Value walletpassphrase(const Array& params, bool fHelp)
1446 {
1447     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1448         throw runtime_error(
1449             "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1450             "Stores the wallet decryption key in memory for <timeout> seconds."
1451             "stakeonly is an optional true/false allowing only stake creation.");
1452     if (fHelp)
1453         return true;
1454     if (!pwalletMain->IsCrypted())
1455         throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1456
1457     if (!pwalletMain->IsLocked())
1458         throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1459
1460     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1461     SecureString strWalletPass;
1462     strWalletPass.reserve(100);
1463     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1464     // Alternately, find a way to make params[0] mlock()'d to begin with.
1465     strWalletPass = params[0].get_str().c_str();
1466
1467     if (strWalletPass.length() > 0)
1468     {
1469         if (!pwalletMain->Unlock(strWalletPass))
1470             throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1471     }
1472     else
1473         throw runtime_error(
1474             "walletpassphrase <passphrase> <timeout>\n"
1475             "Stores the wallet decryption key in memory for <timeout> seconds.");
1476
1477     CreateThread(ThreadTopUpKeyPool, NULL);
1478     int* pnSleepTime = new int(params[1].get_int());
1479     CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1480
1481     // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1482     if (params.size() > 2)
1483         fWalletUnlockStakeOnly = params[2].get_bool();
1484     else
1485         fWalletUnlockStakeOnly = false;
1486
1487     return Value::null;
1488 }
1489
1490
1491 Value walletpassphrasechange(const Array& params, bool fHelp)
1492 {
1493     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1494         throw runtime_error(
1495             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1496             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1497     if (fHelp)
1498         return true;
1499     if (!pwalletMain->IsCrypted())
1500         throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1501
1502     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1503     // Alternately, find a way to make params[0] mlock()'d to begin with.
1504     SecureString strOldWalletPass;
1505     strOldWalletPass.reserve(100);
1506     strOldWalletPass = params[0].get_str().c_str();
1507
1508     SecureString strNewWalletPass;
1509     strNewWalletPass.reserve(100);
1510     strNewWalletPass = params[1].get_str().c_str();
1511
1512     if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1513         throw runtime_error(
1514             "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1515             "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1516
1517     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1518         throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1519
1520     return Value::null;
1521 }
1522
1523
1524 Value walletlock(const Array& params, bool fHelp)
1525 {
1526     if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1527         throw runtime_error(
1528             "walletlock\n"
1529             "Removes the wallet encryption key from memory, locking the wallet.\n"
1530             "After calling this method, you will need to call walletpassphrase again\n"
1531             "before being able to call any methods which require the wallet to be unlocked.");
1532     if (fHelp)
1533         return true;
1534     if (!pwalletMain->IsCrypted())
1535         throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1536
1537     pwalletMain->Lock();
1538     CRITICAL_BLOCK(cs_nWalletUnlockTime)
1539     {
1540         nWalletUnlockTime = 0;
1541     }
1542
1543     return Value::null;
1544 }
1545
1546
1547 Value encryptwallet(const Array& params, bool fHelp)
1548 {
1549     if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1550         throw runtime_error(
1551             "encryptwallet <passphrase>\n"
1552             "Encrypts the wallet with <passphrase>.");
1553     if (fHelp)
1554         return true;
1555     if (pwalletMain->IsCrypted())
1556         throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1557
1558 #ifdef QT_GUI
1559     // shutting down via RPC while the GUI is running does not work (yet):
1560     throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1561 #endif
1562
1563     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1564     // Alternately, find a way to make params[0] mlock()'d to begin with.
1565     SecureString strWalletPass;
1566     strWalletPass.reserve(100);
1567     strWalletPass = params[0].get_str().c_str();
1568
1569     if (strWalletPass.length() < 1)
1570         throw runtime_error(
1571             "encryptwallet <passphrase>\n"
1572             "Encrypts the wallet with <passphrase>.");
1573
1574     if (!pwalletMain->EncryptWallet(strWalletPass))
1575         throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1576
1577     // BDB seems to have a bad habit of writing old data into
1578     // slack space in .dat files; that is bad if the old data is
1579     // unencrypted private keys.  So:
1580     CreateThread(Shutdown, NULL);
1581     return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1582 }
1583
1584
1585 Value validateaddress(const Array& params, bool fHelp)
1586 {
1587     if (fHelp || params.size() != 1)
1588         throw runtime_error(
1589             "validateaddress <ppcoinaddress>\n"
1590             "Return information about <ppcoinaddress>.");
1591
1592     CBitcoinAddress address(params[0].get_str());
1593     bool isValid = address.IsValid();
1594
1595     Object ret;
1596     ret.push_back(Pair("isvalid", isValid));
1597     if (isValid)
1598     {
1599         // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1600         // version of the address:
1601         string currentAddress = address.ToString();
1602         ret.push_back(Pair("address", currentAddress));
1603         ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1604         if (pwalletMain->mapAddressBook.count(address))
1605             ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1606     }
1607     return ret;
1608 }
1609
1610
1611 Value getwork(const Array& params, bool fHelp)
1612 {
1613     if (fHelp || params.size() > 1)
1614         throw runtime_error(
1615             "getwork [data]\n"
1616             "If [data] is not specified, returns formatted hash data to work on:\n"
1617             "  \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1618             "  \"data\" : block data\n"
1619             "  \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1620             "  \"target\" : little endian hash target\n"
1621             "If [data] is specified, tries to solve the block and returns true if it was successful.");
1622
1623     if (vNodes.empty())
1624         throw JSONRPCError(-9, "PPCoin is not connected!");
1625
1626     if (IsInitialBlockDownload())
1627         throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1628
1629     typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1630     static mapNewBlock_t mapNewBlock;
1631     static vector<CBlock*> vNewBlock;
1632     static CReserveKey reservekey(pwalletMain);
1633
1634     if (params.size() == 0)
1635     {
1636         // Update block
1637         static unsigned int nTransactionsUpdatedLast;
1638         static CBlockIndex* pindexPrev;
1639         static int64 nStart;
1640         static CBlock* pblock;
1641         if (pindexPrev != pindexBest ||
1642             (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1643         {
1644             if (pindexPrev != pindexBest)
1645             {
1646                 // Deallocate old blocks since they're obsolete now
1647                 mapNewBlock.clear();
1648                 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1649                     delete pblock;
1650                 vNewBlock.clear();
1651             }
1652             nTransactionsUpdatedLast = nTransactionsUpdated;
1653             pindexPrev = pindexBest;
1654             nStart = GetTime();
1655
1656             // Create new block
1657             pblock = CreateNewBlock(pwalletMain);
1658             if (!pblock)
1659                 throw JSONRPCError(-7, "Out of memory");
1660             vNewBlock.push_back(pblock);
1661         }
1662
1663         // Update nTime
1664         pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1665         pblock->nNonce = 0;
1666
1667         // Update nExtraNonce
1668         static unsigned int nExtraNonce = 0;
1669         IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1670
1671         // Save
1672         mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1673
1674         // Prebuild hash buffers
1675         char pmidstate[32];
1676         char pdata[128];
1677         char phash1[64];
1678         FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1679
1680         uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1681
1682         Object result;
1683         result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1684         result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
1685         result.push_back(Pair("hash1",    HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1686         result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));
1687         return result;
1688     }
1689     else
1690     {
1691         // Parse parameters
1692         vector<unsigned char> vchData = ParseHex(params[0].get_str());
1693         if (vchData.size() != 128)
1694             throw JSONRPCError(-8, "Invalid parameter");
1695         CBlock* pdata = (CBlock*)&vchData[0];
1696
1697         // Byte reverse
1698         for (int i = 0; i < 128/4; i++)
1699             ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1700
1701         // Get saved block
1702         if (!mapNewBlock.count(pdata->hashMerkleRoot))
1703             return false;
1704         CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1705
1706         pblock->nTime = pdata->nTime;
1707         pblock->nNonce = pdata->nNonce;
1708         pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1709         pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1710
1711         return CheckWork(pblock, *pwalletMain, reservekey);
1712     }
1713 }
1714
1715
1716 Value getmemorypool(const Array& params, bool fHelp)
1717 {
1718     if (fHelp || params.size() > 1)
1719         throw runtime_error(
1720             "getmemorypool [data]\n"
1721             "If [data] is not specified, returns data needed to construct a block to work on:\n"
1722             "  \"version\" : block version\n"
1723             "  \"previousblockhash\" : hash of current highest block\n"
1724             "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1725             "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1726             "  \"time\" : timestamp appropriate for next block\n"
1727             "  \"bits\" : compressed target of next block\n"
1728             "If [data] is specified, tries to solve the block and returns true if it was successful.");
1729
1730     if (params.size() == 0)
1731     {
1732         if (vNodes.empty())
1733             throw JSONRPCError(-9, "PPCoin is not connected!");
1734
1735         if (IsInitialBlockDownload())
1736             throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1737
1738         static CReserveKey reservekey(pwalletMain);
1739
1740         // Update block
1741         static unsigned int nTransactionsUpdatedLast;
1742         static CBlockIndex* pindexPrev;
1743         static int64 nStart;
1744         static CBlock* pblock;
1745         if (pindexPrev != pindexBest ||
1746             (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1747         {
1748             nTransactionsUpdatedLast = nTransactionsUpdated;
1749             pindexPrev = pindexBest;
1750             nStart = GetTime();
1751
1752             // Create new block
1753             if(pblock)
1754                 delete pblock;
1755             pblock = CreateNewBlock(pwalletMain);
1756             if (!pblock)
1757                 throw JSONRPCError(-7, "Out of memory");
1758         }
1759
1760         // Update nTime
1761         pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1762         pblock->nNonce = 0;
1763
1764         Array transactions;
1765         BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1766             if(tx.IsCoinBase() || tx.IsCoinStake())
1767                 continue;
1768
1769             CDataStream ssTx;
1770             ssTx << tx;
1771
1772             transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1773         }
1774
1775         Object result;
1776         result.push_back(Pair("version", pblock->nVersion));
1777         result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1778         result.push_back(Pair("transactions", transactions));
1779         result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1780         result.push_back(Pair("time", (int64_t)pblock->nTime));
1781
1782         union {
1783             int32_t nBits;
1784             char cBits[4];
1785         } uBits;
1786         uBits.nBits = htonl((int32_t)pblock->nBits);
1787         result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1788
1789         return result;
1790     }
1791     else
1792     {
1793         // Parse parameters
1794         CDataStream ssBlock(ParseHex(params[0].get_str()));
1795         CBlock pblock;
1796         ssBlock >> pblock;
1797
1798         return ProcessBlock(NULL, &pblock);
1799     }
1800 }
1801
1802
1803 // ppcoin: reset auto checkpoint
1804 Value resetcheckpoint(const Array& params, bool fHelp)
1805 {
1806     if (fHelp || params.size() < 1 || params.size() > 1)
1807         throw runtime_error(
1808             "resetcheckpoint <checkpointheight>\n"
1809             "Reset automatic checkpoint to specified height.\n"
1810             "<checkpointheight> is the height of the new checkpoint block.\n");
1811
1812     int nCheckpoint = params[0].get_int();
1813     if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1814         throw runtime_error(
1815             "invalid checkpoint height.\n"
1816         );
1817     if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1818         throw runtime_error(
1819             "new checkpoint must be earlier than current auto checkpoint.\n"
1820         );
1821     if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1822         throw runtime_error(
1823             "internal error - reset checkpoint failed.\n"
1824         );
1825
1826     return Value::null;
1827 }
1828
1829
1830 // ppcoin: get branch point of alternative branch
1831 Value getbranchpoint(const Array& params, bool fHelp)
1832 {
1833     if (fHelp || params.size() != 0)
1834         throw runtime_error(
1835             "getbranchpoint\n"
1836             "Returns height of branch point of alternative branch.\n");
1837
1838     Object result;
1839     if (Checkpoints::nBranchPoint > 0)
1840         result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1841     else
1842         result.push_back(Pair("branchpoint", "none"));
1843     result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1844     return result;
1845 }
1846
1847
1848 // ppcoin: reserve balance from being staked for network protection
1849 Value reservebalance(const Array& params, bool fHelp)
1850 {
1851     if (fHelp || params.size() > 2)
1852         throw runtime_error(
1853             "reservebalance [<reserve> [amount]]\n"
1854             "<reserve> is true or false to turn balance reserve on or off.\n"
1855             "<amount> is a real and rounded to cent.\n"
1856             "Set reserve amount not participating in network protection.\n"
1857             "If no parameters provided current setting is printed.\n");
1858
1859     if (params.size() > 0)
1860     {
1861         bool fReserve = params[0].get_bool();
1862         if (fReserve)
1863         {
1864             if (params.size() == 1)
1865                 throw runtime_error("must provide amount to reserve balance.\n");
1866             int64 nAmount = AmountFromValue(params[1]);
1867             nAmount = (nAmount / CENT) * CENT;  // round to cent
1868             if (nAmount < 0)
1869                 throw runtime_error("amount cannot be negative.\n");
1870             WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1871         }
1872         else
1873         {
1874             if (params.size() > 1)
1875                 throw runtime_error("cannot specify amount to turn off reserve.\n");
1876             WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1877         }
1878     }
1879
1880     Object result;
1881     result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1882     result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1883     return result;
1884 }
1885
1886
1887
1888
1889
1890
1891 //
1892 // Call Table
1893 //
1894
1895 pair<string, rpcfn_type> pCallTable[] =
1896 {
1897     make_pair("help",                   &help),
1898     make_pair("stop",                   &stop),
1899     make_pair("getblockcount",          &getblockcount),
1900     make_pair("getblocknumber",         &getblocknumber),
1901     make_pair("getconnectioncount",     &getconnectioncount),
1902     make_pair("getdifficulty",          &getdifficulty),
1903     make_pair("getgenerate",            &getgenerate),
1904     make_pair("setgenerate",            &setgenerate),
1905     make_pair("gethashespersec",        &gethashespersec),
1906     make_pair("getinfo",                &getinfo),
1907     make_pair("getnewaddress",          &getnewaddress),
1908     make_pair("getaccountaddress",      &getaccountaddress),
1909     make_pair("setaccount",             &setaccount),
1910     make_pair("getaccount",             &getaccount),
1911     make_pair("getaddressesbyaccount",  &getaddressesbyaccount),
1912     make_pair("sendtoaddress",          &sendtoaddress),
1913     make_pair("getreceivedbyaddress",   &getreceivedbyaddress),
1914     make_pair("getreceivedbyaccount",   &getreceivedbyaccount),
1915     make_pair("listreceivedbyaddress",  &listreceivedbyaddress),
1916     make_pair("listreceivedbyaccount",  &listreceivedbyaccount),
1917     make_pair("backupwallet",           &backupwallet),
1918     make_pair("keypoolrefill",          &keypoolrefill),
1919     make_pair("walletpassphrase",       &walletpassphrase),
1920     make_pair("walletpassphrasechange", &walletpassphrasechange),
1921     make_pair("walletlock",             &walletlock),
1922     make_pair("encryptwallet",          &encryptwallet),
1923     make_pair("validateaddress",        &validateaddress),
1924     make_pair("getbalance",             &getbalance),
1925     make_pair("move",                   &movecmd),
1926     make_pair("sendfrom",               &sendfrom),
1927     make_pair("sendmany",               &sendmany),
1928     make_pair("gettransaction",         &gettransaction),
1929     make_pair("listtransactions",       &listtransactions),
1930     make_pair("signmessage",           &signmessage),
1931     make_pair("verifymessage",         &verifymessage),
1932     make_pair("getwork",                &getwork),
1933     make_pair("listaccounts",           &listaccounts),
1934     make_pair("settxfee",               &settxfee),
1935     make_pair("getmemorypool",          &getmemorypool),
1936     make_pair("listsinceblock",        &listsinceblock),
1937     make_pair("resetcheckpoint",        &resetcheckpoint),
1938     make_pair("getbranchpoint",         &getbranchpoint),
1939     make_pair("reservebalance",         &reservebalance),
1940 };
1941 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1942
1943 string pAllowInSafeMode[] =
1944 {
1945     "help",
1946     "stop",
1947     "getblockcount",
1948     "getblocknumber",  // deprecated
1949     "getconnectioncount",
1950     "getdifficulty",
1951     "getgenerate",
1952     "setgenerate",
1953     "gethashespersec",
1954     "getinfo",
1955     "getnewaddress",
1956     "getaccountaddress",
1957     "getaccount",
1958     "getaddressesbyaccount",
1959     "backupwallet",
1960     "keypoolrefill",
1961     "walletpassphrase",
1962     "walletlock",
1963     "validateaddress",
1964     "getwork",
1965     "getmemorypool",
1966     "resetcheckpoint",
1967     "getbranchpoint",
1968 };
1969 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1970
1971
1972
1973
1974 //
1975 // HTTP protocol
1976 //
1977 // This ain't Apache.  We're just using HTTP header for the length field
1978 // and to be compatible with other JSON-RPC implementations.
1979 //
1980
1981 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1982 {
1983     ostringstream s;
1984     s << "POST / HTTP/1.1\r\n"
1985       << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1986       << "Host: 127.0.0.1\r\n"
1987       << "Content-Type: application/json\r\n"
1988       << "Content-Length: " << strMsg.size() << "\r\n"
1989       << "Connection: close\r\n"
1990       << "Accept: application/json\r\n";
1991     BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1992         s << item.first << ": " << item.second << "\r\n";
1993     s << "\r\n" << strMsg;
1994
1995     return s.str();
1996 }
1997
1998 string rfc1123Time()
1999 {
2000     char buffer[64];
2001     time_t now;
2002     time(&now);
2003     struct tm* now_gmt = gmtime(&now);
2004     string locale(setlocale(LC_TIME, NULL));
2005     setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2006     strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2007     setlocale(LC_TIME, locale.c_str());
2008     return string(buffer);
2009 }
2010
2011 static string HTTPReply(int nStatus, const string& strMsg)
2012 {
2013     if (nStatus == 401)
2014         return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2015             "Date: %s\r\n"
2016             "Server: ppcoin-json-rpc/%s\r\n"
2017             "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2018             "Content-Type: text/html\r\n"
2019             "Content-Length: 296\r\n"
2020             "\r\n"
2021             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2022             "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2023             "<HTML>\r\n"
2024             "<HEAD>\r\n"
2025             "<TITLE>Error</TITLE>\r\n"
2026             "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2027             "</HEAD>\r\n"
2028             "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2029             "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2030     const char *cStatus;
2031          if (nStatus == 200) cStatus = "OK";
2032     else if (nStatus == 400) cStatus = "Bad Request";
2033     else if (nStatus == 403) cStatus = "Forbidden";
2034     else if (nStatus == 404) cStatus = "Not Found";
2035     else if (nStatus == 500) cStatus = "Internal Server Error";
2036     else cStatus = "";
2037     return strprintf(
2038             "HTTP/1.1 %d %s\r\n"
2039             "Date: %s\r\n"
2040             "Connection: close\r\n"
2041             "Content-Length: %d\r\n"
2042             "Content-Type: application/json\r\n"
2043             "Server: ppcoin-json-rpc/%s\r\n"
2044             "\r\n"
2045             "%s",
2046         nStatus,
2047         cStatus,
2048         rfc1123Time().c_str(),
2049         strMsg.size(),
2050         FormatFullVersion().c_str(),
2051         strMsg.c_str());
2052 }
2053
2054 int ReadHTTPStatus(std::basic_istream<char>& stream)
2055 {
2056     string str;
2057     getline(stream, str);
2058     vector<string> vWords;
2059     boost::split(vWords, str, boost::is_any_of(" "));
2060     if (vWords.size() < 2)
2061         return 500;
2062     return atoi(vWords[1].c_str());
2063 }
2064
2065 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2066 {
2067     int nLen = 0;
2068     loop
2069     {
2070         string str;
2071         std::getline(stream, str);
2072         if (str.empty() || str == "\r")
2073             break;
2074         string::size_type nColon = str.find(":");
2075         if (nColon != string::npos)
2076         {
2077             string strHeader = str.substr(0, nColon);
2078             boost::trim(strHeader);
2079             boost::to_lower(strHeader);
2080             string strValue = str.substr(nColon+1);
2081             boost::trim(strValue);
2082             mapHeadersRet[strHeader] = strValue;
2083             if (strHeader == "content-length")
2084                 nLen = atoi(strValue.c_str());
2085         }
2086     }
2087     return nLen;
2088 }
2089
2090 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2091 {
2092     mapHeadersRet.clear();
2093     strMessageRet = "";
2094
2095     // Read status
2096     int nStatus = ReadHTTPStatus(stream);
2097
2098     // Read header
2099     int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2100     if (nLen < 0 || nLen > MAX_SIZE)
2101         return 500;
2102
2103     // Read message
2104     if (nLen > 0)
2105     {
2106         vector<char> vch(nLen);
2107         stream.read(&vch[0], nLen);
2108         strMessageRet = string(vch.begin(), vch.end());
2109     }
2110
2111     return nStatus;
2112 }
2113
2114 bool HTTPAuthorized(map<string, string>& mapHeaders)
2115 {
2116     string strAuth = mapHeaders["authorization"];
2117     if (strAuth.substr(0,6) != "Basic ")
2118         return false;
2119     string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2120     string strUserPass = DecodeBase64(strUserPass64);
2121     return strUserPass == strRPCUserColonPass;
2122 }
2123
2124 //
2125 // JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
2126 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2127 // unspecified (HTTP errors and contents of 'error').
2128 //
2129 // 1.0 spec: http://json-rpc.org/wiki/specification
2130 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2131 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2132 //
2133
2134 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2135 {
2136     Object request;
2137     request.push_back(Pair("method", strMethod));
2138     request.push_back(Pair("params", params));
2139     request.push_back(Pair("id", id));
2140     return write_string(Value(request), false) + "\n";
2141 }
2142
2143 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2144 {
2145     Object reply;
2146     if (error.type() != null_type)
2147         reply.push_back(Pair("result", Value::null));
2148     else
2149         reply.push_back(Pair("result", result));
2150     reply.push_back(Pair("error", error));
2151     reply.push_back(Pair("id", id));
2152     return write_string(Value(reply), false) + "\n";
2153 }
2154
2155 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2156 {
2157     // Send error reply from json-rpc error object
2158     int nStatus = 500;
2159     int code = find_value(objError, "code").get_int();
2160     if (code == -32600) nStatus = 400;
2161     else if (code == -32601) nStatus = 404;
2162     string strReply = JSONRPCReply(Value::null, objError, id);
2163     stream << HTTPReply(nStatus, strReply) << std::flush;
2164 }
2165
2166 bool ClientAllowed(const string& strAddress)
2167 {
2168     if (strAddress == asio::ip::address_v4::loopback().to_string())
2169         return true;
2170     const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2171     BOOST_FOREACH(string strAllow, vAllow)
2172         if (WildcardMatch(strAddress, strAllow))
2173             return true;
2174     return false;
2175 }
2176
2177 #ifdef USE_SSL
2178 //
2179 // IOStream device that speaks SSL but can also speak non-SSL
2180 //
2181 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2182 public:
2183     SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2184     {
2185         fUseSSL = fUseSSLIn;
2186         fNeedHandshake = fUseSSLIn;
2187     }
2188
2189     void handshake(ssl::stream_base::handshake_type role)
2190     {
2191         if (!fNeedHandshake) return;
2192         fNeedHandshake = false;
2193         stream.handshake(role);
2194     }
2195     std::streamsize read(char* s, std::streamsize n)
2196     {
2197         handshake(ssl::stream_base::server); // HTTPS servers read first
2198         if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2199         return stream.next_layer().read_some(asio::buffer(s, n));
2200     }
2201     std::streamsize write(const char* s, std::streamsize n)
2202     {
2203         handshake(ssl::stream_base::client); // HTTPS clients write first
2204         if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2205         return asio::write(stream.next_layer(), asio::buffer(s, n));
2206     }
2207     bool connect(const std::string& server, const std::string& port)
2208     {
2209         ip::tcp::resolver resolver(stream.get_io_service());
2210         ip::tcp::resolver::query query(server.c_str(), port.c_str());
2211         ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2212         ip::tcp::resolver::iterator end;
2213         boost::system::error_code error = asio::error::host_not_found;
2214         while (error && endpoint_iterator != end)
2215         {
2216             stream.lowest_layer().close();
2217             stream.lowest_layer().connect(*endpoint_iterator++, error);
2218         }
2219         if (error)
2220             return false;
2221         return true;
2222     }
2223
2224 private:
2225     bool fNeedHandshake;
2226     bool fUseSSL;
2227     SSLStream& stream;
2228 };
2229 #endif
2230
2231 void ThreadRPCServer(void* parg)
2232 {
2233     IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2234     try
2235     {
2236         vnThreadsRunning[4]++;
2237         ThreadRPCServer2(parg);
2238         vnThreadsRunning[4]--;
2239     }
2240     catch (std::exception& e) {
2241         vnThreadsRunning[4]--;
2242         PrintException(&e, "ThreadRPCServer()");
2243     } catch (...) {
2244         vnThreadsRunning[4]--;
2245         PrintException(NULL, "ThreadRPCServer()");
2246     }
2247     printf("ThreadRPCServer exiting\n");
2248 }
2249
2250 void ThreadRPCServer2(void* parg)
2251 {
2252     printf("ThreadRPCServer started\n");
2253
2254     strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2255     if (strRPCUserColonPass == ":")
2256     {
2257         string strWhatAmI = "To use ppcoind";
2258         if (mapArgs.count("-server"))
2259             strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2260         else if (mapArgs.count("-daemon"))
2261             strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2262         PrintConsole(
2263             _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2264               "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2265                 strWhatAmI.c_str(),
2266                 GetConfigFile().c_str());
2267 #ifndef QT_GUI
2268         CreateThread(Shutdown, NULL);
2269 #endif
2270         return;
2271     }
2272
2273     bool fUseSSL = GetBoolArg("-rpcssl");
2274     asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2275
2276     asio::io_service io_service;
2277     ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2278     ip::tcp::acceptor acceptor(io_service, endpoint);
2279
2280     acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2281
2282 #ifdef USE_SSL
2283     ssl::context context(io_service, ssl::context::sslv23);
2284     if (fUseSSL)
2285     {
2286         context.set_options(ssl::context::no_sslv2);
2287         filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2288         if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2289         if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2290         else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2291         filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2292         if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2293         if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2294         else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2295
2296         string ciphers = GetArg("-rpcsslciphers",
2297                                          "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2298         SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2299     }
2300 #else
2301     if (fUseSSL)
2302         throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2303 #endif
2304
2305     loop
2306     {
2307         // Accept connection
2308 #ifdef USE_SSL
2309         SSLStream sslStream(io_service, context);
2310         SSLIOStreamDevice d(sslStream, fUseSSL);
2311         iostreams::stream<SSLIOStreamDevice> stream(d);
2312 #else
2313         ip::tcp::iostream stream;
2314 #endif
2315
2316         ip::tcp::endpoint peer;
2317         vnThreadsRunning[4]--;
2318 #ifdef USE_SSL
2319         acceptor.accept(sslStream.lowest_layer(), peer);
2320 #else
2321         acceptor.accept(*stream.rdbuf(), peer);
2322 #endif
2323         vnThreadsRunning[4]++;
2324         if (fShutdown)
2325             return;
2326
2327         // Restrict callers by IP
2328         if (!ClientAllowed(peer.address().to_string()))
2329         {
2330             // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2331             if (!fUseSSL)
2332                 stream << HTTPReply(403, "") << std::flush;
2333             continue;
2334         }
2335
2336         map<string, string> mapHeaders;
2337         string strRequest;
2338
2339         boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2340         if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2341         {   // Timed out:
2342             acceptor.cancel();
2343             printf("ThreadRPCServer ReadHTTP timeout\n");
2344             continue;
2345         }
2346
2347         // Check authorization
2348         if (mapHeaders.count("authorization") == 0)
2349         {
2350             stream << HTTPReply(401, "") << std::flush;
2351             continue;
2352         }
2353         if (!HTTPAuthorized(mapHeaders))
2354         {
2355             // Deter brute-forcing short passwords
2356             if (mapArgs["-rpcpassword"].size() < 15)
2357                 Sleep(50);
2358
2359             stream << HTTPReply(401, "") << std::flush;
2360             printf("ThreadRPCServer incorrect password attempt\n");
2361             continue;
2362         }
2363
2364         Value id = Value::null;
2365         try
2366         {
2367             // Parse request
2368             Value valRequest;
2369             if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2370                 throw JSONRPCError(-32700, "Parse error");
2371             const Object& request = valRequest.get_obj();
2372
2373             // Parse id now so errors from here on will have the id
2374             id = find_value(request, "id");
2375
2376             // Parse method
2377             Value valMethod = find_value(request, "method");
2378             if (valMethod.type() == null_type)
2379                 throw JSONRPCError(-32600, "Missing method");
2380             if (valMethod.type() != str_type)
2381                 throw JSONRPCError(-32600, "Method must be a string");
2382             string strMethod = valMethod.get_str();
2383             if (strMethod != "getwork" && strMethod != "getmemorypool")
2384                 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2385
2386             // Parse params
2387             Value valParams = find_value(request, "params");
2388             Array params;
2389             if (valParams.type() == array_type)
2390                 params = valParams.get_array();
2391             else if (valParams.type() == null_type)
2392                 params = Array();
2393             else
2394                 throw JSONRPCError(-32600, "Params must be an array");
2395
2396             // Find method
2397             map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2398             if (mi == mapCallTable.end())
2399                 throw JSONRPCError(-32601, "Method not found");
2400
2401             // Observe safe mode
2402             string strWarning = GetWarnings("rpc");
2403             if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2404                 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2405
2406             try
2407             {
2408                 // Execute
2409                 Value result;
2410                 CRITICAL_BLOCK(cs_main)
2411                 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2412                     result = (*(*mi).second)(params, false);
2413
2414                 // Send reply
2415                 string strReply = JSONRPCReply(result, Value::null, id);
2416                 stream << HTTPReply(200, strReply) << std::flush;
2417             }
2418             catch (std::exception& e)
2419             {
2420                 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2421             }
2422         }
2423         catch (Object& objError)
2424         {
2425             ErrorReply(stream, objError, id);
2426         }
2427         catch (std::exception& e)
2428         {
2429             ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2430         }
2431     }
2432 }
2433
2434
2435
2436
2437 Object CallRPC(const string& strMethod, const Array& params)
2438 {
2439     if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2440         throw runtime_error(strprintf(
2441             _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2442               "If the file does not exist, create it with owner-readable-only file permissions."),
2443                 GetConfigFile().c_str()));
2444
2445     // Connect to localhost
2446     bool fUseSSL = GetBoolArg("-rpcssl");
2447 #ifdef USE_SSL
2448     asio::io_service io_service;
2449     ssl::context context(io_service, ssl::context::sslv23);
2450     context.set_options(ssl::context::no_sslv2);
2451     SSLStream sslStream(io_service, context);
2452     SSLIOStreamDevice d(sslStream, fUseSSL);
2453     iostreams::stream<SSLIOStreamDevice> stream(d);
2454     if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2455         throw runtime_error("couldn't connect to server");
2456 #else
2457     if (fUseSSL)
2458         throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2459
2460     ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2461     if (stream.fail())
2462         throw runtime_error("couldn't connect to server");
2463 #endif
2464
2465
2466     // HTTP basic authentication
2467     string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2468     map<string, string> mapRequestHeaders;
2469     mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2470
2471     // Send request
2472     string strRequest = JSONRPCRequest(strMethod, params, 1);
2473     string strPost = HTTPPost(strRequest, mapRequestHeaders);
2474     stream << strPost << std::flush;
2475
2476     // Receive reply
2477     map<string, string> mapHeaders;
2478     string strReply;
2479     int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2480     if (nStatus == 401)
2481         throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2482     else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2483         throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2484     else if (strReply.empty())
2485         throw runtime_error("no response from server");
2486
2487     // Parse reply
2488     Value valReply;
2489     if (!read_string(strReply, valReply))
2490         throw runtime_error("couldn't parse reply from server");
2491     const Object& reply = valReply.get_obj();
2492     if (reply.empty())
2493         throw runtime_error("expected reply to have result, error and id properties");
2494
2495     return reply;
2496 }
2497
2498
2499
2500
2501 template<typename T>
2502 void ConvertTo(Value& value)
2503 {
2504     if (value.type() == str_type)
2505     {
2506         // reinterpret string as unquoted json value
2507         Value value2;
2508         if (!read_string(value.get_str(), value2))
2509             throw runtime_error("type mismatch");
2510         value = value2.get_value<T>();
2511     }
2512     else
2513     {
2514         value = value.get_value<T>();
2515     }
2516 }
2517
2518 int CommandLineRPC(int argc, char *argv[])
2519 {
2520     string strPrint;
2521     int nRet = 0;
2522     try
2523     {
2524         // Skip switches
2525         while (argc > 1 && IsSwitchChar(argv[1][0]))
2526         {
2527             argc--;
2528             argv++;
2529         }
2530
2531         // Method
2532         if (argc < 2)
2533             throw runtime_error("too few parameters");
2534         string strMethod = argv[1];
2535
2536         // Parameters default to strings
2537         Array params;
2538         for (int i = 2; i < argc; i++)
2539             params.push_back(argv[i]);
2540         int n = params.size();
2541
2542         //
2543         // Special case non-string parameter types
2544         //
2545         if (strMethod == "setgenerate"            && n > 0) ConvertTo<bool>(params[0]);
2546         if (strMethod == "setgenerate"            && n > 1) ConvertTo<boost::int64_t>(params[1]);
2547         if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
2548         if (strMethod == "settxfee"               && n > 0) ConvertTo<double>(params[0]);
2549         if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<boost::int64_t>(params[1]);
2550         if (strMethod == "getreceivedbyaccount"   && n > 1) ConvertTo<boost::int64_t>(params[1]);
2551         if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
2552         if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
2553         if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
2554         if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
2555         if (strMethod == "getbalance"             && n > 1) ConvertTo<boost::int64_t>(params[1]);
2556         if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
2557         if (strMethod == "move"                   && n > 3) ConvertTo<boost::int64_t>(params[3]);
2558         if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);
2559         if (strMethod == "sendfrom"               && n > 3) ConvertTo<boost::int64_t>(params[3]);
2560         if (strMethod == "listtransactions"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
2561         if (strMethod == "listtransactions"       && n > 2) ConvertTo<boost::int64_t>(params[2]);
2562         if (strMethod == "listaccounts"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
2563         if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
2564         if (strMethod == "walletpassphrase"       && n > 2) ConvertTo<bool>(params[2]);
2565         if (strMethod == "listsinceblock"         && n > 1) ConvertTo<boost::int64_t>(params[1]);
2566         if (strMethod == "sendmany"               && n > 1)
2567         {
2568             string s = params[1].get_str();
2569             Value v;
2570             if (!read_string(s, v) || v.type() != obj_type)
2571                 throw runtime_error("type mismatch");
2572             params[1] = v.get_obj();
2573         }
2574         if (strMethod == "sendmany"                && n > 2) ConvertTo<boost::int64_t>(params[2]);
2575         if (strMethod == "resetcheckpoint"         && n > 0) ConvertTo<boost::int64_t>(params[0]);
2576         if (strMethod == "reservebalance"          && n > 0) ConvertTo<bool>(params[0]);
2577         if (strMethod == "reservebalance"          && n > 1) ConvertTo<double>(params[1]);
2578
2579         // Execute
2580         Object reply = CallRPC(strMethod, params);
2581
2582         // Parse reply
2583         const Value& result = find_value(reply, "result");
2584         const Value& error  = find_value(reply, "error");
2585
2586         if (error.type() != null_type)
2587         {
2588             // Error
2589             strPrint = "error: " + write_string(error, false);
2590             int code = find_value(error.get_obj(), "code").get_int();
2591             nRet = abs(code);
2592         }
2593         else
2594         {
2595             // Result
2596             if (result.type() == null_type)
2597                 strPrint = "";
2598             else if (result.type() == str_type)
2599                 strPrint = result.get_str();
2600             else
2601                 strPrint = write_string(result, true);
2602         }
2603     }
2604     catch (std::exception& e)
2605     {
2606         strPrint = string("error: ") + e.what();
2607         nRet = 87;
2608     }
2609     catch (...)
2610     {
2611         PrintException(NULL, "CommandLineRPC()");
2612     }
2613
2614     if (strPrint != "")
2615     {
2616         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2617     }
2618     return nRet;
2619 }
2620
2621
2622
2623
2624 #ifdef TEST
2625 int main(int argc, char *argv[])
2626 {
2627 #ifdef _MSC_VER
2628     // Turn off microsoft heap dump noise
2629     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2630     _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2631 #endif
2632     setbuf(stdin, NULL);
2633     setbuf(stdout, NULL);
2634     setbuf(stderr, NULL);
2635
2636     try
2637     {
2638         if (argc >= 2 && string(argv[1]) == "-server")
2639         {
2640             printf("server ready\n");
2641             ThreadRPCServer(NULL);
2642         }
2643         else
2644         {
2645             return CommandLineRPC(argc, argv);
2646         }
2647     }
2648     catch (std::exception& e) {
2649         PrintException(&e, "main()");
2650     } catch (...) {
2651         PrintException(NULL, "main()");
2652     }
2653     return 0;
2654 }
2655 #endif