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