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("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
312 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
313 obj.push_back(Pair("blocks", (int)nBestHeight));
314 obj.push_back(Pair("connections", (int)vNodes.size()));
315 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
316 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
317 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
318 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
319 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
320 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
321 obj.push_back(Pair("testnet", fTestNet));
322 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
323 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
324 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
325 if (pwalletMain->IsCrypted())
326 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
327 obj.push_back(Pair("errors", GetWarnings("statusbar")));
332 Value getnewaddress(const Array& params, bool fHelp)
334 if (fHelp || params.size() > 1)
336 "getnewaddress [account]\n"
337 "Returns a new ppcoin address for receiving payments. "
338 "If [account] is specified (recommended), it is added to the address book "
339 "so payments received with the address will be credited to [account].");
341 // Parse the account first so we don't generate a key if there's an error
343 if (params.size() > 0)
344 strAccount = AccountFromValue(params[0]);
346 if (!pwalletMain->IsLocked())
347 pwalletMain->TopUpKeyPool();
349 // Generate a new key that is added to wallet
350 std::vector<unsigned char> newKey;
351 if (!pwalletMain->GetKeyFromPool(newKey, false))
352 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
353 CBitcoinAddress address(newKey);
355 pwalletMain->SetAddressBookName(address, strAccount);
357 return address.ToString();
361 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
363 CWalletDB walletdb(pwalletMain->strWalletFile);
366 walletdb.ReadAccount(strAccount, account);
368 bool bKeyUsed = false;
370 // Check if the current key has been used
371 if (!account.vchPubKey.empty())
373 CScript scriptPubKey;
374 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
375 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
376 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
379 const CWalletTx& wtx = (*it).second;
380 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
381 if (txout.scriptPubKey == scriptPubKey)
386 // Generate a new key
387 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
389 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
390 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
392 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
393 walletdb.WriteAccount(strAccount, account);
396 return CBitcoinAddress(account.vchPubKey);
399 Value getaccountaddress(const Array& params, bool fHelp)
401 if (fHelp || params.size() != 1)
403 "getaccountaddress <account>\n"
404 "Returns the current ppcoin address for receiving payments to this account.");
406 // Parse the account first so we don't generate a key if there's an error
407 string strAccount = AccountFromValue(params[0]);
411 ret = GetAccountAddress(strAccount).ToString();
418 Value setaccount(const Array& params, bool fHelp)
420 if (fHelp || params.size() < 1 || params.size() > 2)
422 "setaccount <ppcoinaddress> <account>\n"
423 "Sets the account associated with the given address.");
425 CBitcoinAddress address(params[0].get_str());
426 if (!address.IsValid())
427 throw JSONRPCError(-5, "Invalid ppcoin address");
431 if (params.size() > 1)
432 strAccount = AccountFromValue(params[1]);
434 // Detect when changing the account of an address that is the 'unused current key' of another account:
435 if (pwalletMain->mapAddressBook.count(address))
437 string strOldAccount = pwalletMain->mapAddressBook[address];
438 if (address == GetAccountAddress(strOldAccount))
439 GetAccountAddress(strOldAccount, true);
442 pwalletMain->SetAddressBookName(address, strAccount);
448 Value getaccount(const Array& params, bool fHelp)
450 if (fHelp || params.size() != 1)
452 "getaccount <ppcoinaddress>\n"
453 "Returns the account associated with the given address.");
455 CBitcoinAddress address(params[0].get_str());
456 if (!address.IsValid())
457 throw JSONRPCError(-5, "Invalid ppcoin address");
460 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
461 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
462 strAccount = (*mi).second;
467 Value getaddressesbyaccount(const Array& params, bool fHelp)
469 if (fHelp || params.size() != 1)
471 "getaddressesbyaccount <account>\n"
472 "Returns the list of addresses for the given account.");
474 string strAccount = AccountFromValue(params[0]);
476 // Find all addresses that have the given account
478 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
480 const CBitcoinAddress& address = item.first;
481 const string& strName = item.second;
482 if (strName == strAccount)
483 ret.push_back(address.ToString());
488 Value settxfee(const Array& params, bool fHelp)
490 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
492 "settxfee <amount>\n"
493 "<amount> is a real and is rounded to 0.01 (cent)\n"
494 "Minimum and default transaction fee per KB is 1 cent");
496 nTransactionFee = AmountFromValue(params[0]);
497 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
501 Value sendtoaddress(const Array& params, bool fHelp)
503 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
505 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
506 "<amount> is a real and is rounded to the nearest 0.000001\n"
507 "requires wallet passphrase to be set with walletpassphrase first");
508 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
510 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
511 "<amount> is a real and is rounded to the nearest 0.000001");
513 CBitcoinAddress address(params[0].get_str());
514 if (!address.IsValid())
515 throw JSONRPCError(-5, "Invalid ppcoin address");
518 int64 nAmount = AmountFromValue(params[1]);
522 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
523 wtx.mapValue["comment"] = params[2].get_str();
524 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
525 wtx.mapValue["to"] = params[3].get_str();
527 if (pwalletMain->IsLocked())
528 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
530 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
532 throw JSONRPCError(-4, strError);
534 return wtx.GetHash().GetHex();
537 static const string strMessageMagic = "Bitcoin Signed Message:\n";
539 Value signmessage(const Array& params, bool fHelp)
541 if (fHelp || params.size() != 2)
543 "signmessage <ppcoinaddress> <message>\n"
544 "Sign a message with the private key of an address");
546 if (pwalletMain->IsLocked())
547 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
549 string strAddress = params[0].get_str();
550 string strMessage = params[1].get_str();
552 CBitcoinAddress addr(strAddress);
554 throw JSONRPCError(-3, "Invalid address");
557 if (!pwalletMain->GetKey(addr, key))
558 throw JSONRPCError(-4, "Private key not available");
560 CDataStream ss(SER_GETHASH);
561 ss << strMessageMagic;
564 vector<unsigned char> vchSig;
565 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
566 throw JSONRPCError(-5, "Sign failed");
568 return EncodeBase64(&vchSig[0], vchSig.size());
571 Value verifymessage(const Array& params, bool fHelp)
573 if (fHelp || params.size() != 3)
575 "verifymessage <ppcoinaddress> <signature> <message>\n"
576 "Verify a signed message");
578 string strAddress = params[0].get_str();
579 string strSign = params[1].get_str();
580 string strMessage = params[2].get_str();
582 CBitcoinAddress addr(strAddress);
584 throw JSONRPCError(-3, "Invalid address");
586 bool fInvalid = false;
587 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
590 throw JSONRPCError(-5, "Malformed base64 encoding");
592 CDataStream ss(SER_GETHASH);
593 ss << strMessageMagic;
597 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
600 return (key.GetAddress() == addr);
604 Value getreceivedbyaddress(const Array& params, bool fHelp)
606 if (fHelp || params.size() < 1 || params.size() > 2)
608 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
609 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
612 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
613 CScript scriptPubKey;
614 if (!address.IsValid())
615 throw JSONRPCError(-5, "Invalid ppcoin address");
616 scriptPubKey.SetBitcoinAddress(address);
617 if (!IsMine(*pwalletMain,scriptPubKey))
620 // Minimum confirmations
622 if (params.size() > 1)
623 nMinDepth = params[1].get_int();
627 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
629 const CWalletTx& wtx = (*it).second;
630 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
633 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
634 if (txout.scriptPubKey == scriptPubKey)
635 if (wtx.GetDepthInMainChain() >= nMinDepth)
636 nAmount += txout.nValue;
639 return ValueFromAmount(nAmount);
643 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
645 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
647 const CBitcoinAddress& address = item.first;
648 const string& strName = item.second;
649 if (strName == strAccount)
650 setAddress.insert(address);
655 Value getreceivedbyaccount(const Array& params, bool fHelp)
657 if (fHelp || params.size() < 1 || params.size() > 2)
659 "getreceivedbyaccount <account> [minconf=1]\n"
660 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
662 // Minimum confirmations
664 if (params.size() > 1)
665 nMinDepth = params[1].get_int();
667 // Get the set of pub keys that have the label
668 string strAccount = AccountFromValue(params[0]);
669 set<CBitcoinAddress> setAddress;
670 GetAccountAddresses(strAccount, setAddress);
674 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
676 const CWalletTx& wtx = (*it).second;
677 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
680 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
682 CBitcoinAddress address;
683 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
684 if (wtx.GetDepthInMainChain() >= nMinDepth)
685 nAmount += txout.nValue;
689 return (double)nAmount / (double)COIN;
693 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
697 // Tally wallet transactions
698 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
700 const CWalletTx& wtx = (*it).second;
704 int64 nGenerated, nReceived, nSent, nFee;
705 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
707 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
708 nBalance += nReceived;
709 nBalance += nGenerated - nSent - nFee;
712 // Tally internal accounting entries
713 nBalance += walletdb.GetAccountCreditDebit(strAccount);
718 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
720 CWalletDB walletdb(pwalletMain->strWalletFile);
721 return GetAccountBalance(walletdb, strAccount, nMinDepth);
725 Value getbalance(const Array& params, bool fHelp)
727 if (fHelp || params.size() > 2)
729 "getbalance [account] [minconf=1]\n"
730 "If [account] is not specified, returns the server's total available balance.\n"
731 "If [account] is specified, returns the balance in the account.");
733 if (params.size() == 0)
734 return ValueFromAmount(pwalletMain->GetBalance());
737 if (params.size() > 1)
738 nMinDepth = params[1].get_int();
740 if (params[0].get_str() == "*") {
741 // Calculate total balance a different way from GetBalance()
742 // (GetBalance() sums up all unspent TxOuts)
743 // getbalance and getbalance '*' should always return the same number.
745 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
747 const CWalletTx& wtx = (*it).second;
751 int64 allGeneratedImmature, allGeneratedMature, allFee;
752 allGeneratedImmature = allGeneratedMature = allFee = 0;
753 string strSentAccount;
754 list<pair<CBitcoinAddress, int64> > listReceived;
755 list<pair<CBitcoinAddress, int64> > listSent;
756 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
757 if (wtx.GetDepthInMainChain() >= nMinDepth)
758 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
759 nBalance += r.second;
760 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
761 nBalance -= r.second;
763 nBalance += allGeneratedMature;
765 return ValueFromAmount(nBalance);
768 string strAccount = AccountFromValue(params[0]);
770 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
772 return ValueFromAmount(nBalance);
776 Value movecmd(const Array& params, bool fHelp)
778 if (fHelp || params.size() < 3 || params.size() > 5)
780 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
781 "Move from one account in your wallet to another.");
783 string strFrom = AccountFromValue(params[0]);
784 string strTo = AccountFromValue(params[1]);
785 int64 nAmount = AmountFromValue(params[2]);
786 if (params.size() > 3)
787 // unused parameter, used to be nMinDepth, keep type-checking it though
788 (void)params[3].get_int();
790 if (params.size() > 4)
791 strComment = params[4].get_str();
793 CWalletDB walletdb(pwalletMain->strWalletFile);
796 int64 nNow = GetAdjustedTime();
799 CAccountingEntry debit;
800 debit.strAccount = strFrom;
801 debit.nCreditDebit = -nAmount;
803 debit.strOtherAccount = strTo;
804 debit.strComment = strComment;
805 walletdb.WriteAccountingEntry(debit);
808 CAccountingEntry credit;
809 credit.strAccount = strTo;
810 credit.nCreditDebit = nAmount;
812 credit.strOtherAccount = strFrom;
813 credit.strComment = strComment;
814 walletdb.WriteAccountingEntry(credit);
816 walletdb.TxnCommit();
822 Value sendfrom(const Array& params, bool fHelp)
824 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
826 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
827 "<amount> is a real and is rounded to the nearest 0.000001\n"
828 "requires wallet passphrase to be set with walletpassphrase first");
829 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
831 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
832 "<amount> is a real and is rounded to the nearest 0.000001");
834 string strAccount = AccountFromValue(params[0]);
835 CBitcoinAddress address(params[1].get_str());
836 if (!address.IsValid())
837 throw JSONRPCError(-5, "Invalid ppcoin address");
838 int64 nAmount = AmountFromValue(params[2]);
840 if (params.size() > 3)
841 nMinDepth = params[3].get_int();
844 wtx.strFromAccount = strAccount;
845 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
846 wtx.mapValue["comment"] = params[4].get_str();
847 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
848 wtx.mapValue["to"] = params[5].get_str();
850 if (pwalletMain->IsLocked())
851 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
854 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
855 if (nAmount > nBalance)
856 throw JSONRPCError(-6, "Account has insufficient funds");
859 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
861 throw JSONRPCError(-4, strError);
863 return wtx.GetHash().GetHex();
867 Value sendmany(const Array& params, bool fHelp)
869 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
871 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
872 "amounts are double-precision floating point numbers\n"
873 "requires wallet passphrase to be set with walletpassphrase first");
874 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
876 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
877 "amounts are double-precision floating point numbers");
879 string strAccount = AccountFromValue(params[0]);
880 Object sendTo = params[1].get_obj();
882 if (params.size() > 2)
883 nMinDepth = params[2].get_int();
886 wtx.strFromAccount = strAccount;
887 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
888 wtx.mapValue["comment"] = params[3].get_str();
890 set<CBitcoinAddress> setAddress;
891 vector<pair<CScript, int64> > vecSend;
893 int64 totalAmount = 0;
894 BOOST_FOREACH(const Pair& s, sendTo)
896 CBitcoinAddress address(s.name_);
897 if (!address.IsValid())
898 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
900 if (setAddress.count(address))
901 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
902 setAddress.insert(address);
904 CScript scriptPubKey;
905 scriptPubKey.SetBitcoinAddress(address);
906 int64 nAmount = AmountFromValue(s.value_);
907 totalAmount += nAmount;
909 vecSend.push_back(make_pair(scriptPubKey, nAmount));
912 if (pwalletMain->IsLocked())
913 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
914 if (fWalletUnlockStakeOnly)
915 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
918 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
919 if (totalAmount > nBalance)
920 throw JSONRPCError(-6, "Account has insufficient funds");
923 CReserveKey keyChange(pwalletMain);
924 int64 nFeeRequired = 0;
925 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
928 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
929 throw JSONRPCError(-6, "Insufficient funds");
930 throw JSONRPCError(-4, "Transaction creation failed");
932 if (!pwalletMain->CommitTransaction(wtx, keyChange))
933 throw JSONRPCError(-4, "Transaction commit failed");
935 return wtx.GetHash().GetHex();
950 Value ListReceived(const Array& params, bool fByAccounts)
952 // Minimum confirmations
954 if (params.size() > 0)
955 nMinDepth = params[0].get_int();
957 // Whether to include empty accounts
958 bool fIncludeEmpty = false;
959 if (params.size() > 1)
960 fIncludeEmpty = params[1].get_bool();
963 map<CBitcoinAddress, tallyitem> mapTally;
964 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
966 const CWalletTx& wtx = (*it).second;
967 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
970 int nDepth = wtx.GetDepthInMainChain();
971 if (nDepth < nMinDepth)
974 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
976 CBitcoinAddress address;
977 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
980 tallyitem& item = mapTally[address];
981 item.nAmount += txout.nValue;
982 item.nConf = min(item.nConf, nDepth);
988 map<string, tallyitem> mapAccountTally;
989 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
991 const CBitcoinAddress& address = item.first;
992 const string& strAccount = item.second;
993 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
994 if (it == mapTally.end() && !fIncludeEmpty)
999 if (it != mapTally.end())
1001 nAmount = (*it).second.nAmount;
1002 nConf = (*it).second.nConf;
1007 tallyitem& item = mapAccountTally[strAccount];
1008 item.nAmount += nAmount;
1009 item.nConf = min(item.nConf, nConf);
1014 obj.push_back(Pair("address", address.ToString()));
1015 obj.push_back(Pair("account", strAccount));
1016 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1017 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1024 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1026 int64 nAmount = (*it).second.nAmount;
1027 int nConf = (*it).second.nConf;
1029 obj.push_back(Pair("account", (*it).first));
1030 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1031 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1039 Value listreceivedbyaddress(const Array& params, bool fHelp)
1041 if (fHelp || params.size() > 2)
1042 throw runtime_error(
1043 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1044 "[minconf] is the minimum number of confirmations before payments are included.\n"
1045 "[includeempty] whether to include addresses that haven't received any payments.\n"
1046 "Returns an array of objects containing:\n"
1047 " \"address\" : receiving address\n"
1048 " \"account\" : the account of the receiving address\n"
1049 " \"amount\" : total amount received by the address\n"
1050 " \"confirmations\" : number of confirmations of the most recent transaction included");
1052 return ListReceived(params, false);
1055 Value listreceivedbyaccount(const Array& params, bool fHelp)
1057 if (fHelp || params.size() > 2)
1058 throw runtime_error(
1059 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1060 "[minconf] is the minimum number of confirmations before payments are included.\n"
1061 "[includeempty] whether to include accounts that haven't received any payments.\n"
1062 "Returns an array of objects containing:\n"
1063 " \"account\" : the account of the receiving addresses\n"
1064 " \"amount\" : total amount received by addresses with this account\n"
1065 " \"confirmations\" : number of confirmations of the most recent transaction included");
1067 return ListReceived(params, true);
1070 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1072 int64 nGeneratedImmature, nGeneratedMature, nFee;
1073 string strSentAccount;
1074 list<pair<CBitcoinAddress, int64> > listReceived;
1075 list<pair<CBitcoinAddress, int64> > listSent;
1076 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1078 bool fAllAccounts = (strAccount == string("*"));
1080 // Generated blocks assigned to account ""
1081 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1084 entry.push_back(Pair("account", string("")));
1085 if (nGeneratedImmature)
1087 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1088 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1092 entry.push_back(Pair("category", "generate"));
1093 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1096 WalletTxToJSON(wtx, entry);
1097 ret.push_back(entry);
1101 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1103 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1106 entry.push_back(Pair("account", strSentAccount));
1107 entry.push_back(Pair("address", s.first.ToString()));
1108 entry.push_back(Pair("category", "send"));
1109 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1110 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1112 WalletTxToJSON(wtx, entry);
1113 ret.push_back(entry);
1118 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1119 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1122 if (pwalletMain->mapAddressBook.count(r.first))
1123 account = pwalletMain->mapAddressBook[r.first];
1124 if (fAllAccounts || (account == strAccount))
1127 entry.push_back(Pair("account", account));
1128 entry.push_back(Pair("address", r.first.ToString()));
1129 entry.push_back(Pair("category", "receive"));
1130 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1132 WalletTxToJSON(wtx, entry);
1133 ret.push_back(entry);
1138 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1140 bool fAllAccounts = (strAccount == string("*"));
1142 if (fAllAccounts || acentry.strAccount == strAccount)
1145 entry.push_back(Pair("account", acentry.strAccount));
1146 entry.push_back(Pair("category", "move"));
1147 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1148 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1149 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1150 entry.push_back(Pair("comment", acentry.strComment));
1151 ret.push_back(entry);
1155 Value listtransactions(const Array& params, bool fHelp)
1157 if (fHelp || params.size() > 3)
1158 throw runtime_error(
1159 "listtransactions [account] [count=10] [from=0]\n"
1160 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1162 string strAccount = "*";
1163 if (params.size() > 0)
1164 strAccount = params[0].get_str();
1166 if (params.size() > 1)
1167 nCount = params[1].get_int();
1169 if (params.size() > 2)
1170 nFrom = params[2].get_int();
1173 CWalletDB walletdb(pwalletMain->strWalletFile);
1175 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1176 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1177 typedef multimap<int64, TxPair > TxItems;
1180 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1182 CWalletTx* wtx = &((*it).second);
1183 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1185 list<CAccountingEntry> acentries;
1186 walletdb.ListAccountCreditDebit(strAccount, acentries);
1187 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1189 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1192 // Now: iterate backwards until we have nCount items to return:
1193 TxItems::reverse_iterator it = txByTime.rbegin();
1194 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1195 for (; it != txByTime.rend(); ++it)
1197 CWalletTx *const pwtx = (*it).second.first;
1199 ListTransactions(*pwtx, strAccount, 0, true, ret);
1200 CAccountingEntry *const pacentry = (*it).second.second;
1202 AcentryToJSON(*pacentry, strAccount, ret);
1204 if (ret.size() >= nCount) break;
1206 // ret is now newest to oldest
1208 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1209 if (ret.size() > nCount)
1211 Array::iterator last = ret.begin();
1212 std::advance(last, nCount);
1213 ret.erase(last, ret.end());
1215 std::reverse(ret.begin(), ret.end()); // oldest to newest
1220 Value listaccounts(const Array& params, bool fHelp)
1222 if (fHelp || params.size() > 1)
1223 throw runtime_error(
1224 "listaccounts [minconf=1]\n"
1225 "Returns Object that has account names as keys, account balances as values.");
1228 if (params.size() > 0)
1229 nMinDepth = params[0].get_int();
1231 map<string, int64> mapAccountBalances;
1232 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1233 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1234 mapAccountBalances[entry.second] = 0;
1237 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1239 const CWalletTx& wtx = (*it).second;
1240 int64 nGeneratedImmature, nGeneratedMature, nFee;
1241 string strSentAccount;
1242 list<pair<CBitcoinAddress, int64> > listReceived;
1243 list<pair<CBitcoinAddress, int64> > listSent;
1244 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1245 mapAccountBalances[strSentAccount] -= nFee;
1246 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1247 mapAccountBalances[strSentAccount] -= s.second;
1248 if (wtx.GetDepthInMainChain() >= nMinDepth)
1250 mapAccountBalances[""] += nGeneratedMature;
1251 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1252 if (pwalletMain->mapAddressBook.count(r.first))
1253 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1255 mapAccountBalances[""] += r.second;
1259 list<CAccountingEntry> acentries;
1260 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1261 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1262 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1265 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1266 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1271 Value listsinceblock(const Array& params, bool fHelp)
1274 throw runtime_error(
1275 "listsinceblock [blockid] [target-confirmations]\n"
1276 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1278 CBlockIndex *pindex = NULL;
1279 int target_confirms = 1;
1281 if (params.size() > 0)
1283 uint256 blockId = 0;
1285 blockId.SetHex(params[0].get_str());
1286 pindex = CBlockLocator(blockId).GetBlockIndex();
1289 if (params.size() > 1)
1291 target_confirms = params[1].get_int();
1293 if (target_confirms < 1)
1294 throw JSONRPCError(-8, "Invalid parameter");
1297 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1301 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1303 CWalletTx tx = (*it).second;
1305 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1306 ListTransactions(tx, "*", 0, true, transactions);
1311 if (target_confirms == 1)
1314 lastblock = hashBestChain;
1318 int target_height = pindexBest->nHeight + 1 - target_confirms;
1321 for (block = pindexBest;
1322 block && block->nHeight > target_height;
1323 block = block->pprev);
1325 lastblock = block ? block->GetBlockHash() : 0;
1329 ret.push_back(Pair("transactions", transactions));
1330 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1335 Value gettransaction(const Array& params, bool fHelp)
1337 if (fHelp || params.size() != 1)
1338 throw runtime_error(
1339 "gettransaction <txid>\n"
1340 "Get detailed information about <txid>");
1343 hash.SetHex(params[0].get_str());
1347 if (!pwalletMain->mapWallet.count(hash))
1348 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1349 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1351 int64 nCredit = wtx.GetCredit();
1352 int64 nDebit = wtx.GetDebit();
1353 int64 nNet = nCredit - nDebit;
1354 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1356 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1358 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1360 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1363 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1364 entry.push_back(Pair("details", details));
1370 Value backupwallet(const Array& params, bool fHelp)
1372 if (fHelp || params.size() != 1)
1373 throw runtime_error(
1374 "backupwallet <destination>\n"
1375 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1377 string strDest = params[0].get_str();
1378 BackupWallet(*pwalletMain, strDest);
1384 Value keypoolrefill(const Array& params, bool fHelp)
1386 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1387 throw runtime_error(
1389 "Fills the keypool, requires wallet passphrase to be set.");
1390 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1391 throw runtime_error(
1393 "Fills the keypool.");
1395 if (pwalletMain->IsLocked())
1396 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1398 pwalletMain->TopUpKeyPool();
1400 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1401 throw JSONRPCError(-4, "Error refreshing keypool.");
1407 void ThreadTopUpKeyPool(void* parg)
1409 pwalletMain->TopUpKeyPool();
1412 void ThreadCleanWalletPassphrase(void* parg)
1414 int64 nMyWakeTime = GetTime() + *((int*)parg);
1416 if (nWalletUnlockTime == 0)
1418 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1420 nWalletUnlockTime = nMyWakeTime;
1423 while (GetTime() < nWalletUnlockTime)
1424 Sleep(GetTime() - nWalletUnlockTime);
1426 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1428 nWalletUnlockTime = 0;
1433 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1435 if (nWalletUnlockTime < nMyWakeTime)
1436 nWalletUnlockTime = nMyWakeTime;
1442 pwalletMain->Lock();
1447 Value walletpassphrase(const Array& params, bool fHelp)
1449 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1450 throw runtime_error(
1451 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1452 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1453 "stakeonly is optional true/false allowing only stake creation.");
1456 if (!pwalletMain->IsCrypted())
1457 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1459 if (!pwalletMain->IsLocked())
1460 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1462 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1463 SecureString strWalletPass;
1464 strWalletPass.reserve(100);
1465 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1466 // Alternately, find a way to make params[0] mlock()'d to begin with.
1467 strWalletPass = params[0].get_str().c_str();
1469 if (strWalletPass.length() > 0)
1471 if (!pwalletMain->Unlock(strWalletPass))
1472 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1475 throw runtime_error(
1476 "walletpassphrase <passphrase> <timeout>\n"
1477 "Stores the wallet decryption key in memory for <timeout> seconds.");
1479 CreateThread(ThreadTopUpKeyPool, NULL);
1480 int* pnSleepTime = new int(params[1].get_int());
1481 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1483 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1484 if (params.size() > 2)
1485 fWalletUnlockStakeOnly = params[2].get_bool();
1487 fWalletUnlockStakeOnly = false;
1493 Value walletpassphrasechange(const Array& params, bool fHelp)
1495 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1496 throw runtime_error(
1497 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1498 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1501 if (!pwalletMain->IsCrypted())
1502 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1504 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1505 // Alternately, find a way to make params[0] mlock()'d to begin with.
1506 SecureString strOldWalletPass;
1507 strOldWalletPass.reserve(100);
1508 strOldWalletPass = params[0].get_str().c_str();
1510 SecureString strNewWalletPass;
1511 strNewWalletPass.reserve(100);
1512 strNewWalletPass = params[1].get_str().c_str();
1514 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1515 throw runtime_error(
1516 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1517 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1519 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1520 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1526 Value walletlock(const Array& params, bool fHelp)
1528 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1529 throw runtime_error(
1531 "Removes the wallet encryption key from memory, locking the wallet.\n"
1532 "After calling this method, you will need to call walletpassphrase again\n"
1533 "before being able to call any methods which require the wallet to be unlocked.");
1536 if (!pwalletMain->IsCrypted())
1537 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1539 pwalletMain->Lock();
1540 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1542 nWalletUnlockTime = 0;
1549 Value encryptwallet(const Array& params, bool fHelp)
1551 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1552 throw runtime_error(
1553 "encryptwallet <passphrase>\n"
1554 "Encrypts the wallet with <passphrase>.");
1557 if (pwalletMain->IsCrypted())
1558 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1561 // shutting down via RPC while the GUI is running does not work (yet):
1562 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1565 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1566 // Alternately, find a way to make params[0] mlock()'d to begin with.
1567 SecureString strWalletPass;
1568 strWalletPass.reserve(100);
1569 strWalletPass = params[0].get_str().c_str();
1571 if (strWalletPass.length() < 1)
1572 throw runtime_error(
1573 "encryptwallet <passphrase>\n"
1574 "Encrypts the wallet with <passphrase>.");
1576 if (!pwalletMain->EncryptWallet(strWalletPass))
1577 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1579 // BDB seems to have a bad habit of writing old data into
1580 // slack space in .dat files; that is bad if the old data is
1581 // unencrypted private keys. So:
1582 CreateThread(Shutdown, NULL);
1583 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1587 Value validateaddress(const Array& params, bool fHelp)
1589 if (fHelp || params.size() != 1)
1590 throw runtime_error(
1591 "validateaddress <ppcoinaddress>\n"
1592 "Return information about <ppcoinaddress>.");
1594 CBitcoinAddress address(params[0].get_str());
1595 bool isValid = address.IsValid();
1598 ret.push_back(Pair("isvalid", isValid));
1601 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1602 // version of the address:
1603 string currentAddress = address.ToString();
1604 ret.push_back(Pair("address", currentAddress));
1605 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1606 if (pwalletMain->mapAddressBook.count(address))
1607 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1613 Value getwork(const Array& params, bool fHelp)
1615 if (fHelp || params.size() > 1)
1616 throw runtime_error(
1618 "If [data] is not specified, returns formatted hash data to work on:\n"
1619 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1620 " \"data\" : block data\n"
1621 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1622 " \"target\" : little endian hash target\n"
1623 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1626 throw JSONRPCError(-9, "PPCoin is not connected!");
1628 if (IsInitialBlockDownload())
1629 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1631 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1632 static mapNewBlock_t mapNewBlock;
1633 static vector<CBlock*> vNewBlock;
1634 static CReserveKey reservekey(pwalletMain);
1636 if (params.size() == 0)
1639 static unsigned int nTransactionsUpdatedLast;
1640 static CBlockIndex* pindexPrev;
1641 static int64 nStart;
1642 static CBlock* pblock;
1643 if (pindexPrev != pindexBest ||
1644 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1646 if (pindexPrev != pindexBest)
1648 // Deallocate old blocks since they're obsolete now
1649 mapNewBlock.clear();
1650 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1654 nTransactionsUpdatedLast = nTransactionsUpdated;
1655 pindexPrev = pindexBest;
1659 pblock = CreateNewBlock(pwalletMain);
1661 throw JSONRPCError(-7, "Out of memory");
1662 vNewBlock.push_back(pblock);
1666 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1669 // Update nExtraNonce
1670 static unsigned int nExtraNonce = 0;
1671 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1674 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1676 // Prebuild hash buffers
1680 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1682 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1685 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1686 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1687 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1688 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1694 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1695 if (vchData.size() != 128)
1696 throw JSONRPCError(-8, "Invalid parameter");
1697 CBlock* pdata = (CBlock*)&vchData[0];
1700 for (int i = 0; i < 128/4; i++)
1701 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1704 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1706 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1708 pblock->nTime = pdata->nTime;
1709 pblock->nNonce = pdata->nNonce;
1710 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1711 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1713 return CheckWork(pblock, *pwalletMain, reservekey);
1718 Value getmemorypool(const Array& params, bool fHelp)
1720 if (fHelp || params.size() > 1)
1721 throw runtime_error(
1722 "getmemorypool [data]\n"
1723 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1724 " \"version\" : block version\n"
1725 " \"previousblockhash\" : hash of current highest block\n"
1726 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1727 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1728 " \"time\" : timestamp appropriate for next block\n"
1729 " \"bits\" : compressed target of next block\n"
1730 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1732 if (params.size() == 0)
1735 throw JSONRPCError(-9, "PPCoin is not connected!");
1737 if (IsInitialBlockDownload())
1738 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1740 static CReserveKey reservekey(pwalletMain);
1743 static unsigned int nTransactionsUpdatedLast;
1744 static CBlockIndex* pindexPrev;
1745 static int64 nStart;
1746 static CBlock* pblock;
1747 if (pindexPrev != pindexBest ||
1748 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1750 nTransactionsUpdatedLast = nTransactionsUpdated;
1751 pindexPrev = pindexBest;
1757 pblock = CreateNewBlock(pwalletMain);
1759 throw JSONRPCError(-7, "Out of memory");
1763 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1767 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1768 if(tx.IsCoinBase() || tx.IsCoinStake())
1774 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1778 result.push_back(Pair("version", pblock->nVersion));
1779 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1780 result.push_back(Pair("transactions", transactions));
1781 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1782 result.push_back(Pair("time", (int64_t)pblock->nTime));
1788 uBits.nBits = htonl((int32_t)pblock->nBits);
1789 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1796 CDataStream ssBlock(ParseHex(params[0].get_str()));
1800 return ProcessBlock(NULL, &pblock);
1805 // ppcoin: reset auto checkpoint
1806 Value resetcheckpoint(const Array& params, bool fHelp)
1808 if (fHelp || params.size() < 1 || params.size() > 1)
1809 throw runtime_error(
1810 "resetcheckpoint <checkpointheight>\n"
1811 "Reset automatic checkpoint to specified height.\n"
1812 "<checkpointheight> is the height of the new checkpoint block.\n");
1814 int nCheckpoint = params[0].get_int();
1815 if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1816 throw runtime_error(
1817 "invalid checkpoint height.\n"
1819 if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1820 throw runtime_error(
1821 "new checkpoint must be earlier than current auto checkpoint.\n"
1823 if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1824 throw runtime_error(
1825 "internal error - reset checkpoint failed.\n"
1832 // ppcoin: get branch point of alternative branch
1833 Value getbranchpoint(const Array& params, bool fHelp)
1835 if (fHelp || params.size() != 0)
1836 throw runtime_error(
1838 "Returns height of branch point of alternative branch.\n");
1841 if (Checkpoints::nBranchPoint > 0)
1842 result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1844 result.push_back(Pair("branchpoint", "none"));
1845 result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1850 // ppcoin: reserve balance from being staked for network protection
1851 Value reservebalance(const Array& params, bool fHelp)
1853 if (fHelp || params.size() > 2)
1854 throw runtime_error(
1855 "reservebalance [<reserve> [amount]]\n"
1856 "<reserve> is true or false to turn balance reserve on or off.\n"
1857 "<amount> is a real and rounded to cent.\n"
1858 "Set reserve amount not participating in network protection.\n"
1859 "If no parameters provided current setting is printed.\n");
1861 if (params.size() > 0)
1863 bool fReserve = params[0].get_bool();
1866 if (params.size() == 1)
1867 throw runtime_error("must provide amount to reserve balance.\n");
1868 int64 nAmount = AmountFromValue(params[1]);
1869 nAmount = (nAmount / CENT) * CENT; // round to cent
1871 throw runtime_error("amount cannot be negative.\n");
1872 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1876 if (params.size() > 1)
1877 throw runtime_error("cannot specify amount to turn off reserve.\n");
1878 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1883 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1884 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1889 // ppcoin: check wallet integrity
1890 Value checkwallet(const Array& params, bool fHelp)
1892 if (fHelp || params.size() > 0)
1893 throw runtime_error(
1895 "Check wallet for integrity.\n");
1898 int64 nBalanceInQuestion;
1899 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1902 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1903 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1910 // ppcoin: repair wallet
1911 Value repairwallet(const Array& params, bool fHelp)
1913 if (fHelp || params.size() > 0)
1914 throw runtime_error(
1916 "Repair wallet if checkwallet reports any problem.\n");
1919 int64 nBalanceInQuestion;
1920 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1922 if (nMismatchSpent == 0)
1924 result.push_back(Pair("wallet check passed", true));
1928 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1929 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1934 extern CCriticalSection cs_mapAlerts;
1935 extern map<uint256, CAlert> mapAlerts;
1937 // ppcoin: send alert.
1938 // There is a known deadlock situation with ThreadMessageHandler
1939 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1940 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1941 Value sendalert(const Array& params, bool fHelp)
1943 if (fHelp || params.size() < 5)
1944 throw runtime_error(
1945 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1946 "<message> is the alert text message\n"
1947 "<privatekey> is hex string of alert master private key\n"
1948 "<minver> is the minimum applicable client version\n"
1949 "<maxver> is the maximum applicable client version\n"
1950 "<id> is the alert id\n"
1951 "[cancelupto] cancels all alert id's up to this number\n"
1952 "Returns true or false.");
1957 alert.strStatusBar = params[0].get_str();
1958 alert.nMinVer = params[2].get_int();
1959 alert.nMaxVer = params[3].get_int();
1960 alert.nID = params[4].get_int();
1961 if (params.size() > 5)
1962 alert.nCancel = params[5].get_int();
1963 alert.nVersion = VERSION;
1964 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1965 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1966 alert.nPriority = 1;
1969 sMsg << (CUnsignedAlert)alert;
1970 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1972 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1973 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1974 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1975 throw runtime_error(
1976 "Unable to sign alert, check private key?\n");
1977 if(!alert.ProcessAlert())
1978 throw runtime_error(
1979 "Failed to process alert.\n");
1981 CRITICAL_BLOCK(cs_vNodes)
1982 BOOST_FOREACH(CNode* pnode, vNodes)
1983 alert.RelayTo(pnode);
1986 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1987 result.push_back(Pair("nVersion", alert.nVersion));
1988 result.push_back(Pair("nMinVer", alert.nMinVer));
1989 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1990 result.push_back(Pair("nID", alert.nID));
1991 if (alert.nCancel > 0)
1992 result.push_back(Pair("nCancel", alert.nCancel));
1996 Value makekeypair(const Array& params, bool fHelp)
2000 CPrivKey vchPrivKey = key.GetPrivKey();
2003 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
2004 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
2012 pair<string, rpcfn_type> pCallTable[] =
2014 make_pair("help", &help),
2015 make_pair("stop", &stop),
2016 make_pair("getblockcount", &getblockcount),
2017 make_pair("getblocknumber", &getblocknumber),
2018 make_pair("getconnectioncount", &getconnectioncount),
2019 make_pair("getdifficulty", &getdifficulty),
2020 make_pair("getgenerate", &getgenerate),
2021 make_pair("setgenerate", &setgenerate),
2022 make_pair("gethashespersec", &gethashespersec),
2023 make_pair("getinfo", &getinfo),
2024 make_pair("getnewaddress", &getnewaddress),
2025 make_pair("getaccountaddress", &getaccountaddress),
2026 make_pair("setaccount", &setaccount),
2027 make_pair("getaccount", &getaccount),
2028 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2029 make_pair("sendtoaddress", &sendtoaddress),
2030 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2031 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2032 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2033 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2034 make_pair("backupwallet", &backupwallet),
2035 make_pair("keypoolrefill", &keypoolrefill),
2036 make_pair("walletpassphrase", &walletpassphrase),
2037 make_pair("walletpassphrasechange", &walletpassphrasechange),
2038 make_pair("walletlock", &walletlock),
2039 make_pair("encryptwallet", &encryptwallet),
2040 make_pair("validateaddress", &validateaddress),
2041 make_pair("getbalance", &getbalance),
2042 make_pair("move", &movecmd),
2043 make_pair("sendfrom", &sendfrom),
2044 make_pair("sendmany", &sendmany),
2045 make_pair("gettransaction", &gettransaction),
2046 make_pair("listtransactions", &listtransactions),
2047 make_pair("signmessage", &signmessage),
2048 make_pair("verifymessage", &verifymessage),
2049 make_pair("getwork", &getwork),
2050 make_pair("listaccounts", &listaccounts),
2051 make_pair("settxfee", &settxfee),
2052 make_pair("getmemorypool", &getmemorypool),
2053 make_pair("listsinceblock", &listsinceblock),
2054 make_pair("resetcheckpoint", &resetcheckpoint),
2055 make_pair("getbranchpoint", &getbranchpoint),
2056 make_pair("reservebalance", &reservebalance),
2057 make_pair("checkwallet", &checkwallet),
2058 make_pair("repairwallet", &repairwallet),
2059 make_pair("sendalert", &sendalert),
2060 make_pair("makekeypair", &makekeypair),
2062 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2064 string pAllowInSafeMode[] =
2069 "getblocknumber", // deprecated
2070 "getconnectioncount",
2077 "getaccountaddress",
2079 "getaddressesbyaccount",
2090 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2098 // This ain't Apache. We're just using HTTP header for the length field
2099 // and to be compatible with other JSON-RPC implementations.
2102 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2105 s << "POST / HTTP/1.1\r\n"
2106 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2107 << "Host: 127.0.0.1\r\n"
2108 << "Content-Type: application/json\r\n"
2109 << "Content-Length: " << strMsg.size() << "\r\n"
2110 << "Connection: close\r\n"
2111 << "Accept: application/json\r\n";
2112 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2113 s << item.first << ": " << item.second << "\r\n";
2114 s << "\r\n" << strMsg;
2119 string rfc1123Time()
2124 struct tm* now_gmt = gmtime(&now);
2125 string locale(setlocale(LC_TIME, NULL));
2126 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2127 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2128 setlocale(LC_TIME, locale.c_str());
2129 return string(buffer);
2132 static string HTTPReply(int nStatus, const string& strMsg)
2135 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2137 "Server: ppcoin-json-rpc/%s\r\n"
2138 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2139 "Content-Type: text/html\r\n"
2140 "Content-Length: 296\r\n"
2142 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2143 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2146 "<TITLE>Error</TITLE>\r\n"
2147 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2149 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2150 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2151 const char *cStatus;
2152 if (nStatus == 200) cStatus = "OK";
2153 else if (nStatus == 400) cStatus = "Bad Request";
2154 else if (nStatus == 403) cStatus = "Forbidden";
2155 else if (nStatus == 404) cStatus = "Not Found";
2156 else if (nStatus == 500) cStatus = "Internal Server Error";
2159 "HTTP/1.1 %d %s\r\n"
2161 "Connection: close\r\n"
2162 "Content-Length: %d\r\n"
2163 "Content-Type: application/json\r\n"
2164 "Server: ppcoin-json-rpc/%s\r\n"
2169 rfc1123Time().c_str(),
2171 FormatFullVersion().c_str(),
2175 int ReadHTTPStatus(std::basic_istream<char>& stream)
2178 getline(stream, str);
2179 vector<string> vWords;
2180 boost::split(vWords, str, boost::is_any_of(" "));
2181 if (vWords.size() < 2)
2183 return atoi(vWords[1].c_str());
2186 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2192 std::getline(stream, str);
2193 if (str.empty() || str == "\r")
2195 string::size_type nColon = str.find(":");
2196 if (nColon != string::npos)
2198 string strHeader = str.substr(0, nColon);
2199 boost::trim(strHeader);
2200 boost::to_lower(strHeader);
2201 string strValue = str.substr(nColon+1);
2202 boost::trim(strValue);
2203 mapHeadersRet[strHeader] = strValue;
2204 if (strHeader == "content-length")
2205 nLen = atoi(strValue.c_str());
2211 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2213 mapHeadersRet.clear();
2217 int nStatus = ReadHTTPStatus(stream);
2220 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2221 if (nLen < 0 || nLen > MAX_SIZE)
2227 vector<char> vch(nLen);
2228 stream.read(&vch[0], nLen);
2229 strMessageRet = string(vch.begin(), vch.end());
2235 bool HTTPAuthorized(map<string, string>& mapHeaders)
2237 string strAuth = mapHeaders["authorization"];
2238 if (strAuth.substr(0,6) != "Basic ")
2240 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2241 string strUserPass = DecodeBase64(strUserPass64);
2242 return strUserPass == strRPCUserColonPass;
2246 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2247 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2248 // unspecified (HTTP errors and contents of 'error').
2250 // 1.0 spec: http://json-rpc.org/wiki/specification
2251 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2252 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2255 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2258 request.push_back(Pair("method", strMethod));
2259 request.push_back(Pair("params", params));
2260 request.push_back(Pair("id", id));
2261 return write_string(Value(request), false) + "\n";
2264 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2267 if (error.type() != null_type)
2268 reply.push_back(Pair("result", Value::null));
2270 reply.push_back(Pair("result", result));
2271 reply.push_back(Pair("error", error));
2272 reply.push_back(Pair("id", id));
2273 return write_string(Value(reply), false) + "\n";
2276 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2278 // Send error reply from json-rpc error object
2280 int code = find_value(objError, "code").get_int();
2281 if (code == -32600) nStatus = 400;
2282 else if (code == -32601) nStatus = 404;
2283 string strReply = JSONRPCReply(Value::null, objError, id);
2284 stream << HTTPReply(nStatus, strReply) << std::flush;
2287 bool ClientAllowed(const string& strAddress)
2289 if (strAddress == asio::ip::address_v4::loopback().to_string())
2291 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2292 BOOST_FOREACH(string strAllow, vAllow)
2293 if (WildcardMatch(strAddress, strAllow))
2300 // IOStream device that speaks SSL but can also speak non-SSL
2302 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2304 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2306 fUseSSL = fUseSSLIn;
2307 fNeedHandshake = fUseSSLIn;
2310 void handshake(ssl::stream_base::handshake_type role)
2312 if (!fNeedHandshake) return;
2313 fNeedHandshake = false;
2314 stream.handshake(role);
2316 std::streamsize read(char* s, std::streamsize n)
2318 handshake(ssl::stream_base::server); // HTTPS servers read first
2319 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2320 return stream.next_layer().read_some(asio::buffer(s, n));
2322 std::streamsize write(const char* s, std::streamsize n)
2324 handshake(ssl::stream_base::client); // HTTPS clients write first
2325 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2326 return asio::write(stream.next_layer(), asio::buffer(s, n));
2328 bool connect(const std::string& server, const std::string& port)
2330 ip::tcp::resolver resolver(stream.get_io_service());
2331 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2332 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2333 ip::tcp::resolver::iterator end;
2334 boost::system::error_code error = asio::error::host_not_found;
2335 while (error && endpoint_iterator != end)
2337 stream.lowest_layer().close();
2338 stream.lowest_layer().connect(*endpoint_iterator++, error);
2346 bool fNeedHandshake;
2352 void ThreadRPCServer(void* parg)
2354 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2357 vnThreadsRunning[4]++;
2358 ThreadRPCServer2(parg);
2359 vnThreadsRunning[4]--;
2361 catch (std::exception& e) {
2362 vnThreadsRunning[4]--;
2363 PrintException(&e, "ThreadRPCServer()");
2365 vnThreadsRunning[4]--;
2366 PrintException(NULL, "ThreadRPCServer()");
2368 printf("ThreadRPCServer exiting\n");
2371 void ThreadRPCServer2(void* parg)
2373 printf("ThreadRPCServer started\n");
2375 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2376 if (strRPCUserColonPass == ":")
2378 string strWhatAmI = "To use ppcoind";
2379 if (mapArgs.count("-server"))
2380 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2381 else if (mapArgs.count("-daemon"))
2382 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2384 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2385 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2387 GetConfigFile().c_str());
2389 CreateThread(Shutdown, NULL);
2394 bool fUseSSL = GetBoolArg("-rpcssl");
2395 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2397 asio::io_service io_service;
2398 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2399 ip::tcp::acceptor acceptor(io_service, endpoint);
2401 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2404 ssl::context context(io_service, ssl::context::sslv23);
2407 context.set_options(ssl::context::no_sslv2);
2408 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2409 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2410 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2411 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2412 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2413 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2414 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2415 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2417 string ciphers = GetArg("-rpcsslciphers",
2418 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2419 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2423 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2428 // Accept connection
2430 SSLStream sslStream(io_service, context);
2431 SSLIOStreamDevice d(sslStream, fUseSSL);
2432 iostreams::stream<SSLIOStreamDevice> stream(d);
2434 ip::tcp::iostream stream;
2437 ip::tcp::endpoint peer;
2438 vnThreadsRunning[4]--;
2440 acceptor.accept(sslStream.lowest_layer(), peer);
2442 acceptor.accept(*stream.rdbuf(), peer);
2444 vnThreadsRunning[4]++;
2448 // Restrict callers by IP
2449 if (!ClientAllowed(peer.address().to_string()))
2451 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2453 stream << HTTPReply(403, "") << std::flush;
2457 map<string, string> mapHeaders;
2460 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2461 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2464 printf("ThreadRPCServer ReadHTTP timeout\n");
2468 // Check authorization
2469 if (mapHeaders.count("authorization") == 0)
2471 stream << HTTPReply(401, "") << std::flush;
2474 if (!HTTPAuthorized(mapHeaders))
2476 // Deter brute-forcing short passwords
2477 if (mapArgs["-rpcpassword"].size() < 15)
2480 stream << HTTPReply(401, "") << std::flush;
2481 printf("ThreadRPCServer incorrect password attempt\n");
2485 Value id = Value::null;
2490 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2491 throw JSONRPCError(-32700, "Parse error");
2492 const Object& request = valRequest.get_obj();
2494 // Parse id now so errors from here on will have the id
2495 id = find_value(request, "id");
2498 Value valMethod = find_value(request, "method");
2499 if (valMethod.type() == null_type)
2500 throw JSONRPCError(-32600, "Missing method");
2501 if (valMethod.type() != str_type)
2502 throw JSONRPCError(-32600, "Method must be a string");
2503 string strMethod = valMethod.get_str();
2504 if (strMethod != "getwork" && strMethod != "getmemorypool")
2505 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2508 Value valParams = find_value(request, "params");
2510 if (valParams.type() == array_type)
2511 params = valParams.get_array();
2512 else if (valParams.type() == null_type)
2515 throw JSONRPCError(-32600, "Params must be an array");
2518 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2519 if (mi == mapCallTable.end())
2520 throw JSONRPCError(-32601, "Method not found");
2522 // Observe safe mode
2523 string strWarning = GetWarnings("rpc");
2524 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2525 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2531 CRITICAL_BLOCK(cs_main)
2532 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2533 result = (*(*mi).second)(params, false);
2536 string strReply = JSONRPCReply(result, Value::null, id);
2537 stream << HTTPReply(200, strReply) << std::flush;
2539 catch (std::exception& e)
2541 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2544 catch (Object& objError)
2546 ErrorReply(stream, objError, id);
2548 catch (std::exception& e)
2550 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2558 Object CallRPC(const string& strMethod, const Array& params)
2560 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2561 throw runtime_error(strprintf(
2562 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2563 "If the file does not exist, create it with owner-readable-only file permissions."),
2564 GetConfigFile().c_str()));
2566 // Connect to localhost
2567 bool fUseSSL = GetBoolArg("-rpcssl");
2569 asio::io_service io_service;
2570 ssl::context context(io_service, ssl::context::sslv23);
2571 context.set_options(ssl::context::no_sslv2);
2572 SSLStream sslStream(io_service, context);
2573 SSLIOStreamDevice d(sslStream, fUseSSL);
2574 iostreams::stream<SSLIOStreamDevice> stream(d);
2575 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2576 throw runtime_error("couldn't connect to server");
2579 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2581 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2583 throw runtime_error("couldn't connect to server");
2587 // HTTP basic authentication
2588 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2589 map<string, string> mapRequestHeaders;
2590 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2593 string strRequest = JSONRPCRequest(strMethod, params, 1);
2594 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2595 stream << strPost << std::flush;
2598 map<string, string> mapHeaders;
2600 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2602 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2603 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2604 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2605 else if (strReply.empty())
2606 throw runtime_error("no response from server");
2610 if (!read_string(strReply, valReply))
2611 throw runtime_error("couldn't parse reply from server");
2612 const Object& reply = valReply.get_obj();
2614 throw runtime_error("expected reply to have result, error and id properties");
2622 template<typename T>
2623 void ConvertTo(Value& value)
2625 if (value.type() == str_type)
2627 // reinterpret string as unquoted json value
2629 if (!read_string(value.get_str(), value2))
2630 throw runtime_error("type mismatch");
2631 value = value2.get_value<T>();
2635 value = value.get_value<T>();
2639 int CommandLineRPC(int argc, char *argv[])
2646 while (argc > 1 && IsSwitchChar(argv[1][0]))
2654 throw runtime_error("too few parameters");
2655 string strMethod = argv[1];
2657 // Parameters default to strings
2659 for (int i = 2; i < argc; i++)
2660 params.push_back(argv[i]);
2661 int n = params.size();
2664 // Special case non-string parameter types
2666 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2667 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2668 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2669 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2670 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2671 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2672 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2673 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2674 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2675 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2676 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2677 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2678 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2679 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2680 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2681 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2682 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2683 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2684 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2685 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2686 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2687 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2688 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2689 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2690 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2691 if (strMethod == "sendmany" && n > 1)
2693 string s = params[1].get_str();
2695 if (!read_string(s, v) || v.type() != obj_type)
2696 throw runtime_error("type mismatch");
2697 params[1] = v.get_obj();
2699 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2700 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2701 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2702 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2705 Object reply = CallRPC(strMethod, params);
2708 const Value& result = find_value(reply, "result");
2709 const Value& error = find_value(reply, "error");
2711 if (error.type() != null_type)
2714 strPrint = "error: " + write_string(error, false);
2715 int code = find_value(error.get_obj(), "code").get_int();
2721 if (result.type() == null_type)
2723 else if (result.type() == str_type)
2724 strPrint = result.get_str();
2726 strPrint = write_string(result, true);
2729 catch (std::exception& e)
2731 strPrint = string("error: ") + e.what();
2736 PrintException(NULL, "CommandLineRPC()");
2741 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2750 int main(int argc, char *argv[])
2753 // Turn off microsoft heap dump noise
2754 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2755 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2757 setbuf(stdin, NULL);
2758 setbuf(stdout, NULL);
2759 setbuf(stderr, NULL);
2763 if (argc >= 2 && string(argv[1]) == "-server")
2765 printf("server ready\n");
2766 ThreadRPCServer(NULL);
2770 return CommandLineRPC(argc, argv);
2773 catch (std::exception& e) {
2774 PrintException(&e, "main()");
2776 PrintException(NULL, "main()");