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