1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Copyright (c) 2011-2012 The PPCoin developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
12 #include <boost/asio.hpp>
13 #include <boost/iostreams/concepts.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #include <boost/algorithm/string.hpp>
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;
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.
32 using namespace boost;
33 using namespace boost::asio;
34 using namespace json_spirit;
36 void ThreadRPCServer2(void* parg);
37 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
38 extern map<string, rpcfn_type> mapCallTable;
40 static std::string strRPCUserColonPass;
42 static int64 nWalletUnlockTime;
43 static CCriticalSection cs_nWalletUnlockTime;
46 Object JSONRPCError(int code, const string& message)
49 error.push_back(Pair("code", code));
50 error.push_back(Pair("message", message));
55 void PrintConsole(const std::string &format, ...)
58 int limit = sizeof(buffer);
60 va_start(arg_ptr, format);
61 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
63 if (ret < 0 || ret >= limit)
69 fprintf(stdout, "%s", buffer);
73 int64 AmountFromValue(const Value& value)
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");
84 Value ValueFromAmount(int64 amount)
86 return (double)amount / (double)COIN;
89 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
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));
98 string AccountFromValue(const Value& value)
100 string strAccount = value.get_str();
101 if (strAccount == "*")
102 throw JSONRPCError(-11, "Invalid account name");
109 /// Note: This interface may still be subject to change.
113 Value help(const Array& params, bool fHelp)
115 if (fHelp || params.size() > 1)
118 "List commands, or get help for a command.");
121 if (params.size() > 0)
122 strCommand = params[0].get_str();
125 set<rpcfn_type> setDone;
126 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
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))
135 if (strCommand != "" && strMethod != strCommand)
140 rpcfn_type pfn = (*mi).second;
141 if (setDone.insert(pfn).second)
142 (*pfn)(params, true);
144 catch (std::exception& e)
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";
155 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
156 strRet = strRet.substr(0,strRet.size()-1);
161 Value stop(const Array& params, bool fHelp)
163 if (fHelp || params.size() != 0)
166 "Stop ppcoin server.");
168 // Shutdown will take long enough that the response should get back
169 CreateThread(Shutdown, NULL);
170 return "ppcoin server stopping";
172 throw runtime_error("NYI: cannot shut down GUI with RPC command");
177 Value getblockcount(const Array& params, bool fHelp)
179 if (fHelp || params.size() != 0)
182 "Returns the number of blocks in the longest block chain.");
189 Value getblocknumber(const Array& params, bool fHelp)
191 if (fHelp || params.size() != 0)
194 "Deprecated. Use getblockcount.");
200 Value getconnectioncount(const Array& params, bool fHelp)
202 if (fHelp || params.size() != 0)
204 "getconnectioncount\n"
205 "Returns the number of connections to other nodes.");
207 return (int)vNodes.size();
211 double GetDifficulty()
213 // Floating point number that is a multiple of the minimum difficulty,
214 // minimum difficulty = 1.0.
216 if (pindexBest == NULL)
218 int nShift = (pindexBest->nBits >> 24) & 0xff;
221 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
237 Value getdifficulty(const Array& params, bool fHelp)
239 if (fHelp || params.size() != 0)
242 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
244 return GetDifficulty();
248 Value getgenerate(const Array& params, bool fHelp)
250 if (fHelp || params.size() != 0)
253 "Returns true or false.");
255 return (bool)fGenerateBitcoins;
259 Value setgenerate(const Array& params, bool fHelp)
261 if (fHelp || params.size() < 1 || params.size() > 2)
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.");
267 bool fGenerate = true;
268 if (params.size() > 0)
269 fGenerate = params[0].get_bool();
271 if (params.size() > 1)
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)
282 GenerateBitcoins(fGenerate, pwalletMain);
287 Value gethashespersec(const Array& params, bool fHelp)
289 if (fHelp || params.size() != 0)
292 "Returns a recent hashes per second performance measurement while generating.");
294 if (GetTimeMillis() - nHPSTimerStart > 8000)
295 return (boost::int64_t)0;
296 return (boost::int64_t)dHashesPerSec;
300 Value getinfo(const Array& params, bool fHelp)
302 if (fHelp || params.size() != 0)
305 "Returns an object containing various state info.");
308 obj.push_back(Pair("version", (int)VERSION));
309 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
310 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
311 obj.push_back(Pair("blocks", (int)nBestHeight));
312 obj.push_back(Pair("connections", (int)vNodes.size()));
313 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
314 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
315 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
316 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
317 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
318 obj.push_back(Pair("testnet", fTestNet));
319 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
320 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
321 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
322 if (pwalletMain->IsCrypted())
323 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
324 obj.push_back(Pair("errors", GetWarnings("statusbar")));
329 Value getnewaddress(const Array& params, bool fHelp)
331 if (fHelp || params.size() > 1)
333 "getnewaddress [account]\n"
334 "Returns a new ppcoin address for receiving payments. "
335 "If [account] is specified (recommended), it is added to the address book "
336 "so payments received with the address will be credited to [account].");
338 // Parse the account first so we don't generate a key if there's an error
340 if (params.size() > 0)
341 strAccount = AccountFromValue(params[0]);
343 if (!pwalletMain->IsLocked())
344 pwalletMain->TopUpKeyPool();
346 // Generate a new key that is added to wallet
347 std::vector<unsigned char> newKey;
348 if (!pwalletMain->GetKeyFromPool(newKey, false))
349 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
350 CBitcoinAddress address(newKey);
352 pwalletMain->SetAddressBookName(address, strAccount);
354 return address.ToString();
358 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
360 CWalletDB walletdb(pwalletMain->strWalletFile);
363 walletdb.ReadAccount(strAccount, account);
365 bool bKeyUsed = false;
367 // Check if the current key has been used
368 if (!account.vchPubKey.empty())
370 CScript scriptPubKey;
371 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
372 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
373 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
376 const CWalletTx& wtx = (*it).second;
377 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
378 if (txout.scriptPubKey == scriptPubKey)
383 // Generate a new key
384 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
386 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
387 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
389 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
390 walletdb.WriteAccount(strAccount, account);
393 return CBitcoinAddress(account.vchPubKey);
396 Value getaccountaddress(const Array& params, bool fHelp)
398 if (fHelp || params.size() != 1)
400 "getaccountaddress <account>\n"
401 "Returns the current ppcoin address for receiving payments to this account.");
403 // Parse the account first so we don't generate a key if there's an error
404 string strAccount = AccountFromValue(params[0]);
408 ret = GetAccountAddress(strAccount).ToString();
415 Value setaccount(const Array& params, bool fHelp)
417 if (fHelp || params.size() < 1 || params.size() > 2)
419 "setaccount <ppcoinaddress> <account>\n"
420 "Sets the account associated with the given address.");
422 CBitcoinAddress address(params[0].get_str());
423 if (!address.IsValid())
424 throw JSONRPCError(-5, "Invalid ppcoin address");
428 if (params.size() > 1)
429 strAccount = AccountFromValue(params[1]);
431 // Detect when changing the account of an address that is the 'unused current key' of another account:
432 if (pwalletMain->mapAddressBook.count(address))
434 string strOldAccount = pwalletMain->mapAddressBook[address];
435 if (address == GetAccountAddress(strOldAccount))
436 GetAccountAddress(strOldAccount, true);
439 pwalletMain->SetAddressBookName(address, strAccount);
445 Value getaccount(const Array& params, bool fHelp)
447 if (fHelp || params.size() != 1)
449 "getaccount <ppcoinaddress>\n"
450 "Returns the account associated with the given address.");
452 CBitcoinAddress address(params[0].get_str());
453 if (!address.IsValid())
454 throw JSONRPCError(-5, "Invalid ppcoin address");
457 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
458 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
459 strAccount = (*mi).second;
464 Value getaddressesbyaccount(const Array& params, bool fHelp)
466 if (fHelp || params.size() != 1)
468 "getaddressesbyaccount <account>\n"
469 "Returns the list of addresses for the given account.");
471 string strAccount = AccountFromValue(params[0]);
473 // Find all addresses that have the given account
475 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
477 const CBitcoinAddress& address = item.first;
478 const string& strName = item.second;
479 if (strName == strAccount)
480 ret.push_back(address.ToString());
485 Value settxfee(const Array& params, bool fHelp)
487 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
489 "settxfee <amount>\n"
490 "<amount> is a real and is rounded to 0.01 (cent)\n"
491 "Minimum and default transaction fee per KB is 1 cent");
493 nTransactionFee = AmountFromValue(params[0]);
494 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
498 Value sendtoaddress(const Array& params, bool fHelp)
500 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
502 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
503 "<amount> is a real and is rounded to the nearest 0.000001\n"
504 "requires wallet passphrase to be set with walletpassphrase first");
505 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
507 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
508 "<amount> is a real and is rounded to the nearest 0.000001");
510 CBitcoinAddress address(params[0].get_str());
511 if (!address.IsValid())
512 throw JSONRPCError(-5, "Invalid ppcoin address");
515 int64 nAmount = AmountFromValue(params[1]);
519 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
520 wtx.mapValue["comment"] = params[2].get_str();
521 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
522 wtx.mapValue["to"] = params[3].get_str();
524 if (pwalletMain->IsLocked())
525 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
527 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
529 throw JSONRPCError(-4, strError);
531 return wtx.GetHash().GetHex();
534 static const string strMessageMagic = "Bitcoin Signed Message:\n";
536 Value signmessage(const Array& params, bool fHelp)
538 if (fHelp || params.size() != 2)
540 "signmessage <ppcoinaddress> <message>\n"
541 "Sign a message with the private key of an address");
543 if (pwalletMain->IsLocked())
544 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
546 string strAddress = params[0].get_str();
547 string strMessage = params[1].get_str();
549 CBitcoinAddress addr(strAddress);
551 throw JSONRPCError(-3, "Invalid address");
554 if (!pwalletMain->GetKey(addr, key))
555 throw JSONRPCError(-4, "Private key not available");
557 CDataStream ss(SER_GETHASH);
558 ss << strMessageMagic;
561 vector<unsigned char> vchSig;
562 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
563 throw JSONRPCError(-5, "Sign failed");
565 return EncodeBase64(&vchSig[0], vchSig.size());
568 Value verifymessage(const Array& params, bool fHelp)
570 if (fHelp || params.size() != 3)
572 "verifymessage <ppcoinaddress> <signature> <message>\n"
573 "Verify a signed message");
575 string strAddress = params[0].get_str();
576 string strSign = params[1].get_str();
577 string strMessage = params[2].get_str();
579 CBitcoinAddress addr(strAddress);
581 throw JSONRPCError(-3, "Invalid address");
583 bool fInvalid = false;
584 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
587 throw JSONRPCError(-5, "Malformed base64 encoding");
589 CDataStream ss(SER_GETHASH);
590 ss << strMessageMagic;
594 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
597 return (key.GetAddress() == addr);
601 Value getreceivedbyaddress(const Array& params, bool fHelp)
603 if (fHelp || params.size() < 1 || params.size() > 2)
605 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
606 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
609 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
610 CScript scriptPubKey;
611 if (!address.IsValid())
612 throw JSONRPCError(-5, "Invalid ppcoin address");
613 scriptPubKey.SetBitcoinAddress(address);
614 if (!IsMine(*pwalletMain,scriptPubKey))
617 // Minimum confirmations
619 if (params.size() > 1)
620 nMinDepth = params[1].get_int();
624 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
626 const CWalletTx& wtx = (*it).second;
627 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
630 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
631 if (txout.scriptPubKey == scriptPubKey)
632 if (wtx.GetDepthInMainChain() >= nMinDepth)
633 nAmount += txout.nValue;
636 return ValueFromAmount(nAmount);
640 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
642 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
644 const CBitcoinAddress& address = item.first;
645 const string& strName = item.second;
646 if (strName == strAccount)
647 setAddress.insert(address);
652 Value getreceivedbyaccount(const Array& params, bool fHelp)
654 if (fHelp || params.size() < 1 || params.size() > 2)
656 "getreceivedbyaccount <account> [minconf=1]\n"
657 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
659 // Minimum confirmations
661 if (params.size() > 1)
662 nMinDepth = params[1].get_int();
664 // Get the set of pub keys that have the label
665 string strAccount = AccountFromValue(params[0]);
666 set<CBitcoinAddress> setAddress;
667 GetAccountAddresses(strAccount, setAddress);
671 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
673 const CWalletTx& wtx = (*it).second;
674 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
677 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
679 CBitcoinAddress address;
680 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
681 if (wtx.GetDepthInMainChain() >= nMinDepth)
682 nAmount += txout.nValue;
686 return (double)nAmount / (double)COIN;
690 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
694 // Tally wallet transactions
695 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
697 const CWalletTx& wtx = (*it).second;
701 int64 nGenerated, nReceived, nSent, nFee;
702 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
704 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
705 nBalance += nReceived;
706 nBalance += nGenerated - nSent - nFee;
709 // Tally internal accounting entries
710 nBalance += walletdb.GetAccountCreditDebit(strAccount);
715 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
717 CWalletDB walletdb(pwalletMain->strWalletFile);
718 return GetAccountBalance(walletdb, strAccount, nMinDepth);
722 Value getbalance(const Array& params, bool fHelp)
724 if (fHelp || params.size() > 2)
726 "getbalance [account] [minconf=1]\n"
727 "If [account] is not specified, returns the server's total available balance.\n"
728 "If [account] is specified, returns the balance in the account.");
730 if (params.size() == 0)
731 return ValueFromAmount(pwalletMain->GetBalance());
734 if (params.size() > 1)
735 nMinDepth = params[1].get_int();
737 if (params[0].get_str() == "*") {
738 // Calculate total balance a different way from GetBalance()
739 // (GetBalance() sums up all unspent TxOuts)
740 // getbalance and getbalance '*' should always return the same number.
742 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
744 const CWalletTx& wtx = (*it).second;
748 int64 allGeneratedImmature, allGeneratedMature, allFee;
749 allGeneratedImmature = allGeneratedMature = allFee = 0;
750 string strSentAccount;
751 list<pair<CBitcoinAddress, int64> > listReceived;
752 list<pair<CBitcoinAddress, int64> > listSent;
753 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
754 if (wtx.GetDepthInMainChain() >= nMinDepth)
755 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
756 nBalance += r.second;
757 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
758 nBalance -= r.second;
760 nBalance += allGeneratedMature;
762 return ValueFromAmount(nBalance);
765 string strAccount = AccountFromValue(params[0]);
767 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
769 return ValueFromAmount(nBalance);
773 Value movecmd(const Array& params, bool fHelp)
775 if (fHelp || params.size() < 3 || params.size() > 5)
777 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
778 "Move from one account in your wallet to another.");
780 string strFrom = AccountFromValue(params[0]);
781 string strTo = AccountFromValue(params[1]);
782 int64 nAmount = AmountFromValue(params[2]);
783 if (params.size() > 3)
784 // unused parameter, used to be nMinDepth, keep type-checking it though
785 (void)params[3].get_int();
787 if (params.size() > 4)
788 strComment = params[4].get_str();
790 CWalletDB walletdb(pwalletMain->strWalletFile);
793 int64 nNow = GetAdjustedTime();
796 CAccountingEntry debit;
797 debit.strAccount = strFrom;
798 debit.nCreditDebit = -nAmount;
800 debit.strOtherAccount = strTo;
801 debit.strComment = strComment;
802 walletdb.WriteAccountingEntry(debit);
805 CAccountingEntry credit;
806 credit.strAccount = strTo;
807 credit.nCreditDebit = nAmount;
809 credit.strOtherAccount = strFrom;
810 credit.strComment = strComment;
811 walletdb.WriteAccountingEntry(credit);
813 walletdb.TxnCommit();
819 Value sendfrom(const Array& params, bool fHelp)
821 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
823 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
824 "<amount> is a real and is rounded to the nearest 0.000001\n"
825 "requires wallet passphrase to be set with walletpassphrase first");
826 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
828 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
829 "<amount> is a real and is rounded to the nearest 0.000001");
831 string strAccount = AccountFromValue(params[0]);
832 CBitcoinAddress address(params[1].get_str());
833 if (!address.IsValid())
834 throw JSONRPCError(-5, "Invalid ppcoin address");
835 int64 nAmount = AmountFromValue(params[2]);
837 if (params.size() > 3)
838 nMinDepth = params[3].get_int();
841 wtx.strFromAccount = strAccount;
842 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
843 wtx.mapValue["comment"] = params[4].get_str();
844 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
845 wtx.mapValue["to"] = params[5].get_str();
847 if (pwalletMain->IsLocked())
848 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
851 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
852 if (nAmount > nBalance)
853 throw JSONRPCError(-6, "Account has insufficient funds");
856 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
858 throw JSONRPCError(-4, strError);
860 return wtx.GetHash().GetHex();
864 Value sendmany(const Array& params, bool fHelp)
866 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
868 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
869 "amounts are double-precision floating point numbers\n"
870 "requires wallet passphrase to be set with walletpassphrase first");
871 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
873 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
874 "amounts are double-precision floating point numbers");
876 string strAccount = AccountFromValue(params[0]);
877 Object sendTo = params[1].get_obj();
879 if (params.size() > 2)
880 nMinDepth = params[2].get_int();
883 wtx.strFromAccount = strAccount;
884 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
885 wtx.mapValue["comment"] = params[3].get_str();
887 set<CBitcoinAddress> setAddress;
888 vector<pair<CScript, int64> > vecSend;
890 int64 totalAmount = 0;
891 BOOST_FOREACH(const Pair& s, sendTo)
893 CBitcoinAddress address(s.name_);
894 if (!address.IsValid())
895 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
897 if (setAddress.count(address))
898 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
899 setAddress.insert(address);
901 CScript scriptPubKey;
902 scriptPubKey.SetBitcoinAddress(address);
903 int64 nAmount = AmountFromValue(s.value_);
904 totalAmount += nAmount;
906 vecSend.push_back(make_pair(scriptPubKey, nAmount));
909 if (pwalletMain->IsLocked())
910 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
913 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
914 if (totalAmount > nBalance)
915 throw JSONRPCError(-6, "Account has insufficient funds");
918 CReserveKey keyChange(pwalletMain);
919 int64 nFeeRequired = 0;
920 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
923 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
924 throw JSONRPCError(-6, "Insufficient funds");
925 throw JSONRPCError(-4, "Transaction creation failed");
927 if (!pwalletMain->CommitTransaction(wtx, keyChange))
928 throw JSONRPCError(-4, "Transaction commit failed");
930 return wtx.GetHash().GetHex();
945 Value ListReceived(const Array& params, bool fByAccounts)
947 // Minimum confirmations
949 if (params.size() > 0)
950 nMinDepth = params[0].get_int();
952 // Whether to include empty accounts
953 bool fIncludeEmpty = false;
954 if (params.size() > 1)
955 fIncludeEmpty = params[1].get_bool();
958 map<CBitcoinAddress, tallyitem> mapTally;
959 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
961 const CWalletTx& wtx = (*it).second;
962 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
965 int nDepth = wtx.GetDepthInMainChain();
966 if (nDepth < nMinDepth)
969 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
971 CBitcoinAddress address;
972 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
975 tallyitem& item = mapTally[address];
976 item.nAmount += txout.nValue;
977 item.nConf = min(item.nConf, nDepth);
983 map<string, tallyitem> mapAccountTally;
984 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
986 const CBitcoinAddress& address = item.first;
987 const string& strAccount = item.second;
988 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
989 if (it == mapTally.end() && !fIncludeEmpty)
994 if (it != mapTally.end())
996 nAmount = (*it).second.nAmount;
997 nConf = (*it).second.nConf;
1002 tallyitem& item = mapAccountTally[strAccount];
1003 item.nAmount += nAmount;
1004 item.nConf = min(item.nConf, nConf);
1009 obj.push_back(Pair("address", address.ToString()));
1010 obj.push_back(Pair("account", strAccount));
1011 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1012 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1019 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1021 int64 nAmount = (*it).second.nAmount;
1022 int nConf = (*it).second.nConf;
1024 obj.push_back(Pair("account", (*it).first));
1025 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1026 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1034 Value listreceivedbyaddress(const Array& params, bool fHelp)
1036 if (fHelp || params.size() > 2)
1037 throw runtime_error(
1038 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1039 "[minconf] is the minimum number of confirmations before payments are included.\n"
1040 "[includeempty] whether to include addresses that haven't received any payments.\n"
1041 "Returns an array of objects containing:\n"
1042 " \"address\" : receiving address\n"
1043 " \"account\" : the account of the receiving address\n"
1044 " \"amount\" : total amount received by the address\n"
1045 " \"confirmations\" : number of confirmations of the most recent transaction included");
1047 return ListReceived(params, false);
1050 Value listreceivedbyaccount(const Array& params, bool fHelp)
1052 if (fHelp || params.size() > 2)
1053 throw runtime_error(
1054 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1055 "[minconf] is the minimum number of confirmations before payments are included.\n"
1056 "[includeempty] whether to include accounts that haven't received any payments.\n"
1057 "Returns an array of objects containing:\n"
1058 " \"account\" : the account of the receiving addresses\n"
1059 " \"amount\" : total amount received by addresses with this account\n"
1060 " \"confirmations\" : number of confirmations of the most recent transaction included");
1062 return ListReceived(params, true);
1065 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1067 int64 nGeneratedImmature, nGeneratedMature, nFee;
1068 string strSentAccount;
1069 list<pair<CBitcoinAddress, int64> > listReceived;
1070 list<pair<CBitcoinAddress, int64> > listSent;
1071 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1073 bool fAllAccounts = (strAccount == string("*"));
1075 // Generated blocks assigned to account ""
1076 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1079 entry.push_back(Pair("account", string("")));
1080 if (nGeneratedImmature)
1082 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1083 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1087 entry.push_back(Pair("category", "generate"));
1088 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1091 WalletTxToJSON(wtx, entry);
1092 ret.push_back(entry);
1096 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1098 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1101 entry.push_back(Pair("account", strSentAccount));
1102 entry.push_back(Pair("address", s.first.ToString()));
1103 entry.push_back(Pair("category", "send"));
1104 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1105 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1107 WalletTxToJSON(wtx, entry);
1108 ret.push_back(entry);
1113 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1114 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1117 if (pwalletMain->mapAddressBook.count(r.first))
1118 account = pwalletMain->mapAddressBook[r.first];
1119 if (fAllAccounts || (account == strAccount))
1122 entry.push_back(Pair("account", account));
1123 entry.push_back(Pair("address", r.first.ToString()));
1124 entry.push_back(Pair("category", "receive"));
1125 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1127 WalletTxToJSON(wtx, entry);
1128 ret.push_back(entry);
1133 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1135 bool fAllAccounts = (strAccount == string("*"));
1137 if (fAllAccounts || acentry.strAccount == strAccount)
1140 entry.push_back(Pair("account", acentry.strAccount));
1141 entry.push_back(Pair("category", "move"));
1142 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1143 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1144 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1145 entry.push_back(Pair("comment", acentry.strComment));
1146 ret.push_back(entry);
1150 Value listtransactions(const Array& params, bool fHelp)
1152 if (fHelp || params.size() > 3)
1153 throw runtime_error(
1154 "listtransactions [account] [count=10] [from=0]\n"
1155 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1157 string strAccount = "*";
1158 if (params.size() > 0)
1159 strAccount = params[0].get_str();
1161 if (params.size() > 1)
1162 nCount = params[1].get_int();
1164 if (params.size() > 2)
1165 nFrom = params[2].get_int();
1168 CWalletDB walletdb(pwalletMain->strWalletFile);
1170 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1171 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1172 typedef multimap<int64, TxPair > TxItems;
1175 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1177 CWalletTx* wtx = &((*it).second);
1178 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1180 list<CAccountingEntry> acentries;
1181 walletdb.ListAccountCreditDebit(strAccount, acentries);
1182 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1184 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1187 // Now: iterate backwards until we have nCount items to return:
1188 TxItems::reverse_iterator it = txByTime.rbegin();
1189 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1190 for (; it != txByTime.rend(); ++it)
1192 CWalletTx *const pwtx = (*it).second.first;
1194 ListTransactions(*pwtx, strAccount, 0, true, ret);
1195 CAccountingEntry *const pacentry = (*it).second.second;
1197 AcentryToJSON(*pacentry, strAccount, ret);
1199 if (ret.size() >= nCount) break;
1201 // ret is now newest to oldest
1203 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1204 if (ret.size() > nCount)
1206 Array::iterator last = ret.begin();
1207 std::advance(last, nCount);
1208 ret.erase(last, ret.end());
1210 std::reverse(ret.begin(), ret.end()); // oldest to newest
1215 Value listaccounts(const Array& params, bool fHelp)
1217 if (fHelp || params.size() > 1)
1218 throw runtime_error(
1219 "listaccounts [minconf=1]\n"
1220 "Returns Object that has account names as keys, account balances as values.");
1223 if (params.size() > 0)
1224 nMinDepth = params[0].get_int();
1226 map<string, int64> mapAccountBalances;
1227 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1228 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1229 mapAccountBalances[entry.second] = 0;
1232 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1234 const CWalletTx& wtx = (*it).second;
1235 int64 nGeneratedImmature, nGeneratedMature, nFee;
1236 string strSentAccount;
1237 list<pair<CBitcoinAddress, int64> > listReceived;
1238 list<pair<CBitcoinAddress, int64> > listSent;
1239 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1240 mapAccountBalances[strSentAccount] -= nFee;
1241 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1242 mapAccountBalances[strSentAccount] -= s.second;
1243 if (wtx.GetDepthInMainChain() >= nMinDepth)
1245 mapAccountBalances[""] += nGeneratedMature;
1246 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1247 if (pwalletMain->mapAddressBook.count(r.first))
1248 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1250 mapAccountBalances[""] += r.second;
1254 list<CAccountingEntry> acentries;
1255 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1256 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1257 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1260 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1261 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1266 Value listsinceblock(const Array& params, bool fHelp)
1269 throw runtime_error(
1270 "listsinceblock [blockid] [target-confirmations]\n"
1271 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1273 CBlockIndex *pindex = NULL;
1274 int target_confirms = 1;
1276 if (params.size() > 0)
1278 uint256 blockId = 0;
1280 blockId.SetHex(params[0].get_str());
1281 pindex = CBlockLocator(blockId).GetBlockIndex();
1284 if (params.size() > 1)
1286 target_confirms = params[1].get_int();
1288 if (target_confirms < 1)
1289 throw JSONRPCError(-8, "Invalid parameter");
1292 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1296 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1298 CWalletTx tx = (*it).second;
1300 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1301 ListTransactions(tx, "*", 0, true, transactions);
1306 if (target_confirms == 1)
1309 lastblock = hashBestChain;
1313 int target_height = pindexBest->nHeight + 1 - target_confirms;
1316 for (block = pindexBest;
1317 block && block->nHeight > target_height;
1318 block = block->pprev);
1320 lastblock = block ? block->GetBlockHash() : 0;
1324 ret.push_back(Pair("transactions", transactions));
1325 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1330 Value gettransaction(const Array& params, bool fHelp)
1332 if (fHelp || params.size() != 1)
1333 throw runtime_error(
1334 "gettransaction <txid>\n"
1335 "Get detailed information about <txid>");
1338 hash.SetHex(params[0].get_str());
1342 if (!pwalletMain->mapWallet.count(hash))
1343 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1344 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1346 int64 nCredit = wtx.GetCredit();
1347 int64 nDebit = wtx.GetDebit();
1348 int64 nNet = nCredit - nDebit;
1349 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1351 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1353 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1355 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1358 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1359 entry.push_back(Pair("details", details));
1365 Value backupwallet(const Array& params, bool fHelp)
1367 if (fHelp || params.size() != 1)
1368 throw runtime_error(
1369 "backupwallet <destination>\n"
1370 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1372 string strDest = params[0].get_str();
1373 BackupWallet(*pwalletMain, strDest);
1379 Value keypoolrefill(const Array& params, bool fHelp)
1381 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1382 throw runtime_error(
1384 "Fills the keypool, requires wallet passphrase to be set.");
1385 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1386 throw runtime_error(
1388 "Fills the keypool.");
1390 if (pwalletMain->IsLocked())
1391 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1393 pwalletMain->TopUpKeyPool();
1395 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1396 throw JSONRPCError(-4, "Error refreshing keypool.");
1402 void ThreadTopUpKeyPool(void* parg)
1404 pwalletMain->TopUpKeyPool();
1407 void ThreadCleanWalletPassphrase(void* parg)
1409 int64 nMyWakeTime = GetTime() + *((int*)parg);
1411 if (nWalletUnlockTime == 0)
1413 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1415 nWalletUnlockTime = nMyWakeTime;
1418 while (GetTime() < nWalletUnlockTime)
1419 Sleep(GetTime() - nWalletUnlockTime);
1421 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1423 nWalletUnlockTime = 0;
1428 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1430 if (nWalletUnlockTime < nMyWakeTime)
1431 nWalletUnlockTime = nMyWakeTime;
1437 pwalletMain->Lock();
1442 Value walletpassphrase(const Array& params, bool fHelp)
1444 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1445 throw runtime_error(
1446 "walletpassphrase <passphrase> <timeout>\n"
1447 "Stores the wallet decryption key in memory for <timeout> seconds.");
1450 if (!pwalletMain->IsCrypted())
1451 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1453 if (!pwalletMain->IsLocked())
1454 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
1456 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1457 SecureString strWalletPass;
1458 strWalletPass.reserve(100);
1459 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1460 // Alternately, find a way to make params[0] mlock()'d to begin with.
1461 strWalletPass = params[0].get_str().c_str();
1463 if (strWalletPass.length() > 0)
1465 if (!pwalletMain->Unlock(strWalletPass))
1466 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1469 throw runtime_error(
1470 "walletpassphrase <passphrase> <timeout>\n"
1471 "Stores the wallet decryption key in memory for <timeout> seconds.");
1473 CreateThread(ThreadTopUpKeyPool, NULL);
1474 int* pnSleepTime = new int(params[1].get_int());
1475 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1481 Value walletpassphrasechange(const Array& params, bool fHelp)
1483 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1484 throw runtime_error(
1485 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1486 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1489 if (!pwalletMain->IsCrypted())
1490 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1492 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1493 // Alternately, find a way to make params[0] mlock()'d to begin with.
1494 SecureString strOldWalletPass;
1495 strOldWalletPass.reserve(100);
1496 strOldWalletPass = params[0].get_str().c_str();
1498 SecureString strNewWalletPass;
1499 strNewWalletPass.reserve(100);
1500 strNewWalletPass = params[1].get_str().c_str();
1502 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1503 throw runtime_error(
1504 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1505 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1507 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1508 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1514 Value walletlock(const Array& params, bool fHelp)
1516 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1517 throw runtime_error(
1519 "Removes the wallet encryption key from memory, locking the wallet.\n"
1520 "After calling this method, you will need to call walletpassphrase again\n"
1521 "before being able to call any methods which require the wallet to be unlocked.");
1524 if (!pwalletMain->IsCrypted())
1525 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1527 pwalletMain->Lock();
1528 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1530 nWalletUnlockTime = 0;
1537 Value encryptwallet(const Array& params, bool fHelp)
1539 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1540 throw runtime_error(
1541 "encryptwallet <passphrase>\n"
1542 "Encrypts the wallet with <passphrase>.");
1545 if (pwalletMain->IsCrypted())
1546 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1549 // shutting down via RPC while the GUI is running does not work (yet):
1550 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1553 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1554 // Alternately, find a way to make params[0] mlock()'d to begin with.
1555 SecureString strWalletPass;
1556 strWalletPass.reserve(100);
1557 strWalletPass = params[0].get_str().c_str();
1559 if (strWalletPass.length() < 1)
1560 throw runtime_error(
1561 "encryptwallet <passphrase>\n"
1562 "Encrypts the wallet with <passphrase>.");
1564 if (!pwalletMain->EncryptWallet(strWalletPass))
1565 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1567 // BDB seems to have a bad habit of writing old data into
1568 // slack space in .dat files; that is bad if the old data is
1569 // unencrypted private keys. So:
1570 CreateThread(Shutdown, NULL);
1571 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1575 Value validateaddress(const Array& params, bool fHelp)
1577 if (fHelp || params.size() != 1)
1578 throw runtime_error(
1579 "validateaddress <ppcoinaddress>\n"
1580 "Return information about <ppcoinaddress>.");
1582 CBitcoinAddress address(params[0].get_str());
1583 bool isValid = address.IsValid();
1586 ret.push_back(Pair("isvalid", isValid));
1589 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1590 // version of the address:
1591 string currentAddress = address.ToString();
1592 ret.push_back(Pair("address", currentAddress));
1593 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1594 if (pwalletMain->mapAddressBook.count(address))
1595 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1601 Value getwork(const Array& params, bool fHelp)
1603 if (fHelp || params.size() > 1)
1604 throw runtime_error(
1606 "If [data] is not specified, returns formatted hash data to work on:\n"
1607 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1608 " \"data\" : block data\n"
1609 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1610 " \"target\" : little endian hash target\n"
1611 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1614 throw JSONRPCError(-9, "PPCoin is not connected!");
1616 if (IsInitialBlockDownload())
1617 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1619 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1620 static mapNewBlock_t mapNewBlock;
1621 static vector<CBlock*> vNewBlock;
1622 static CReserveKey reservekey(pwalletMain);
1624 if (params.size() == 0)
1627 static unsigned int nTransactionsUpdatedLast;
1628 static CBlockIndex* pindexPrev;
1629 static int64 nStart;
1630 static CBlock* pblock;
1631 if (pindexPrev != pindexBest ||
1632 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1634 if (pindexPrev != pindexBest)
1636 // Deallocate old blocks since they're obsolete now
1637 mapNewBlock.clear();
1638 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1642 nTransactionsUpdatedLast = nTransactionsUpdated;
1643 pindexPrev = pindexBest;
1647 pblock = CreateNewBlock(pwalletMain);
1649 throw JSONRPCError(-7, "Out of memory");
1650 vNewBlock.push_back(pblock);
1654 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1657 // Update nExtraNonce
1658 static unsigned int nExtraNonce = 0;
1659 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1662 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1664 // Prebuild hash buffers
1668 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1670 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1673 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1674 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1675 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1676 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1682 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1683 if (vchData.size() != 128)
1684 throw JSONRPCError(-8, "Invalid parameter");
1685 CBlock* pdata = (CBlock*)&vchData[0];
1688 for (int i = 0; i < 128/4; i++)
1689 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1692 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1694 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1696 pblock->nTime = pdata->nTime;
1697 pblock->nNonce = pdata->nNonce;
1698 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1699 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1701 return CheckWork(pblock, *pwalletMain, reservekey);
1706 Value getmemorypool(const Array& params, bool fHelp)
1708 if (fHelp || params.size() > 1)
1709 throw runtime_error(
1710 "getmemorypool [data]\n"
1711 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1712 " \"version\" : block version\n"
1713 " \"previousblockhash\" : hash of current highest block\n"
1714 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1715 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1716 " \"time\" : timestamp appropriate for next block\n"
1717 " \"bits\" : compressed target of next block\n"
1718 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1720 if (params.size() == 0)
1723 throw JSONRPCError(-9, "PPCoin is not connected!");
1725 if (IsInitialBlockDownload())
1726 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1728 static CReserveKey reservekey(pwalletMain);
1731 static unsigned int nTransactionsUpdatedLast;
1732 static CBlockIndex* pindexPrev;
1733 static int64 nStart;
1734 static CBlock* pblock;
1735 if (pindexPrev != pindexBest ||
1736 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1738 nTransactionsUpdatedLast = nTransactionsUpdated;
1739 pindexPrev = pindexBest;
1745 pblock = CreateNewBlock(pwalletMain);
1747 throw JSONRPCError(-7, "Out of memory");
1751 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1755 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1756 if(tx.IsCoinBase() || tx.IsCoinStake())
1762 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1766 result.push_back(Pair("version", pblock->nVersion));
1767 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1768 result.push_back(Pair("transactions", transactions));
1769 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1770 result.push_back(Pair("time", (int64_t)pblock->nTime));
1776 uBits.nBits = htonl((int32_t)pblock->nBits);
1777 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1784 CDataStream ssBlock(ParseHex(params[0].get_str()));
1788 return ProcessBlock(NULL, &pblock);
1806 pair<string, rpcfn_type> pCallTable[] =
1808 make_pair("help", &help),
1809 make_pair("stop", &stop),
1810 make_pair("getblockcount", &getblockcount),
1811 make_pair("getblocknumber", &getblocknumber),
1812 make_pair("getconnectioncount", &getconnectioncount),
1813 make_pair("getdifficulty", &getdifficulty),
1814 make_pair("getgenerate", &getgenerate),
1815 make_pair("setgenerate", &setgenerate),
1816 make_pair("gethashespersec", &gethashespersec),
1817 make_pair("getinfo", &getinfo),
1818 make_pair("getnewaddress", &getnewaddress),
1819 make_pair("getaccountaddress", &getaccountaddress),
1820 make_pair("setaccount", &setaccount),
1821 make_pair("getaccount", &getaccount),
1822 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1823 make_pair("sendtoaddress", &sendtoaddress),
1824 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1825 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1826 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1827 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1828 make_pair("backupwallet", &backupwallet),
1829 make_pair("keypoolrefill", &keypoolrefill),
1830 make_pair("walletpassphrase", &walletpassphrase),
1831 make_pair("walletpassphrasechange", &walletpassphrasechange),
1832 make_pair("walletlock", &walletlock),
1833 make_pair("encryptwallet", &encryptwallet),
1834 make_pair("validateaddress", &validateaddress),
1835 make_pair("getbalance", &getbalance),
1836 make_pair("move", &movecmd),
1837 make_pair("sendfrom", &sendfrom),
1838 make_pair("sendmany", &sendmany),
1839 make_pair("gettransaction", &gettransaction),
1840 make_pair("listtransactions", &listtransactions),
1841 make_pair("signmessage", &signmessage),
1842 make_pair("verifymessage", &verifymessage),
1843 make_pair("getwork", &getwork),
1844 make_pair("listaccounts", &listaccounts),
1845 make_pair("settxfee", &settxfee),
1846 make_pair("getmemorypool", &getmemorypool),
1847 make_pair("listsinceblock", &listsinceblock),
1849 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1851 string pAllowInSafeMode[] =
1856 "getblocknumber", // deprecated
1857 "getconnectioncount",
1864 "getaccountaddress",
1866 "getaddressesbyaccount",
1875 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1883 // This ain't Apache. We're just using HTTP header for the length field
1884 // and to be compatible with other JSON-RPC implementations.
1887 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1890 s << "POST / HTTP/1.1\r\n"
1891 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1892 << "Host: 127.0.0.1\r\n"
1893 << "Content-Type: application/json\r\n"
1894 << "Content-Length: " << strMsg.size() << "\r\n"
1895 << "Connection: close\r\n"
1896 << "Accept: application/json\r\n";
1897 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1898 s << item.first << ": " << item.second << "\r\n";
1899 s << "\r\n" << strMsg;
1904 string rfc1123Time()
1909 struct tm* now_gmt = gmtime(&now);
1910 string locale(setlocale(LC_TIME, NULL));
1911 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
1912 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
1913 setlocale(LC_TIME, locale.c_str());
1914 return string(buffer);
1917 static string HTTPReply(int nStatus, const string& strMsg)
1920 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
1922 "Server: ppcoin-json-rpc/%s\r\n"
1923 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
1924 "Content-Type: text/html\r\n"
1925 "Content-Length: 296\r\n"
1927 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
1928 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
1931 "<TITLE>Error</TITLE>\r\n"
1932 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
1934 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
1935 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
1936 const char *cStatus;
1937 if (nStatus == 200) cStatus = "OK";
1938 else if (nStatus == 400) cStatus = "Bad Request";
1939 else if (nStatus == 403) cStatus = "Forbidden";
1940 else if (nStatus == 404) cStatus = "Not Found";
1941 else if (nStatus == 500) cStatus = "Internal Server Error";
1944 "HTTP/1.1 %d %s\r\n"
1946 "Connection: close\r\n"
1947 "Content-Length: %d\r\n"
1948 "Content-Type: application/json\r\n"
1949 "Server: ppcoin-json-rpc/%s\r\n"
1954 rfc1123Time().c_str(),
1956 FormatFullVersion().c_str(),
1960 int ReadHTTPStatus(std::basic_istream<char>& stream)
1963 getline(stream, str);
1964 vector<string> vWords;
1965 boost::split(vWords, str, boost::is_any_of(" "));
1966 if (vWords.size() < 2)
1968 return atoi(vWords[1].c_str());
1971 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
1977 std::getline(stream, str);
1978 if (str.empty() || str == "\r")
1980 string::size_type nColon = str.find(":");
1981 if (nColon != string::npos)
1983 string strHeader = str.substr(0, nColon);
1984 boost::trim(strHeader);
1985 boost::to_lower(strHeader);
1986 string strValue = str.substr(nColon+1);
1987 boost::trim(strValue);
1988 mapHeadersRet[strHeader] = strValue;
1989 if (strHeader == "content-length")
1990 nLen = atoi(strValue.c_str());
1996 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
1998 mapHeadersRet.clear();
2002 int nStatus = ReadHTTPStatus(stream);
2005 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2006 if (nLen < 0 || nLen > MAX_SIZE)
2012 vector<char> vch(nLen);
2013 stream.read(&vch[0], nLen);
2014 strMessageRet = string(vch.begin(), vch.end());
2020 bool HTTPAuthorized(map<string, string>& mapHeaders)
2022 string strAuth = mapHeaders["authorization"];
2023 if (strAuth.substr(0,6) != "Basic ")
2025 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2026 string strUserPass = DecodeBase64(strUserPass64);
2027 return strUserPass == strRPCUserColonPass;
2031 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2032 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2033 // unspecified (HTTP errors and contents of 'error').
2035 // 1.0 spec: http://json-rpc.org/wiki/specification
2036 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2037 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2040 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2043 request.push_back(Pair("method", strMethod));
2044 request.push_back(Pair("params", params));
2045 request.push_back(Pair("id", id));
2046 return write_string(Value(request), false) + "\n";
2049 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2052 if (error.type() != null_type)
2053 reply.push_back(Pair("result", Value::null));
2055 reply.push_back(Pair("result", result));
2056 reply.push_back(Pair("error", error));
2057 reply.push_back(Pair("id", id));
2058 return write_string(Value(reply), false) + "\n";
2061 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2063 // Send error reply from json-rpc error object
2065 int code = find_value(objError, "code").get_int();
2066 if (code == -32600) nStatus = 400;
2067 else if (code == -32601) nStatus = 404;
2068 string strReply = JSONRPCReply(Value::null, objError, id);
2069 stream << HTTPReply(nStatus, strReply) << std::flush;
2072 bool ClientAllowed(const string& strAddress)
2074 if (strAddress == asio::ip::address_v4::loopback().to_string())
2076 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2077 BOOST_FOREACH(string strAllow, vAllow)
2078 if (WildcardMatch(strAddress, strAllow))
2085 // IOStream device that speaks SSL but can also speak non-SSL
2087 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2089 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2091 fUseSSL = fUseSSLIn;
2092 fNeedHandshake = fUseSSLIn;
2095 void handshake(ssl::stream_base::handshake_type role)
2097 if (!fNeedHandshake) return;
2098 fNeedHandshake = false;
2099 stream.handshake(role);
2101 std::streamsize read(char* s, std::streamsize n)
2103 handshake(ssl::stream_base::server); // HTTPS servers read first
2104 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2105 return stream.next_layer().read_some(asio::buffer(s, n));
2107 std::streamsize write(const char* s, std::streamsize n)
2109 handshake(ssl::stream_base::client); // HTTPS clients write first
2110 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2111 return asio::write(stream.next_layer(), asio::buffer(s, n));
2113 bool connect(const std::string& server, const std::string& port)
2115 ip::tcp::resolver resolver(stream.get_io_service());
2116 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2117 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2118 ip::tcp::resolver::iterator end;
2119 boost::system::error_code error = asio::error::host_not_found;
2120 while (error && endpoint_iterator != end)
2122 stream.lowest_layer().close();
2123 stream.lowest_layer().connect(*endpoint_iterator++, error);
2131 bool fNeedHandshake;
2137 void ThreadRPCServer(void* parg)
2139 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2142 vnThreadsRunning[4]++;
2143 ThreadRPCServer2(parg);
2144 vnThreadsRunning[4]--;
2146 catch (std::exception& e) {
2147 vnThreadsRunning[4]--;
2148 PrintException(&e, "ThreadRPCServer()");
2150 vnThreadsRunning[4]--;
2151 PrintException(NULL, "ThreadRPCServer()");
2153 printf("ThreadRPCServer exiting\n");
2156 void ThreadRPCServer2(void* parg)
2158 printf("ThreadRPCServer started\n");
2160 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2161 if (strRPCUserColonPass == ":")
2163 string strWhatAmI = "To use ppcoind";
2164 if (mapArgs.count("-server"))
2165 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2166 else if (mapArgs.count("-daemon"))
2167 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2169 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2170 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2172 GetConfigFile().c_str());
2174 CreateThread(Shutdown, NULL);
2179 bool fUseSSL = GetBoolArg("-rpcssl");
2180 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2182 asio::io_service io_service;
2183 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2184 ip::tcp::acceptor acceptor(io_service, endpoint);
2186 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2189 ssl::context context(io_service, ssl::context::sslv23);
2192 context.set_options(ssl::context::no_sslv2);
2193 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2194 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2195 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2196 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2197 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2198 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2199 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2200 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2202 string ciphers = GetArg("-rpcsslciphers",
2203 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2204 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2208 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2213 // Accept connection
2215 SSLStream sslStream(io_service, context);
2216 SSLIOStreamDevice d(sslStream, fUseSSL);
2217 iostreams::stream<SSLIOStreamDevice> stream(d);
2219 ip::tcp::iostream stream;
2222 ip::tcp::endpoint peer;
2223 vnThreadsRunning[4]--;
2225 acceptor.accept(sslStream.lowest_layer(), peer);
2227 acceptor.accept(*stream.rdbuf(), peer);
2229 vnThreadsRunning[4]++;
2233 // Restrict callers by IP
2234 if (!ClientAllowed(peer.address().to_string()))
2236 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2238 stream << HTTPReply(403, "") << std::flush;
2242 map<string, string> mapHeaders;
2245 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2246 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2249 printf("ThreadRPCServer ReadHTTP timeout\n");
2253 // Check authorization
2254 if (mapHeaders.count("authorization") == 0)
2256 stream << HTTPReply(401, "") << std::flush;
2259 if (!HTTPAuthorized(mapHeaders))
2261 // Deter brute-forcing short passwords
2262 if (mapArgs["-rpcpassword"].size() < 15)
2265 stream << HTTPReply(401, "") << std::flush;
2266 printf("ThreadRPCServer incorrect password attempt\n");
2270 Value id = Value::null;
2275 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2276 throw JSONRPCError(-32700, "Parse error");
2277 const Object& request = valRequest.get_obj();
2279 // Parse id now so errors from here on will have the id
2280 id = find_value(request, "id");
2283 Value valMethod = find_value(request, "method");
2284 if (valMethod.type() == null_type)
2285 throw JSONRPCError(-32600, "Missing method");
2286 if (valMethod.type() != str_type)
2287 throw JSONRPCError(-32600, "Method must be a string");
2288 string strMethod = valMethod.get_str();
2289 if (strMethod != "getwork" && strMethod != "getmemorypool")
2290 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2293 Value valParams = find_value(request, "params");
2295 if (valParams.type() == array_type)
2296 params = valParams.get_array();
2297 else if (valParams.type() == null_type)
2300 throw JSONRPCError(-32600, "Params must be an array");
2303 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2304 if (mi == mapCallTable.end())
2305 throw JSONRPCError(-32601, "Method not found");
2307 // Observe safe mode
2308 string strWarning = GetWarnings("rpc");
2309 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2310 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2316 CRITICAL_BLOCK(cs_main)
2317 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2318 result = (*(*mi).second)(params, false);
2321 string strReply = JSONRPCReply(result, Value::null, id);
2322 stream << HTTPReply(200, strReply) << std::flush;
2324 catch (std::exception& e)
2326 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2329 catch (Object& objError)
2331 ErrorReply(stream, objError, id);
2333 catch (std::exception& e)
2335 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2343 Object CallRPC(const string& strMethod, const Array& params)
2345 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2346 throw runtime_error(strprintf(
2347 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2348 "If the file does not exist, create it with owner-readable-only file permissions."),
2349 GetConfigFile().c_str()));
2351 // Connect to localhost
2352 bool fUseSSL = GetBoolArg("-rpcssl");
2354 asio::io_service io_service;
2355 ssl::context context(io_service, ssl::context::sslv23);
2356 context.set_options(ssl::context::no_sslv2);
2357 SSLStream sslStream(io_service, context);
2358 SSLIOStreamDevice d(sslStream, fUseSSL);
2359 iostreams::stream<SSLIOStreamDevice> stream(d);
2360 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2361 throw runtime_error("couldn't connect to server");
2364 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2366 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2368 throw runtime_error("couldn't connect to server");
2372 // HTTP basic authentication
2373 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2374 map<string, string> mapRequestHeaders;
2375 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2378 string strRequest = JSONRPCRequest(strMethod, params, 1);
2379 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2380 stream << strPost << std::flush;
2383 map<string, string> mapHeaders;
2385 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2387 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2388 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2389 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2390 else if (strReply.empty())
2391 throw runtime_error("no response from server");
2395 if (!read_string(strReply, valReply))
2396 throw runtime_error("couldn't parse reply from server");
2397 const Object& reply = valReply.get_obj();
2399 throw runtime_error("expected reply to have result, error and id properties");
2407 template<typename T>
2408 void ConvertTo(Value& value)
2410 if (value.type() == str_type)
2412 // reinterpret string as unquoted json value
2414 if (!read_string(value.get_str(), value2))
2415 throw runtime_error("type mismatch");
2416 value = value2.get_value<T>();
2420 value = value.get_value<T>();
2424 int CommandLineRPC(int argc, char *argv[])
2431 while (argc > 1 && IsSwitchChar(argv[1][0]))
2439 throw runtime_error("too few parameters");
2440 string strMethod = argv[1];
2442 // Parameters default to strings
2444 for (int i = 2; i < argc; i++)
2445 params.push_back(argv[i]);
2446 int n = params.size();
2449 // Special case non-string parameter types
2451 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2452 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2453 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2454 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2455 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2456 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2457 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2458 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2459 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2460 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2461 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2462 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2463 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2464 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2465 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2466 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2467 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2468 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2469 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2470 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2471 if (strMethod == "sendmany" && n > 1)
2473 string s = params[1].get_str();
2475 if (!read_string(s, v) || v.type() != obj_type)
2476 throw runtime_error("type mismatch");
2477 params[1] = v.get_obj();
2479 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2482 Object reply = CallRPC(strMethod, params);
2485 const Value& result = find_value(reply, "result");
2486 const Value& error = find_value(reply, "error");
2488 if (error.type() != null_type)
2491 strPrint = "error: " + write_string(error, false);
2492 int code = find_value(error.get_obj(), "code").get_int();
2498 if (result.type() == null_type)
2500 else if (result.type() == str_type)
2501 strPrint = result.get_str();
2503 strPrint = write_string(result, true);
2506 catch (std::exception& e)
2508 strPrint = string("error: ") + e.what();
2513 PrintException(NULL, "CommandLineRPC()");
2518 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2527 int main(int argc, char *argv[])
2530 // Turn off microsoft heap dump noise
2531 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2532 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2534 setbuf(stdin, NULL);
2535 setbuf(stdout, NULL);
2536 setbuf(stderr, NULL);
2540 if (argc >= 2 && string(argv[1]) == "-server")
2542 printf("server ready\n");
2543 ThreadRPCServer(NULL);
2547 return CommandLineRPC(argc, argv);
2550 catch (std::exception& e) {
2551 PrintException(&e, "main()");
2553 PrintException(NULL, "main()");