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