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.
11 #include "checkpoints.h"
13 #include <boost/asio.hpp>
14 #include <boost/iostreams/concepts.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #include <boost/algorithm/string.hpp>
18 #include <boost/asio/ssl.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/filesystem/fstream.hpp>
21 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
23 #include "json/json_spirit_reader_template.h"
24 #include "json/json_spirit_writer_template.h"
25 #include "json/json_spirit_utils.h"
26 #define printf OutputDebugStringF
27 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
28 // precompiled in headers.h. The problem might be when the pch file goes over
29 // a certain size around 145MB. If we need access to json_spirit outside this
30 // file, we could use the compiled json_spirit option.
33 using namespace boost;
34 using namespace boost::asio;
35 using namespace json_spirit;
37 void ThreadRPCServer2(void* parg);
38 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
39 extern map<string, rpcfn_type> mapCallTable;
41 static std::string strRPCUserColonPass;
43 static int64 nWalletUnlockTime;
44 static CCriticalSection cs_nWalletUnlockTime;
47 Object JSONRPCError(int code, const string& message)
50 error.push_back(Pair("code", code));
51 error.push_back(Pair("message", message));
56 void PrintConsole(const std::string &format, ...)
59 int limit = sizeof(buffer);
61 va_start(arg_ptr, format);
62 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
64 if (ret < 0 || ret >= limit)
70 fprintf(stdout, "%s", buffer);
74 int64 AmountFromValue(const Value& value)
76 double dAmount = value.get_real();
77 if (dAmount <= 0.0 || dAmount > MAX_MONEY)
78 throw JSONRPCError(-3, "Invalid amount");
79 int64 nAmount = roundint64(dAmount * COIN);
80 if (!MoneyRange(nAmount))
81 throw JSONRPCError(-3, "Invalid amount");
85 Value ValueFromAmount(int64 amount)
87 return (double)amount / (double)COIN;
90 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
92 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
93 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
94 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
95 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
96 entry.push_back(Pair(item.first, item.second));
99 string AccountFromValue(const Value& value)
101 string strAccount = value.get_str();
102 if (strAccount == "*")
103 throw JSONRPCError(-11, "Invalid account name");
110 /// Note: This interface may still be subject to change.
114 Value help(const Array& params, bool fHelp)
116 if (fHelp || params.size() > 1)
119 "List commands, or get help for a command.");
122 if (params.size() > 0)
123 strCommand = params[0].get_str();
126 set<rpcfn_type> setDone;
127 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
129 string strMethod = (*mi).first;
130 // We already filter duplicates, but these deprecated screw up the sort order
131 if (strMethod == "getamountreceived" ||
132 strMethod == "getallreceived" ||
133 strMethod == "getblocknumber" || // deprecated
134 (strMethod.find("label") != string::npos))
136 if (strCommand != "" && strMethod != strCommand)
141 rpcfn_type pfn = (*mi).second;
142 if (setDone.insert(pfn).second)
143 (*pfn)(params, true);
145 catch (std::exception& e)
147 // Help text is returned in an exception
148 string strHelp = string(e.what());
149 if (strCommand == "")
150 if (strHelp.find('\n') != -1)
151 strHelp = strHelp.substr(0, strHelp.find('\n'));
152 strRet += strHelp + "\n";
156 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
157 strRet = strRet.substr(0,strRet.size()-1);
162 Value stop(const Array& params, bool fHelp)
164 if (fHelp || params.size() != 0)
167 "Stop ppcoin server.");
169 // Shutdown will take long enough that the response should get back
170 CreateThread(Shutdown, NULL);
171 return "ppcoin server stopping";
173 throw runtime_error("NYI: cannot shut down GUI with RPC command");
178 Value getblockcount(const Array& params, bool fHelp)
180 if (fHelp || params.size() != 0)
183 "Returns the number of blocks in the longest block chain.");
190 Value getblocknumber(const Array& params, bool fHelp)
192 if (fHelp || params.size() != 0)
195 "Deprecated. Use getblockcount.");
201 Value getconnectioncount(const Array& params, bool fHelp)
203 if (fHelp || params.size() != 0)
205 "getconnectioncount\n"
206 "Returns the number of connections to other nodes.");
208 return (int)vNodes.size();
212 double GetDifficulty()
214 // Floating point number that is a multiple of the minimum difficulty,
215 // minimum difficulty = 1.0.
217 if (pindexBest == NULL)
219 int nShift = (pindexBest->nBits >> 24) & 0xff;
222 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
238 Value getdifficulty(const Array& params, bool fHelp)
240 if (fHelp || params.size() != 0)
243 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
245 return GetDifficulty();
249 Value getgenerate(const Array& params, bool fHelp)
251 if (fHelp || params.size() != 0)
254 "Returns true or false.");
256 return (bool)fGenerateBitcoins;
260 Value setgenerate(const Array& params, bool fHelp)
262 if (fHelp || params.size() < 1 || params.size() > 2)
264 "setgenerate <generate> [genproclimit]\n"
265 "<generate> is true or false to turn generation on or off.\n"
266 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
268 bool fGenerate = true;
269 if (params.size() > 0)
270 fGenerate = params[0].get_bool();
272 if (params.size() > 1)
274 int nGenProcLimit = params[1].get_int();
275 fLimitProcessors = (nGenProcLimit != -1);
276 WriteSetting("fLimitProcessors", fLimitProcessors);
277 if (nGenProcLimit != -1)
278 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
279 if (nGenProcLimit == 0)
283 GenerateBitcoins(fGenerate, pwalletMain);
288 Value gethashespersec(const Array& params, bool fHelp)
290 if (fHelp || params.size() != 0)
293 "Returns a recent hashes per second performance measurement while generating.");
295 if (GetTimeMillis() - nHPSTimerStart > 8000)
296 return (boost::int64_t)0;
297 return (boost::int64_t)dHashesPerSec;
301 Value getinfo(const Array& params, bool fHelp)
303 if (fHelp || params.size() != 0)
306 "Returns an object containing various state info.");
309 obj.push_back(Pair("version", (int)VERSION));
310 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
311 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
312 obj.push_back(Pair("blocks", (int)nBestHeight));
313 obj.push_back(Pair("connections", (int)vNodes.size()));
314 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
315 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
316 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
317 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
318 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
319 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
320 obj.push_back(Pair("testnet", fTestNet));
321 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
322 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
323 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
324 if (pwalletMain->IsCrypted())
325 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
326 obj.push_back(Pair("errors", GetWarnings("statusbar")));
331 Value getnewaddress(const Array& params, bool fHelp)
333 if (fHelp || params.size() > 1)
335 "getnewaddress [account]\n"
336 "Returns a new ppcoin address for receiving payments. "
337 "If [account] is specified (recommended), it is added to the address book "
338 "so payments received with the address will be credited to [account].");
340 // Parse the account first so we don't generate a key if there's an error
342 if (params.size() > 0)
343 strAccount = AccountFromValue(params[0]);
345 if (!pwalletMain->IsLocked())
346 pwalletMain->TopUpKeyPool();
348 // Generate a new key that is added to wallet
349 std::vector<unsigned char> newKey;
350 if (!pwalletMain->GetKeyFromPool(newKey, false))
351 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
352 CBitcoinAddress address(newKey);
354 pwalletMain->SetAddressBookName(address, strAccount);
356 return address.ToString();
360 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
362 CWalletDB walletdb(pwalletMain->strWalletFile);
365 walletdb.ReadAccount(strAccount, account);
367 bool bKeyUsed = false;
369 // Check if the current key has been used
370 if (!account.vchPubKey.empty())
372 CScript scriptPubKey;
373 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
374 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
375 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
378 const CWalletTx& wtx = (*it).second;
379 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
380 if (txout.scriptPubKey == scriptPubKey)
385 // Generate a new key
386 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
388 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
389 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
391 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
392 walletdb.WriteAccount(strAccount, account);
395 return CBitcoinAddress(account.vchPubKey);
398 Value getaccountaddress(const Array& params, bool fHelp)
400 if (fHelp || params.size() != 1)
402 "getaccountaddress <account>\n"
403 "Returns the current ppcoin address for receiving payments to this account.");
405 // Parse the account first so we don't generate a key if there's an error
406 string strAccount = AccountFromValue(params[0]);
410 ret = GetAccountAddress(strAccount).ToString();
417 Value setaccount(const Array& params, bool fHelp)
419 if (fHelp || params.size() < 1 || params.size() > 2)
421 "setaccount <ppcoinaddress> <account>\n"
422 "Sets the account associated with the given address.");
424 CBitcoinAddress address(params[0].get_str());
425 if (!address.IsValid())
426 throw JSONRPCError(-5, "Invalid ppcoin address");
430 if (params.size() > 1)
431 strAccount = AccountFromValue(params[1]);
433 // Detect when changing the account of an address that is the 'unused current key' of another account:
434 if (pwalletMain->mapAddressBook.count(address))
436 string strOldAccount = pwalletMain->mapAddressBook[address];
437 if (address == GetAccountAddress(strOldAccount))
438 GetAccountAddress(strOldAccount, true);
441 pwalletMain->SetAddressBookName(address, strAccount);
447 Value getaccount(const Array& params, bool fHelp)
449 if (fHelp || params.size() != 1)
451 "getaccount <ppcoinaddress>\n"
452 "Returns the account associated with the given address.");
454 CBitcoinAddress address(params[0].get_str());
455 if (!address.IsValid())
456 throw JSONRPCError(-5, "Invalid ppcoin address");
459 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
460 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
461 strAccount = (*mi).second;
466 Value getaddressesbyaccount(const Array& params, bool fHelp)
468 if (fHelp || params.size() != 1)
470 "getaddressesbyaccount <account>\n"
471 "Returns the list of addresses for the given account.");
473 string strAccount = AccountFromValue(params[0]);
475 // Find all addresses that have the given account
477 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
479 const CBitcoinAddress& address = item.first;
480 const string& strName = item.second;
481 if (strName == strAccount)
482 ret.push_back(address.ToString());
487 Value settxfee(const Array& params, bool fHelp)
489 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
491 "settxfee <amount>\n"
492 "<amount> is a real and is rounded to 0.01 (cent)\n"
493 "Minimum and default transaction fee per KB is 1 cent");
495 nTransactionFee = AmountFromValue(params[0]);
496 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
500 Value sendtoaddress(const Array& params, bool fHelp)
502 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
504 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
505 "<amount> is a real and is rounded to the nearest 0.000001\n"
506 "requires wallet passphrase to be set with walletpassphrase first");
507 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
509 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
510 "<amount> is a real and is rounded to the nearest 0.000001");
512 CBitcoinAddress address(params[0].get_str());
513 if (!address.IsValid())
514 throw JSONRPCError(-5, "Invalid ppcoin address");
517 int64 nAmount = AmountFromValue(params[1]);
521 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
522 wtx.mapValue["comment"] = params[2].get_str();
523 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
524 wtx.mapValue["to"] = params[3].get_str();
526 if (pwalletMain->IsLocked())
527 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
529 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
531 throw JSONRPCError(-4, strError);
533 return wtx.GetHash().GetHex();
536 static const string strMessageMagic = "Bitcoin Signed Message:\n";
538 Value signmessage(const Array& params, bool fHelp)
540 if (fHelp || params.size() != 2)
542 "signmessage <ppcoinaddress> <message>\n"
543 "Sign a message with the private key of an address");
545 if (pwalletMain->IsLocked())
546 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
548 string strAddress = params[0].get_str();
549 string strMessage = params[1].get_str();
551 CBitcoinAddress addr(strAddress);
553 throw JSONRPCError(-3, "Invalid address");
556 if (!pwalletMain->GetKey(addr, key))
557 throw JSONRPCError(-4, "Private key not available");
559 CDataStream ss(SER_GETHASH);
560 ss << strMessageMagic;
563 vector<unsigned char> vchSig;
564 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
565 throw JSONRPCError(-5, "Sign failed");
567 return EncodeBase64(&vchSig[0], vchSig.size());
570 Value verifymessage(const Array& params, bool fHelp)
572 if (fHelp || params.size() != 3)
574 "verifymessage <ppcoinaddress> <signature> <message>\n"
575 "Verify a signed message");
577 string strAddress = params[0].get_str();
578 string strSign = params[1].get_str();
579 string strMessage = params[2].get_str();
581 CBitcoinAddress addr(strAddress);
583 throw JSONRPCError(-3, "Invalid address");
585 bool fInvalid = false;
586 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
589 throw JSONRPCError(-5, "Malformed base64 encoding");
591 CDataStream ss(SER_GETHASH);
592 ss << strMessageMagic;
596 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
599 return (key.GetAddress() == addr);
603 Value getreceivedbyaddress(const Array& params, bool fHelp)
605 if (fHelp || params.size() < 1 || params.size() > 2)
607 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
608 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
611 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
612 CScript scriptPubKey;
613 if (!address.IsValid())
614 throw JSONRPCError(-5, "Invalid ppcoin address");
615 scriptPubKey.SetBitcoinAddress(address);
616 if (!IsMine(*pwalletMain,scriptPubKey))
619 // Minimum confirmations
621 if (params.size() > 1)
622 nMinDepth = params[1].get_int();
626 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
628 const CWalletTx& wtx = (*it).second;
629 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
632 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
633 if (txout.scriptPubKey == scriptPubKey)
634 if (wtx.GetDepthInMainChain() >= nMinDepth)
635 nAmount += txout.nValue;
638 return ValueFromAmount(nAmount);
642 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
644 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
646 const CBitcoinAddress& address = item.first;
647 const string& strName = item.second;
648 if (strName == strAccount)
649 setAddress.insert(address);
654 Value getreceivedbyaccount(const Array& params, bool fHelp)
656 if (fHelp || params.size() < 1 || params.size() > 2)
658 "getreceivedbyaccount <account> [minconf=1]\n"
659 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
661 // Minimum confirmations
663 if (params.size() > 1)
664 nMinDepth = params[1].get_int();
666 // Get the set of pub keys that have the label
667 string strAccount = AccountFromValue(params[0]);
668 set<CBitcoinAddress> setAddress;
669 GetAccountAddresses(strAccount, setAddress);
673 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
675 const CWalletTx& wtx = (*it).second;
676 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
679 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
681 CBitcoinAddress address;
682 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
683 if (wtx.GetDepthInMainChain() >= nMinDepth)
684 nAmount += txout.nValue;
688 return (double)nAmount / (double)COIN;
692 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
696 // Tally wallet transactions
697 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
699 const CWalletTx& wtx = (*it).second;
703 int64 nGenerated, nReceived, nSent, nFee;
704 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
706 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
707 nBalance += nReceived;
708 nBalance += nGenerated - nSent - nFee;
711 // Tally internal accounting entries
712 nBalance += walletdb.GetAccountCreditDebit(strAccount);
717 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
719 CWalletDB walletdb(pwalletMain->strWalletFile);
720 return GetAccountBalance(walletdb, strAccount, nMinDepth);
724 Value getbalance(const Array& params, bool fHelp)
726 if (fHelp || params.size() > 2)
728 "getbalance [account] [minconf=1]\n"
729 "If [account] is not specified, returns the server's total available balance.\n"
730 "If [account] is specified, returns the balance in the account.");
732 if (params.size() == 0)
733 return ValueFromAmount(pwalletMain->GetBalance());
736 if (params.size() > 1)
737 nMinDepth = params[1].get_int();
739 if (params[0].get_str() == "*") {
740 // Calculate total balance a different way from GetBalance()
741 // (GetBalance() sums up all unspent TxOuts)
742 // getbalance and getbalance '*' should always return the same number.
744 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
746 const CWalletTx& wtx = (*it).second;
750 int64 allGeneratedImmature, allGeneratedMature, allFee;
751 allGeneratedImmature = allGeneratedMature = allFee = 0;
752 string strSentAccount;
753 list<pair<CBitcoinAddress, int64> > listReceived;
754 list<pair<CBitcoinAddress, int64> > listSent;
755 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
756 if (wtx.GetDepthInMainChain() >= nMinDepth)
757 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
758 nBalance += r.second;
759 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
760 nBalance -= r.second;
762 nBalance += allGeneratedMature;
764 return ValueFromAmount(nBalance);
767 string strAccount = AccountFromValue(params[0]);
769 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
771 return ValueFromAmount(nBalance);
775 Value movecmd(const Array& params, bool fHelp)
777 if (fHelp || params.size() < 3 || params.size() > 5)
779 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
780 "Move from one account in your wallet to another.");
782 string strFrom = AccountFromValue(params[0]);
783 string strTo = AccountFromValue(params[1]);
784 int64 nAmount = AmountFromValue(params[2]);
785 if (params.size() > 3)
786 // unused parameter, used to be nMinDepth, keep type-checking it though
787 (void)params[3].get_int();
789 if (params.size() > 4)
790 strComment = params[4].get_str();
792 CWalletDB walletdb(pwalletMain->strWalletFile);
795 int64 nNow = GetAdjustedTime();
798 CAccountingEntry debit;
799 debit.strAccount = strFrom;
800 debit.nCreditDebit = -nAmount;
802 debit.strOtherAccount = strTo;
803 debit.strComment = strComment;
804 walletdb.WriteAccountingEntry(debit);
807 CAccountingEntry credit;
808 credit.strAccount = strTo;
809 credit.nCreditDebit = nAmount;
811 credit.strOtherAccount = strFrom;
812 credit.strComment = strComment;
813 walletdb.WriteAccountingEntry(credit);
815 walletdb.TxnCommit();
821 Value sendfrom(const Array& params, bool fHelp)
823 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
825 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
826 "<amount> is a real and is rounded to the nearest 0.000001\n"
827 "requires wallet passphrase to be set with walletpassphrase first");
828 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
830 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
831 "<amount> is a real and is rounded to the nearest 0.000001");
833 string strAccount = AccountFromValue(params[0]);
834 CBitcoinAddress address(params[1].get_str());
835 if (!address.IsValid())
836 throw JSONRPCError(-5, "Invalid ppcoin address");
837 int64 nAmount = AmountFromValue(params[2]);
839 if (params.size() > 3)
840 nMinDepth = params[3].get_int();
843 wtx.strFromAccount = strAccount;
844 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
845 wtx.mapValue["comment"] = params[4].get_str();
846 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
847 wtx.mapValue["to"] = params[5].get_str();
849 if (pwalletMain->IsLocked())
850 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
853 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
854 if (nAmount > nBalance)
855 throw JSONRPCError(-6, "Account has insufficient funds");
858 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
860 throw JSONRPCError(-4, strError);
862 return wtx.GetHash().GetHex();
866 Value sendmany(const Array& params, bool fHelp)
868 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
870 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
871 "amounts are double-precision floating point numbers\n"
872 "requires wallet passphrase to be set with walletpassphrase first");
873 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
875 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
876 "amounts are double-precision floating point numbers");
878 string strAccount = AccountFromValue(params[0]);
879 Object sendTo = params[1].get_obj();
881 if (params.size() > 2)
882 nMinDepth = params[2].get_int();
885 wtx.strFromAccount = strAccount;
886 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
887 wtx.mapValue["comment"] = params[3].get_str();
889 set<CBitcoinAddress> setAddress;
890 vector<pair<CScript, int64> > vecSend;
892 int64 totalAmount = 0;
893 BOOST_FOREACH(const Pair& s, sendTo)
895 CBitcoinAddress address(s.name_);
896 if (!address.IsValid())
897 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
899 if (setAddress.count(address))
900 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
901 setAddress.insert(address);
903 CScript scriptPubKey;
904 scriptPubKey.SetBitcoinAddress(address);
905 int64 nAmount = AmountFromValue(s.value_);
906 totalAmount += nAmount;
908 vecSend.push_back(make_pair(scriptPubKey, nAmount));
911 if (pwalletMain->IsLocked())
912 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
913 if (fWalletUnlockStakeOnly)
914 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
917 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
918 if (totalAmount > nBalance)
919 throw JSONRPCError(-6, "Account has insufficient funds");
922 CReserveKey keyChange(pwalletMain);
923 int64 nFeeRequired = 0;
924 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
927 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
928 throw JSONRPCError(-6, "Insufficient funds");
929 throw JSONRPCError(-4, "Transaction creation failed");
931 if (!pwalletMain->CommitTransaction(wtx, keyChange))
932 throw JSONRPCError(-4, "Transaction commit failed");
934 return wtx.GetHash().GetHex();
949 Value ListReceived(const Array& params, bool fByAccounts)
951 // Minimum confirmations
953 if (params.size() > 0)
954 nMinDepth = params[0].get_int();
956 // Whether to include empty accounts
957 bool fIncludeEmpty = false;
958 if (params.size() > 1)
959 fIncludeEmpty = params[1].get_bool();
962 map<CBitcoinAddress, tallyitem> mapTally;
963 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
965 const CWalletTx& wtx = (*it).second;
966 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
969 int nDepth = wtx.GetDepthInMainChain();
970 if (nDepth < nMinDepth)
973 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
975 CBitcoinAddress address;
976 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
979 tallyitem& item = mapTally[address];
980 item.nAmount += txout.nValue;
981 item.nConf = min(item.nConf, nDepth);
987 map<string, tallyitem> mapAccountTally;
988 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
990 const CBitcoinAddress& address = item.first;
991 const string& strAccount = item.second;
992 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
993 if (it == mapTally.end() && !fIncludeEmpty)
998 if (it != mapTally.end())
1000 nAmount = (*it).second.nAmount;
1001 nConf = (*it).second.nConf;
1006 tallyitem& item = mapAccountTally[strAccount];
1007 item.nAmount += nAmount;
1008 item.nConf = min(item.nConf, nConf);
1013 obj.push_back(Pair("address", address.ToString()));
1014 obj.push_back(Pair("account", strAccount));
1015 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1016 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1023 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1025 int64 nAmount = (*it).second.nAmount;
1026 int nConf = (*it).second.nConf;
1028 obj.push_back(Pair("account", (*it).first));
1029 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1030 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1038 Value listreceivedbyaddress(const Array& params, bool fHelp)
1040 if (fHelp || params.size() > 2)
1041 throw runtime_error(
1042 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1043 "[minconf] is the minimum number of confirmations before payments are included.\n"
1044 "[includeempty] whether to include addresses that haven't received any payments.\n"
1045 "Returns an array of objects containing:\n"
1046 " \"address\" : receiving address\n"
1047 " \"account\" : the account of the receiving address\n"
1048 " \"amount\" : total amount received by the address\n"
1049 " \"confirmations\" : number of confirmations of the most recent transaction included");
1051 return ListReceived(params, false);
1054 Value listreceivedbyaccount(const Array& params, bool fHelp)
1056 if (fHelp || params.size() > 2)
1057 throw runtime_error(
1058 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1059 "[minconf] is the minimum number of confirmations before payments are included.\n"
1060 "[includeempty] whether to include accounts that haven't received any payments.\n"
1061 "Returns an array of objects containing:\n"
1062 " \"account\" : the account of the receiving addresses\n"
1063 " \"amount\" : total amount received by addresses with this account\n"
1064 " \"confirmations\" : number of confirmations of the most recent transaction included");
1066 return ListReceived(params, true);
1069 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1071 int64 nGeneratedImmature, nGeneratedMature, nFee;
1072 string strSentAccount;
1073 list<pair<CBitcoinAddress, int64> > listReceived;
1074 list<pair<CBitcoinAddress, int64> > listSent;
1075 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1077 bool fAllAccounts = (strAccount == string("*"));
1079 // Generated blocks assigned to account ""
1080 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1083 entry.push_back(Pair("account", string("")));
1084 if (nGeneratedImmature)
1086 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1087 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1091 entry.push_back(Pair("category", "generate"));
1092 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1095 WalletTxToJSON(wtx, entry);
1096 ret.push_back(entry);
1100 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1102 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1105 entry.push_back(Pair("account", strSentAccount));
1106 entry.push_back(Pair("address", s.first.ToString()));
1107 entry.push_back(Pair("category", "send"));
1108 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1109 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1111 WalletTxToJSON(wtx, entry);
1112 ret.push_back(entry);
1117 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1118 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1121 if (pwalletMain->mapAddressBook.count(r.first))
1122 account = pwalletMain->mapAddressBook[r.first];
1123 if (fAllAccounts || (account == strAccount))
1126 entry.push_back(Pair("account", account));
1127 entry.push_back(Pair("address", r.first.ToString()));
1128 entry.push_back(Pair("category", "receive"));
1129 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1131 WalletTxToJSON(wtx, entry);
1132 ret.push_back(entry);
1137 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1139 bool fAllAccounts = (strAccount == string("*"));
1141 if (fAllAccounts || acentry.strAccount == strAccount)
1144 entry.push_back(Pair("account", acentry.strAccount));
1145 entry.push_back(Pair("category", "move"));
1146 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1147 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1148 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1149 entry.push_back(Pair("comment", acentry.strComment));
1150 ret.push_back(entry);
1154 Value listtransactions(const Array& params, bool fHelp)
1156 if (fHelp || params.size() > 3)
1157 throw runtime_error(
1158 "listtransactions [account] [count=10] [from=0]\n"
1159 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1161 string strAccount = "*";
1162 if (params.size() > 0)
1163 strAccount = params[0].get_str();
1165 if (params.size() > 1)
1166 nCount = params[1].get_int();
1168 if (params.size() > 2)
1169 nFrom = params[2].get_int();
1172 CWalletDB walletdb(pwalletMain->strWalletFile);
1174 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1175 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1176 typedef multimap<int64, TxPair > TxItems;
1179 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1181 CWalletTx* wtx = &((*it).second);
1182 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1184 list<CAccountingEntry> acentries;
1185 walletdb.ListAccountCreditDebit(strAccount, acentries);
1186 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1188 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1191 // Now: iterate backwards until we have nCount items to return:
1192 TxItems::reverse_iterator it = txByTime.rbegin();
1193 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1194 for (; it != txByTime.rend(); ++it)
1196 CWalletTx *const pwtx = (*it).second.first;
1198 ListTransactions(*pwtx, strAccount, 0, true, ret);
1199 CAccountingEntry *const pacentry = (*it).second.second;
1201 AcentryToJSON(*pacentry, strAccount, ret);
1203 if (ret.size() >= nCount) break;
1205 // ret is now newest to oldest
1207 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1208 if (ret.size() > nCount)
1210 Array::iterator last = ret.begin();
1211 std::advance(last, nCount);
1212 ret.erase(last, ret.end());
1214 std::reverse(ret.begin(), ret.end()); // oldest to newest
1219 Value listaccounts(const Array& params, bool fHelp)
1221 if (fHelp || params.size() > 1)
1222 throw runtime_error(
1223 "listaccounts [minconf=1]\n"
1224 "Returns Object that has account names as keys, account balances as values.");
1227 if (params.size() > 0)
1228 nMinDepth = params[0].get_int();
1230 map<string, int64> mapAccountBalances;
1231 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1232 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1233 mapAccountBalances[entry.second] = 0;
1236 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1238 const CWalletTx& wtx = (*it).second;
1239 int64 nGeneratedImmature, nGeneratedMature, nFee;
1240 string strSentAccount;
1241 list<pair<CBitcoinAddress, int64> > listReceived;
1242 list<pair<CBitcoinAddress, int64> > listSent;
1243 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1244 mapAccountBalances[strSentAccount] -= nFee;
1245 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1246 mapAccountBalances[strSentAccount] -= s.second;
1247 if (wtx.GetDepthInMainChain() >= nMinDepth)
1249 mapAccountBalances[""] += nGeneratedMature;
1250 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1251 if (pwalletMain->mapAddressBook.count(r.first))
1252 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1254 mapAccountBalances[""] += r.second;
1258 list<CAccountingEntry> acentries;
1259 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1260 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1261 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1264 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1265 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1270 Value listsinceblock(const Array& params, bool fHelp)
1273 throw runtime_error(
1274 "listsinceblock [blockid] [target-confirmations]\n"
1275 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1277 CBlockIndex *pindex = NULL;
1278 int target_confirms = 1;
1280 if (params.size() > 0)
1282 uint256 blockId = 0;
1284 blockId.SetHex(params[0].get_str());
1285 pindex = CBlockLocator(blockId).GetBlockIndex();
1288 if (params.size() > 1)
1290 target_confirms = params[1].get_int();
1292 if (target_confirms < 1)
1293 throw JSONRPCError(-8, "Invalid parameter");
1296 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1300 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1302 CWalletTx tx = (*it).second;
1304 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1305 ListTransactions(tx, "*", 0, true, transactions);
1310 if (target_confirms == 1)
1313 lastblock = hashBestChain;
1317 int target_height = pindexBest->nHeight + 1 - target_confirms;
1320 for (block = pindexBest;
1321 block && block->nHeight > target_height;
1322 block = block->pprev);
1324 lastblock = block ? block->GetBlockHash() : 0;
1328 ret.push_back(Pair("transactions", transactions));
1329 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1334 Value gettransaction(const Array& params, bool fHelp)
1336 if (fHelp || params.size() != 1)
1337 throw runtime_error(
1338 "gettransaction <txid>\n"
1339 "Get detailed information about <txid>");
1342 hash.SetHex(params[0].get_str());
1346 if (!pwalletMain->mapWallet.count(hash))
1347 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1348 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1350 int64 nCredit = wtx.GetCredit();
1351 int64 nDebit = wtx.GetDebit();
1352 int64 nNet = nCredit - nDebit;
1353 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1355 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1357 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1359 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1362 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1363 entry.push_back(Pair("details", details));
1369 Value backupwallet(const Array& params, bool fHelp)
1371 if (fHelp || params.size() != 1)
1372 throw runtime_error(
1373 "backupwallet <destination>\n"
1374 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1376 string strDest = params[0].get_str();
1377 BackupWallet(*pwalletMain, strDest);
1383 Value keypoolrefill(const Array& params, bool fHelp)
1385 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1386 throw runtime_error(
1388 "Fills the keypool, requires wallet passphrase to be set.");
1389 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1390 throw runtime_error(
1392 "Fills the keypool.");
1394 if (pwalletMain->IsLocked())
1395 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1397 pwalletMain->TopUpKeyPool();
1399 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1400 throw JSONRPCError(-4, "Error refreshing keypool.");
1406 void ThreadTopUpKeyPool(void* parg)
1408 pwalletMain->TopUpKeyPool();
1411 void ThreadCleanWalletPassphrase(void* parg)
1413 int64 nMyWakeTime = GetTime() + *((int*)parg);
1415 if (nWalletUnlockTime == 0)
1417 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1419 nWalletUnlockTime = nMyWakeTime;
1422 while (GetTime() < nWalletUnlockTime)
1423 Sleep(GetTime() - nWalletUnlockTime);
1425 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1427 nWalletUnlockTime = 0;
1432 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1434 if (nWalletUnlockTime < nMyWakeTime)
1435 nWalletUnlockTime = nMyWakeTime;
1441 pwalletMain->Lock();
1446 Value walletpassphrase(const Array& params, bool fHelp)
1448 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1449 throw runtime_error(
1450 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1451 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1452 "stakeonly is optional true/false allowing only stake creation.");
1455 if (!pwalletMain->IsCrypted())
1456 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1458 if (!pwalletMain->IsLocked())
1459 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1461 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1462 SecureString strWalletPass;
1463 strWalletPass.reserve(100);
1464 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1465 // Alternately, find a way to make params[0] mlock()'d to begin with.
1466 strWalletPass = params[0].get_str().c_str();
1468 if (strWalletPass.length() > 0)
1470 if (!pwalletMain->Unlock(strWalletPass))
1471 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1474 throw runtime_error(
1475 "walletpassphrase <passphrase> <timeout>\n"
1476 "Stores the wallet decryption key in memory for <timeout> seconds.");
1478 CreateThread(ThreadTopUpKeyPool, NULL);
1479 int* pnSleepTime = new int(params[1].get_int());
1480 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1482 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1483 if (params.size() > 2)
1484 fWalletUnlockStakeOnly = params[2].get_bool();
1486 fWalletUnlockStakeOnly = false;
1492 Value walletpassphrasechange(const Array& params, bool fHelp)
1494 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1495 throw runtime_error(
1496 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1497 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1500 if (!pwalletMain->IsCrypted())
1501 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1503 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1504 // Alternately, find a way to make params[0] mlock()'d to begin with.
1505 SecureString strOldWalletPass;
1506 strOldWalletPass.reserve(100);
1507 strOldWalletPass = params[0].get_str().c_str();
1509 SecureString strNewWalletPass;
1510 strNewWalletPass.reserve(100);
1511 strNewWalletPass = params[1].get_str().c_str();
1513 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1514 throw runtime_error(
1515 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1516 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1518 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1519 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1525 Value walletlock(const Array& params, bool fHelp)
1527 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1528 throw runtime_error(
1530 "Removes the wallet encryption key from memory, locking the wallet.\n"
1531 "After calling this method, you will need to call walletpassphrase again\n"
1532 "before being able to call any methods which require the wallet to be unlocked.");
1535 if (!pwalletMain->IsCrypted())
1536 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1538 pwalletMain->Lock();
1539 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1541 nWalletUnlockTime = 0;
1548 Value encryptwallet(const Array& params, bool fHelp)
1550 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1551 throw runtime_error(
1552 "encryptwallet <passphrase>\n"
1553 "Encrypts the wallet with <passphrase>.");
1556 if (pwalletMain->IsCrypted())
1557 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1560 // shutting down via RPC while the GUI is running does not work (yet):
1561 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1564 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1565 // Alternately, find a way to make params[0] mlock()'d to begin with.
1566 SecureString strWalletPass;
1567 strWalletPass.reserve(100);
1568 strWalletPass = params[0].get_str().c_str();
1570 if (strWalletPass.length() < 1)
1571 throw runtime_error(
1572 "encryptwallet <passphrase>\n"
1573 "Encrypts the wallet with <passphrase>.");
1575 if (!pwalletMain->EncryptWallet(strWalletPass))
1576 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1578 // BDB seems to have a bad habit of writing old data into
1579 // slack space in .dat files; that is bad if the old data is
1580 // unencrypted private keys. So:
1581 CreateThread(Shutdown, NULL);
1582 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1586 Value validateaddress(const Array& params, bool fHelp)
1588 if (fHelp || params.size() != 1)
1589 throw runtime_error(
1590 "validateaddress <ppcoinaddress>\n"
1591 "Return information about <ppcoinaddress>.");
1593 CBitcoinAddress address(params[0].get_str());
1594 bool isValid = address.IsValid();
1597 ret.push_back(Pair("isvalid", isValid));
1600 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1601 // version of the address:
1602 string currentAddress = address.ToString();
1603 ret.push_back(Pair("address", currentAddress));
1604 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1605 if (pwalletMain->mapAddressBook.count(address))
1606 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1612 Value getwork(const Array& params, bool fHelp)
1614 if (fHelp || params.size() > 1)
1615 throw runtime_error(
1617 "If [data] is not specified, returns formatted hash data to work on:\n"
1618 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1619 " \"data\" : block data\n"
1620 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1621 " \"target\" : little endian hash target\n"
1622 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1625 throw JSONRPCError(-9, "PPCoin is not connected!");
1627 if (IsInitialBlockDownload())
1628 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1630 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1631 static mapNewBlock_t mapNewBlock;
1632 static vector<CBlock*> vNewBlock;
1633 static CReserveKey reservekey(pwalletMain);
1635 if (params.size() == 0)
1638 static unsigned int nTransactionsUpdatedLast;
1639 static CBlockIndex* pindexPrev;
1640 static int64 nStart;
1641 static CBlock* pblock;
1642 if (pindexPrev != pindexBest ||
1643 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1645 if (pindexPrev != pindexBest)
1647 // Deallocate old blocks since they're obsolete now
1648 mapNewBlock.clear();
1649 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1653 nTransactionsUpdatedLast = nTransactionsUpdated;
1654 pindexPrev = pindexBest;
1658 pblock = CreateNewBlock(pwalletMain);
1660 throw JSONRPCError(-7, "Out of memory");
1661 vNewBlock.push_back(pblock);
1665 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1668 // Update nExtraNonce
1669 static unsigned int nExtraNonce = 0;
1670 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1673 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1675 // Prebuild hash buffers
1679 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1681 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1684 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1685 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1686 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1687 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1693 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1694 if (vchData.size() != 128)
1695 throw JSONRPCError(-8, "Invalid parameter");
1696 CBlock* pdata = (CBlock*)&vchData[0];
1699 for (int i = 0; i < 128/4; i++)
1700 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1703 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1705 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1707 pblock->nTime = pdata->nTime;
1708 pblock->nNonce = pdata->nNonce;
1709 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1710 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1712 return CheckWork(pblock, *pwalletMain, reservekey);
1717 Value getmemorypool(const Array& params, bool fHelp)
1719 if (fHelp || params.size() > 1)
1720 throw runtime_error(
1721 "getmemorypool [data]\n"
1722 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1723 " \"version\" : block version\n"
1724 " \"previousblockhash\" : hash of current highest block\n"
1725 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1726 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1727 " \"time\" : timestamp appropriate for next block\n"
1728 " \"bits\" : compressed target of next block\n"
1729 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1731 if (params.size() == 0)
1734 throw JSONRPCError(-9, "PPCoin is not connected!");
1736 if (IsInitialBlockDownload())
1737 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1739 static CReserveKey reservekey(pwalletMain);
1742 static unsigned int nTransactionsUpdatedLast;
1743 static CBlockIndex* pindexPrev;
1744 static int64 nStart;
1745 static CBlock* pblock;
1746 if (pindexPrev != pindexBest ||
1747 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1749 nTransactionsUpdatedLast = nTransactionsUpdated;
1750 pindexPrev = pindexBest;
1756 pblock = CreateNewBlock(pwalletMain);
1758 throw JSONRPCError(-7, "Out of memory");
1762 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1766 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1767 if(tx.IsCoinBase() || tx.IsCoinStake())
1773 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1777 result.push_back(Pair("version", pblock->nVersion));
1778 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1779 result.push_back(Pair("transactions", transactions));
1780 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1781 result.push_back(Pair("time", (int64_t)pblock->nTime));
1787 uBits.nBits = htonl((int32_t)pblock->nBits);
1788 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1795 CDataStream ssBlock(ParseHex(params[0].get_str()));
1799 return ProcessBlock(NULL, &pblock);
1804 // ppcoin: reset auto checkpoint
1805 Value resetcheckpoint(const Array& params, bool fHelp)
1807 if (fHelp || params.size() < 1 || params.size() > 1)
1808 throw runtime_error(
1809 "resetcheckpoint <checkpointheight>\n"
1810 "Reset automatic checkpoint to specified height.\n"
1811 "<checkpointheight> is the height of the new checkpoint block.\n");
1813 int nCheckpoint = params[0].get_int();
1814 if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1815 throw runtime_error(
1816 "invalid checkpoint height.\n"
1818 if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1819 throw runtime_error(
1820 "new checkpoint must be earlier than current auto checkpoint.\n"
1822 if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1823 throw runtime_error(
1824 "internal error - reset checkpoint failed.\n"
1831 // ppcoin: get branch point of alternative branch
1832 Value getbranchpoint(const Array& params, bool fHelp)
1834 if (fHelp || params.size() != 0)
1835 throw runtime_error(
1837 "Returns height of branch point of alternative branch.\n");
1840 if (Checkpoints::nBranchPoint > 0)
1841 result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1843 result.push_back(Pair("branchpoint", "none"));
1844 result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1849 // ppcoin: reserve balance from being staked for network protection
1850 Value reservebalance(const Array& params, bool fHelp)
1852 if (fHelp || params.size() > 2)
1853 throw runtime_error(
1854 "reservebalance [<reserve> [amount]]\n"
1855 "<reserve> is true or false to turn balance reserve on or off.\n"
1856 "<amount> is a real and rounded to cent.\n"
1857 "Set reserve amount not participating in network protection.\n"
1858 "If no parameters provided current setting is printed.\n");
1860 if (params.size() > 0)
1862 bool fReserve = params[0].get_bool();
1865 if (params.size() == 1)
1866 throw runtime_error("must provide amount to reserve balance.\n");
1867 int64 nAmount = AmountFromValue(params[1]);
1868 nAmount = (nAmount / CENT) * CENT; // round to cent
1870 throw runtime_error("amount cannot be negative.\n");
1871 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1875 if (params.size() > 1)
1876 throw runtime_error("cannot specify amount to turn off reserve.\n");
1877 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1882 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1883 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1896 pair<string, rpcfn_type> pCallTable[] =
1898 make_pair("help", &help),
1899 make_pair("stop", &stop),
1900 make_pair("getblockcount", &getblockcount),
1901 make_pair("getblocknumber", &getblocknumber),
1902 make_pair("getconnectioncount", &getconnectioncount),
1903 make_pair("getdifficulty", &getdifficulty),
1904 make_pair("getgenerate", &getgenerate),
1905 make_pair("setgenerate", &setgenerate),
1906 make_pair("gethashespersec", &gethashespersec),
1907 make_pair("getinfo", &getinfo),
1908 make_pair("getnewaddress", &getnewaddress),
1909 make_pair("getaccountaddress", &getaccountaddress),
1910 make_pair("setaccount", &setaccount),
1911 make_pair("getaccount", &getaccount),
1912 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1913 make_pair("sendtoaddress", &sendtoaddress),
1914 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1915 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1916 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1917 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1918 make_pair("backupwallet", &backupwallet),
1919 make_pair("keypoolrefill", &keypoolrefill),
1920 make_pair("walletpassphrase", &walletpassphrase),
1921 make_pair("walletpassphrasechange", &walletpassphrasechange),
1922 make_pair("walletlock", &walletlock),
1923 make_pair("encryptwallet", &encryptwallet),
1924 make_pair("validateaddress", &validateaddress),
1925 make_pair("getbalance", &getbalance),
1926 make_pair("move", &movecmd),
1927 make_pair("sendfrom", &sendfrom),
1928 make_pair("sendmany", &sendmany),
1929 make_pair("gettransaction", &gettransaction),
1930 make_pair("listtransactions", &listtransactions),
1931 make_pair("signmessage", &signmessage),
1932 make_pair("verifymessage", &verifymessage),
1933 make_pair("getwork", &getwork),
1934 make_pair("listaccounts", &listaccounts),
1935 make_pair("settxfee", &settxfee),
1936 make_pair("getmemorypool", &getmemorypool),
1937 make_pair("listsinceblock", &listsinceblock),
1938 make_pair("resetcheckpoint", &resetcheckpoint),
1939 make_pair("getbranchpoint", &getbranchpoint),
1940 make_pair("reservebalance", &reservebalance),
1942 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1944 string pAllowInSafeMode[] =
1949 "getblocknumber", // deprecated
1950 "getconnectioncount",
1957 "getaccountaddress",
1959 "getaddressesbyaccount",
1970 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1978 // This ain't Apache. We're just using HTTP header for the length field
1979 // and to be compatible with other JSON-RPC implementations.
1982 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1985 s << "POST / HTTP/1.1\r\n"
1986 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1987 << "Host: 127.0.0.1\r\n"
1988 << "Content-Type: application/json\r\n"
1989 << "Content-Length: " << strMsg.size() << "\r\n"
1990 << "Connection: close\r\n"
1991 << "Accept: application/json\r\n";
1992 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1993 s << item.first << ": " << item.second << "\r\n";
1994 s << "\r\n" << strMsg;
1999 string rfc1123Time()
2004 struct tm* now_gmt = gmtime(&now);
2005 string locale(setlocale(LC_TIME, NULL));
2006 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2007 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2008 setlocale(LC_TIME, locale.c_str());
2009 return string(buffer);
2012 static string HTTPReply(int nStatus, const string& strMsg)
2015 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2017 "Server: ppcoin-json-rpc/%s\r\n"
2018 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2019 "Content-Type: text/html\r\n"
2020 "Content-Length: 296\r\n"
2022 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2023 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2026 "<TITLE>Error</TITLE>\r\n"
2027 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2029 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2030 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2031 const char *cStatus;
2032 if (nStatus == 200) cStatus = "OK";
2033 else if (nStatus == 400) cStatus = "Bad Request";
2034 else if (nStatus == 403) cStatus = "Forbidden";
2035 else if (nStatus == 404) cStatus = "Not Found";
2036 else if (nStatus == 500) cStatus = "Internal Server Error";
2039 "HTTP/1.1 %d %s\r\n"
2041 "Connection: close\r\n"
2042 "Content-Length: %d\r\n"
2043 "Content-Type: application/json\r\n"
2044 "Server: ppcoin-json-rpc/%s\r\n"
2049 rfc1123Time().c_str(),
2051 FormatFullVersion().c_str(),
2055 int ReadHTTPStatus(std::basic_istream<char>& stream)
2058 getline(stream, str);
2059 vector<string> vWords;
2060 boost::split(vWords, str, boost::is_any_of(" "));
2061 if (vWords.size() < 2)
2063 return atoi(vWords[1].c_str());
2066 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2072 std::getline(stream, str);
2073 if (str.empty() || str == "\r")
2075 string::size_type nColon = str.find(":");
2076 if (nColon != string::npos)
2078 string strHeader = str.substr(0, nColon);
2079 boost::trim(strHeader);
2080 boost::to_lower(strHeader);
2081 string strValue = str.substr(nColon+1);
2082 boost::trim(strValue);
2083 mapHeadersRet[strHeader] = strValue;
2084 if (strHeader == "content-length")
2085 nLen = atoi(strValue.c_str());
2091 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2093 mapHeadersRet.clear();
2097 int nStatus = ReadHTTPStatus(stream);
2100 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2101 if (nLen < 0 || nLen > MAX_SIZE)
2107 vector<char> vch(nLen);
2108 stream.read(&vch[0], nLen);
2109 strMessageRet = string(vch.begin(), vch.end());
2115 bool HTTPAuthorized(map<string, string>& mapHeaders)
2117 string strAuth = mapHeaders["authorization"];
2118 if (strAuth.substr(0,6) != "Basic ")
2120 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2121 string strUserPass = DecodeBase64(strUserPass64);
2122 return strUserPass == strRPCUserColonPass;
2126 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2127 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2128 // unspecified (HTTP errors and contents of 'error').
2130 // 1.0 spec: http://json-rpc.org/wiki/specification
2131 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2132 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2135 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2138 request.push_back(Pair("method", strMethod));
2139 request.push_back(Pair("params", params));
2140 request.push_back(Pair("id", id));
2141 return write_string(Value(request), false) + "\n";
2144 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2147 if (error.type() != null_type)
2148 reply.push_back(Pair("result", Value::null));
2150 reply.push_back(Pair("result", result));
2151 reply.push_back(Pair("error", error));
2152 reply.push_back(Pair("id", id));
2153 return write_string(Value(reply), false) + "\n";
2156 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2158 // Send error reply from json-rpc error object
2160 int code = find_value(objError, "code").get_int();
2161 if (code == -32600) nStatus = 400;
2162 else if (code == -32601) nStatus = 404;
2163 string strReply = JSONRPCReply(Value::null, objError, id);
2164 stream << HTTPReply(nStatus, strReply) << std::flush;
2167 bool ClientAllowed(const string& strAddress)
2169 if (strAddress == asio::ip::address_v4::loopback().to_string())
2171 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2172 BOOST_FOREACH(string strAllow, vAllow)
2173 if (WildcardMatch(strAddress, strAllow))
2180 // IOStream device that speaks SSL but can also speak non-SSL
2182 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2184 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2186 fUseSSL = fUseSSLIn;
2187 fNeedHandshake = fUseSSLIn;
2190 void handshake(ssl::stream_base::handshake_type role)
2192 if (!fNeedHandshake) return;
2193 fNeedHandshake = false;
2194 stream.handshake(role);
2196 std::streamsize read(char* s, std::streamsize n)
2198 handshake(ssl::stream_base::server); // HTTPS servers read first
2199 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2200 return stream.next_layer().read_some(asio::buffer(s, n));
2202 std::streamsize write(const char* s, std::streamsize n)
2204 handshake(ssl::stream_base::client); // HTTPS clients write first
2205 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2206 return asio::write(stream.next_layer(), asio::buffer(s, n));
2208 bool connect(const std::string& server, const std::string& port)
2210 ip::tcp::resolver resolver(stream.get_io_service());
2211 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2212 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2213 ip::tcp::resolver::iterator end;
2214 boost::system::error_code error = asio::error::host_not_found;
2215 while (error && endpoint_iterator != end)
2217 stream.lowest_layer().close();
2218 stream.lowest_layer().connect(*endpoint_iterator++, error);
2226 bool fNeedHandshake;
2232 void ThreadRPCServer(void* parg)
2234 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2237 vnThreadsRunning[4]++;
2238 ThreadRPCServer2(parg);
2239 vnThreadsRunning[4]--;
2241 catch (std::exception& e) {
2242 vnThreadsRunning[4]--;
2243 PrintException(&e, "ThreadRPCServer()");
2245 vnThreadsRunning[4]--;
2246 PrintException(NULL, "ThreadRPCServer()");
2248 printf("ThreadRPCServer exiting\n");
2251 void ThreadRPCServer2(void* parg)
2253 printf("ThreadRPCServer started\n");
2255 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2256 if (strRPCUserColonPass == ":")
2258 string strWhatAmI = "To use ppcoind";
2259 if (mapArgs.count("-server"))
2260 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2261 else if (mapArgs.count("-daemon"))
2262 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2264 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2265 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2267 GetConfigFile().c_str());
2269 CreateThread(Shutdown, NULL);
2274 bool fUseSSL = GetBoolArg("-rpcssl");
2275 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2277 asio::io_service io_service;
2278 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2279 ip::tcp::acceptor acceptor(io_service, endpoint);
2281 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2284 ssl::context context(io_service, ssl::context::sslv23);
2287 context.set_options(ssl::context::no_sslv2);
2288 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2289 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2290 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2291 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2292 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2293 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2294 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2295 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2297 string ciphers = GetArg("-rpcsslciphers",
2298 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2299 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2303 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2308 // Accept connection
2310 SSLStream sslStream(io_service, context);
2311 SSLIOStreamDevice d(sslStream, fUseSSL);
2312 iostreams::stream<SSLIOStreamDevice> stream(d);
2314 ip::tcp::iostream stream;
2317 ip::tcp::endpoint peer;
2318 vnThreadsRunning[4]--;
2320 acceptor.accept(sslStream.lowest_layer(), peer);
2322 acceptor.accept(*stream.rdbuf(), peer);
2324 vnThreadsRunning[4]++;
2328 // Restrict callers by IP
2329 if (!ClientAllowed(peer.address().to_string()))
2331 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2333 stream << HTTPReply(403, "") << std::flush;
2337 map<string, string> mapHeaders;
2340 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2341 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2344 printf("ThreadRPCServer ReadHTTP timeout\n");
2348 // Check authorization
2349 if (mapHeaders.count("authorization") == 0)
2351 stream << HTTPReply(401, "") << std::flush;
2354 if (!HTTPAuthorized(mapHeaders))
2356 // Deter brute-forcing short passwords
2357 if (mapArgs["-rpcpassword"].size() < 15)
2360 stream << HTTPReply(401, "") << std::flush;
2361 printf("ThreadRPCServer incorrect password attempt\n");
2365 Value id = Value::null;
2370 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2371 throw JSONRPCError(-32700, "Parse error");
2372 const Object& request = valRequest.get_obj();
2374 // Parse id now so errors from here on will have the id
2375 id = find_value(request, "id");
2378 Value valMethod = find_value(request, "method");
2379 if (valMethod.type() == null_type)
2380 throw JSONRPCError(-32600, "Missing method");
2381 if (valMethod.type() != str_type)
2382 throw JSONRPCError(-32600, "Method must be a string");
2383 string strMethod = valMethod.get_str();
2384 if (strMethod != "getwork" && strMethod != "getmemorypool")
2385 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2388 Value valParams = find_value(request, "params");
2390 if (valParams.type() == array_type)
2391 params = valParams.get_array();
2392 else if (valParams.type() == null_type)
2395 throw JSONRPCError(-32600, "Params must be an array");
2398 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2399 if (mi == mapCallTable.end())
2400 throw JSONRPCError(-32601, "Method not found");
2402 // Observe safe mode
2403 string strWarning = GetWarnings("rpc");
2404 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2405 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2411 CRITICAL_BLOCK(cs_main)
2412 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2413 result = (*(*mi).second)(params, false);
2416 string strReply = JSONRPCReply(result, Value::null, id);
2417 stream << HTTPReply(200, strReply) << std::flush;
2419 catch (std::exception& e)
2421 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2424 catch (Object& objError)
2426 ErrorReply(stream, objError, id);
2428 catch (std::exception& e)
2430 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2438 Object CallRPC(const string& strMethod, const Array& params)
2440 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2441 throw runtime_error(strprintf(
2442 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2443 "If the file does not exist, create it with owner-readable-only file permissions."),
2444 GetConfigFile().c_str()));
2446 // Connect to localhost
2447 bool fUseSSL = GetBoolArg("-rpcssl");
2449 asio::io_service io_service;
2450 ssl::context context(io_service, ssl::context::sslv23);
2451 context.set_options(ssl::context::no_sslv2);
2452 SSLStream sslStream(io_service, context);
2453 SSLIOStreamDevice d(sslStream, fUseSSL);
2454 iostreams::stream<SSLIOStreamDevice> stream(d);
2455 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2456 throw runtime_error("couldn't connect to server");
2459 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2461 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2463 throw runtime_error("couldn't connect to server");
2467 // HTTP basic authentication
2468 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2469 map<string, string> mapRequestHeaders;
2470 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2473 string strRequest = JSONRPCRequest(strMethod, params, 1);
2474 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2475 stream << strPost << std::flush;
2478 map<string, string> mapHeaders;
2480 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2482 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2483 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2484 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2485 else if (strReply.empty())
2486 throw runtime_error("no response from server");
2490 if (!read_string(strReply, valReply))
2491 throw runtime_error("couldn't parse reply from server");
2492 const Object& reply = valReply.get_obj();
2494 throw runtime_error("expected reply to have result, error and id properties");
2502 template<typename T>
2503 void ConvertTo(Value& value)
2505 if (value.type() == str_type)
2507 // reinterpret string as unquoted json value
2509 if (!read_string(value.get_str(), value2))
2510 throw runtime_error("type mismatch");
2511 value = value2.get_value<T>();
2515 value = value.get_value<T>();
2519 int CommandLineRPC(int argc, char *argv[])
2526 while (argc > 1 && IsSwitchChar(argv[1][0]))
2534 throw runtime_error("too few parameters");
2535 string strMethod = argv[1];
2537 // Parameters default to strings
2539 for (int i = 2; i < argc; i++)
2540 params.push_back(argv[i]);
2541 int n = params.size();
2544 // Special case non-string parameter types
2546 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2547 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2548 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2549 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2550 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2551 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2552 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2553 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2554 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2555 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2556 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2557 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2558 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2559 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2560 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2561 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2562 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2563 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2564 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2565 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2566 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2567 if (strMethod == "sendmany" && n > 1)
2569 string s = params[1].get_str();
2571 if (!read_string(s, v) || v.type() != obj_type)
2572 throw runtime_error("type mismatch");
2573 params[1] = v.get_obj();
2575 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2576 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2577 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2578 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2581 Object reply = CallRPC(strMethod, params);
2584 const Value& result = find_value(reply, "result");
2585 const Value& error = find_value(reply, "error");
2587 if (error.type() != null_type)
2590 strPrint = "error: " + write_string(error, false);
2591 int code = find_value(error.get_obj(), "code").get_int();
2597 if (result.type() == null_type)
2599 else if (result.type() == str_type)
2600 strPrint = result.get_str();
2602 strPrint = write_string(result, true);
2605 catch (std::exception& e)
2607 strPrint = string("error: ") + e.what();
2612 PrintException(NULL, "CommandLineRPC()");
2617 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2626 int main(int argc, char *argv[])
2629 // Turn off microsoft heap dump noise
2630 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2631 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2633 setbuf(stdin, NULL);
2634 setbuf(stdout, NULL);
2635 setbuf(stderr, NULL);
2639 if (argc >= 2 && string(argv[1]) == "-server")
2641 printf("server ready\n");
2642 ThreadRPCServer(NULL);
2646 return CommandLineRPC(argc, argv);
2649 catch (std::exception& e) {
2650 PrintException(&e, "main()");
2652 PrintException(NULL, "main()");