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: reserve balance from being staked for network protection
1806 Value reservebalance(const Array& params, bool fHelp)
1808 if (fHelp || params.size() > 2)
1809 throw runtime_error(
1810 "reservebalance [<reserve> [amount]]\n"
1811 "<reserve> is true or false to turn balance reserve on or off.\n"
1812 "<amount> is a real and rounded to cent.\n"
1813 "Set reserve amount not participating in network protection.\n"
1814 "If no parameters provided current setting is printed.\n");
1816 if (params.size() > 0)
1818 bool fReserve = params[0].get_bool();
1821 if (params.size() == 1)
1822 throw runtime_error("must provide amount to reserve balance.\n");
1823 int64 nAmount = AmountFromValue(params[1]);
1824 nAmount = (nAmount / CENT) * CENT; // round to cent
1826 throw runtime_error("amount cannot be negative.\n");
1827 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1831 if (params.size() > 1)
1832 throw runtime_error("cannot specify amount to turn off reserve.\n");
1833 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1838 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1839 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1844 // ppcoin: check wallet integrity
1845 Value checkwallet(const Array& params, bool fHelp)
1847 if (fHelp || params.size() > 0)
1848 throw runtime_error(
1850 "Check wallet for integrity.\n");
1853 int64 nBalanceInQuestion;
1854 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1857 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1858 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1865 // ppcoin: repair wallet
1866 Value repairwallet(const Array& params, bool fHelp)
1868 if (fHelp || params.size() > 0)
1869 throw runtime_error(
1871 "Repair wallet if checkwallet reports any problem.\n");
1874 int64 nBalanceInQuestion;
1875 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1877 if (nMismatchSpent == 0)
1879 result.push_back(Pair("wallet check passed", true));
1883 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1884 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1889 // ppcoin: make a public-private key pair
1890 Value makekeypair(const Array& params, bool fHelp)
1892 if (fHelp || params.size() > 1)
1893 throw runtime_error(
1894 "makekeypair [prefix]\n"
1895 "Make a public/private key pair.\n"
1896 "[prefix] is optional preferred prefix for the public key.\n");
1898 string strPrefix = "";
1899 if (params.size() > 0)
1900 strPrefix = params[0].get_str();
1906 } while (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
1908 CPrivKey vchPrivKey = key.GetPrivKey();
1910 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1911 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1915 extern CCriticalSection cs_mapAlerts;
1916 extern map<uint256, CAlert> mapAlerts;
1918 // ppcoin: send alert.
1919 // There is a known deadlock situation with ThreadMessageHandler
1920 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1921 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1922 Value sendalert(const Array& params, bool fHelp)
1924 if (fHelp || params.size() < 5)
1925 throw runtime_error(
1926 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1927 "<message> is the alert text message\n"
1928 "<privatekey> is hex string of alert master private key\n"
1929 "<minver> is the minimum applicable client version\n"
1930 "<maxver> is the maximum applicable client version\n"
1931 "<id> is the alert id\n"
1932 "[cancelupto] cancels all alert id's up to this number\n"
1933 "Returns true or false.");
1938 alert.strStatusBar = params[0].get_str();
1939 alert.nMinVer = params[2].get_int();
1940 alert.nMaxVer = params[3].get_int();
1941 alert.nID = params[4].get_int();
1942 if (params.size() > 5)
1943 alert.nCancel = params[5].get_int();
1944 alert.nVersion = VERSION;
1945 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1946 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1947 alert.nPriority = 1;
1950 sMsg << (CUnsignedAlert)alert;
1951 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1953 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1954 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1955 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1956 throw runtime_error(
1957 "Unable to sign alert, check private key?\n");
1958 if(!alert.ProcessAlert())
1959 throw runtime_error(
1960 "Failed to process alert.\n");
1962 CRITICAL_BLOCK(cs_vNodes)
1963 BOOST_FOREACH(CNode* pnode, vNodes)
1964 alert.RelayTo(pnode);
1967 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1968 result.push_back(Pair("nVersion", alert.nVersion));
1969 result.push_back(Pair("nMinVer", alert.nMinVer));
1970 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1971 result.push_back(Pair("nID", alert.nID));
1972 if (alert.nCancel > 0)
1973 result.push_back(Pair("nCancel", alert.nCancel));
1977 // ppcoin: send checkpoint
1978 Value sendcheckpoint(const Array& params, bool fHelp)
1980 if (fHelp || params.size() > 2 || params.size() < 1 )
1981 throw runtime_error(
1982 "sendcheckpoint <privatekey> [checkpointhash]\n"
1983 "<privatekey> is hex string of checkpoint master private key\n"
1984 "<checkpointhash> is the hash of checkpoint block\n");
1986 CSyncCheckpoint checkpoint;
1989 // TODO: omit checkpointhash parameter
1990 if (params.size() > 1)
1992 checkpoint.hashCheckpoint = uint256(params[1].get_str());
1993 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
1994 throw runtime_error(
1995 "Provided checkpoint block is not on main chain\n");
1999 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
2000 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
2001 throw runtime_error(
2002 "Unable to select a more recent sync-checkpoint");
2006 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2007 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2009 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2010 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2011 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2012 throw runtime_error(
2013 "Unable to sign checkpoint, check private key?\n");
2015 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2016 throw runtime_error(
2017 "Failed to process checkpoint.\n");
2019 CRITICAL_BLOCK(cs_vNodes)
2020 BOOST_FOREACH(CNode* pnode, vNodes)
2021 checkpoint.RelayTo(pnode);
2024 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2025 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2026 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
2035 pair<string, rpcfn_type> pCallTable[] =
2037 make_pair("help", &help),
2038 make_pair("stop", &stop),
2039 make_pair("getblockcount", &getblockcount),
2040 make_pair("getblocknumber", &getblocknumber),
2041 make_pair("getconnectioncount", &getconnectioncount),
2042 make_pair("getdifficulty", &getdifficulty),
2043 make_pair("getgenerate", &getgenerate),
2044 make_pair("setgenerate", &setgenerate),
2045 make_pair("gethashespersec", &gethashespersec),
2046 make_pair("getinfo", &getinfo),
2047 make_pair("getnewaddress", &getnewaddress),
2048 make_pair("getaccountaddress", &getaccountaddress),
2049 make_pair("setaccount", &setaccount),
2050 make_pair("getaccount", &getaccount),
2051 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2052 make_pair("sendtoaddress", &sendtoaddress),
2053 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2054 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2055 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2056 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2057 make_pair("backupwallet", &backupwallet),
2058 make_pair("keypoolrefill", &keypoolrefill),
2059 make_pair("walletpassphrase", &walletpassphrase),
2060 make_pair("walletpassphrasechange", &walletpassphrasechange),
2061 make_pair("walletlock", &walletlock),
2062 make_pair("encryptwallet", &encryptwallet),
2063 make_pair("validateaddress", &validateaddress),
2064 make_pair("getbalance", &getbalance),
2065 make_pair("move", &movecmd),
2066 make_pair("sendfrom", &sendfrom),
2067 make_pair("sendmany", &sendmany),
2068 make_pair("gettransaction", &gettransaction),
2069 make_pair("listtransactions", &listtransactions),
2070 make_pair("signmessage", &signmessage),
2071 make_pair("verifymessage", &verifymessage),
2072 make_pair("getwork", &getwork),
2073 make_pair("listaccounts", &listaccounts),
2074 make_pair("settxfee", &settxfee),
2075 make_pair("getmemorypool", &getmemorypool),
2076 make_pair("listsinceblock", &listsinceblock),
2077 make_pair("reservebalance", &reservebalance),
2078 make_pair("checkwallet", &checkwallet),
2079 make_pair("repairwallet", &repairwallet),
2080 make_pair("makekeypair", &makekeypair),
2081 make_pair("sendalert", &sendalert),
2082 make_pair("sendcheckpoint", &sendcheckpoint),
2084 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2086 string pAllowInSafeMode[] =
2091 "getblocknumber", // deprecated
2092 "getconnectioncount",
2099 "getaccountaddress",
2101 "getaddressesbyaccount",
2110 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2118 // This ain't Apache. We're just using HTTP header for the length field
2119 // and to be compatible with other JSON-RPC implementations.
2122 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2125 s << "POST / HTTP/1.1\r\n"
2126 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2127 << "Host: 127.0.0.1\r\n"
2128 << "Content-Type: application/json\r\n"
2129 << "Content-Length: " << strMsg.size() << "\r\n"
2130 << "Connection: close\r\n"
2131 << "Accept: application/json\r\n";
2132 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2133 s << item.first << ": " << item.second << "\r\n";
2134 s << "\r\n" << strMsg;
2139 string rfc1123Time()
2144 struct tm* now_gmt = gmtime(&now);
2145 string locale(setlocale(LC_TIME, NULL));
2146 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2147 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2148 setlocale(LC_TIME, locale.c_str());
2149 return string(buffer);
2152 static string HTTPReply(int nStatus, const string& strMsg)
2155 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2157 "Server: ppcoin-json-rpc/%s\r\n"
2158 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2159 "Content-Type: text/html\r\n"
2160 "Content-Length: 296\r\n"
2162 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2163 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2166 "<TITLE>Error</TITLE>\r\n"
2167 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2169 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2170 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2171 const char *cStatus;
2172 if (nStatus == 200) cStatus = "OK";
2173 else if (nStatus == 400) cStatus = "Bad Request";
2174 else if (nStatus == 403) cStatus = "Forbidden";
2175 else if (nStatus == 404) cStatus = "Not Found";
2176 else if (nStatus == 500) cStatus = "Internal Server Error";
2179 "HTTP/1.1 %d %s\r\n"
2181 "Connection: close\r\n"
2182 "Content-Length: %d\r\n"
2183 "Content-Type: application/json\r\n"
2184 "Server: ppcoin-json-rpc/%s\r\n"
2189 rfc1123Time().c_str(),
2191 FormatFullVersion().c_str(),
2195 int ReadHTTPStatus(std::basic_istream<char>& stream)
2198 getline(stream, str);
2199 vector<string> vWords;
2200 boost::split(vWords, str, boost::is_any_of(" "));
2201 if (vWords.size() < 2)
2203 return atoi(vWords[1].c_str());
2206 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2212 std::getline(stream, str);
2213 if (str.empty() || str == "\r")
2215 string::size_type nColon = str.find(":");
2216 if (nColon != string::npos)
2218 string strHeader = str.substr(0, nColon);
2219 boost::trim(strHeader);
2220 boost::to_lower(strHeader);
2221 string strValue = str.substr(nColon+1);
2222 boost::trim(strValue);
2223 mapHeadersRet[strHeader] = strValue;
2224 if (strHeader == "content-length")
2225 nLen = atoi(strValue.c_str());
2231 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2233 mapHeadersRet.clear();
2237 int nStatus = ReadHTTPStatus(stream);
2240 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2241 if (nLen < 0 || nLen > MAX_SIZE)
2247 vector<char> vch(nLen);
2248 stream.read(&vch[0], nLen);
2249 strMessageRet = string(vch.begin(), vch.end());
2255 bool HTTPAuthorized(map<string, string>& mapHeaders)
2257 string strAuth = mapHeaders["authorization"];
2258 if (strAuth.substr(0,6) != "Basic ")
2260 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2261 string strUserPass = DecodeBase64(strUserPass64);
2262 return strUserPass == strRPCUserColonPass;
2266 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2267 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2268 // unspecified (HTTP errors and contents of 'error').
2270 // 1.0 spec: http://json-rpc.org/wiki/specification
2271 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2272 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2275 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2278 request.push_back(Pair("method", strMethod));
2279 request.push_back(Pair("params", params));
2280 request.push_back(Pair("id", id));
2281 return write_string(Value(request), false) + "\n";
2284 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2287 if (error.type() != null_type)
2288 reply.push_back(Pair("result", Value::null));
2290 reply.push_back(Pair("result", result));
2291 reply.push_back(Pair("error", error));
2292 reply.push_back(Pair("id", id));
2293 return write_string(Value(reply), false) + "\n";
2296 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2298 // Send error reply from json-rpc error object
2300 int code = find_value(objError, "code").get_int();
2301 if (code == -32600) nStatus = 400;
2302 else if (code == -32601) nStatus = 404;
2303 string strReply = JSONRPCReply(Value::null, objError, id);
2304 stream << HTTPReply(nStatus, strReply) << std::flush;
2307 bool ClientAllowed(const string& strAddress)
2309 if (strAddress == asio::ip::address_v4::loopback().to_string())
2311 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2312 BOOST_FOREACH(string strAllow, vAllow)
2313 if (WildcardMatch(strAddress, strAllow))
2320 // IOStream device that speaks SSL but can also speak non-SSL
2322 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2324 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2326 fUseSSL = fUseSSLIn;
2327 fNeedHandshake = fUseSSLIn;
2330 void handshake(ssl::stream_base::handshake_type role)
2332 if (!fNeedHandshake) return;
2333 fNeedHandshake = false;
2334 stream.handshake(role);
2336 std::streamsize read(char* s, std::streamsize n)
2338 handshake(ssl::stream_base::server); // HTTPS servers read first
2339 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2340 return stream.next_layer().read_some(asio::buffer(s, n));
2342 std::streamsize write(const char* s, std::streamsize n)
2344 handshake(ssl::stream_base::client); // HTTPS clients write first
2345 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2346 return asio::write(stream.next_layer(), asio::buffer(s, n));
2348 bool connect(const std::string& server, const std::string& port)
2350 ip::tcp::resolver resolver(stream.get_io_service());
2351 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2352 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2353 ip::tcp::resolver::iterator end;
2354 boost::system::error_code error = asio::error::host_not_found;
2355 while (error && endpoint_iterator != end)
2357 stream.lowest_layer().close();
2358 stream.lowest_layer().connect(*endpoint_iterator++, error);
2366 bool fNeedHandshake;
2372 void ThreadRPCServer(void* parg)
2374 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2377 vnThreadsRunning[4]++;
2378 ThreadRPCServer2(parg);
2379 vnThreadsRunning[4]--;
2381 catch (std::exception& e) {
2382 vnThreadsRunning[4]--;
2383 PrintException(&e, "ThreadRPCServer()");
2385 vnThreadsRunning[4]--;
2386 PrintException(NULL, "ThreadRPCServer()");
2388 printf("ThreadRPCServer exiting\n");
2391 void ThreadRPCServer2(void* parg)
2393 printf("ThreadRPCServer started\n");
2395 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2396 if (strRPCUserColonPass == ":")
2398 string strWhatAmI = "To use ppcoind";
2399 if (mapArgs.count("-server"))
2400 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2401 else if (mapArgs.count("-daemon"))
2402 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2404 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2405 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2407 GetConfigFile().c_str());
2409 CreateThread(Shutdown, NULL);
2414 bool fUseSSL = GetBoolArg("-rpcssl");
2415 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2417 asio::io_service io_service;
2418 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2419 ip::tcp::acceptor acceptor(io_service, endpoint);
2421 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2424 ssl::context context(io_service, ssl::context::sslv23);
2427 context.set_options(ssl::context::no_sslv2);
2428 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2429 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2430 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2431 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2432 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2433 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2434 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2435 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2437 string ciphers = GetArg("-rpcsslciphers",
2438 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2439 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2443 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2448 // Accept connection
2450 SSLStream sslStream(io_service, context);
2451 SSLIOStreamDevice d(sslStream, fUseSSL);
2452 iostreams::stream<SSLIOStreamDevice> stream(d);
2454 ip::tcp::iostream stream;
2457 ip::tcp::endpoint peer;
2458 vnThreadsRunning[4]--;
2460 acceptor.accept(sslStream.lowest_layer(), peer);
2462 acceptor.accept(*stream.rdbuf(), peer);
2464 vnThreadsRunning[4]++;
2468 // Restrict callers by IP
2469 if (!ClientAllowed(peer.address().to_string()))
2471 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2473 stream << HTTPReply(403, "") << std::flush;
2477 map<string, string> mapHeaders;
2480 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2481 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2484 printf("ThreadRPCServer ReadHTTP timeout\n");
2488 // Check authorization
2489 if (mapHeaders.count("authorization") == 0)
2491 stream << HTTPReply(401, "") << std::flush;
2494 if (!HTTPAuthorized(mapHeaders))
2496 // Deter brute-forcing short passwords
2497 if (mapArgs["-rpcpassword"].size() < 15)
2500 stream << HTTPReply(401, "") << std::flush;
2501 printf("ThreadRPCServer incorrect password attempt\n");
2505 Value id = Value::null;
2510 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2511 throw JSONRPCError(-32700, "Parse error");
2512 const Object& request = valRequest.get_obj();
2514 // Parse id now so errors from here on will have the id
2515 id = find_value(request, "id");
2518 Value valMethod = find_value(request, "method");
2519 if (valMethod.type() == null_type)
2520 throw JSONRPCError(-32600, "Missing method");
2521 if (valMethod.type() != str_type)
2522 throw JSONRPCError(-32600, "Method must be a string");
2523 string strMethod = valMethod.get_str();
2524 if (strMethod != "getwork" && strMethod != "getmemorypool")
2525 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2528 Value valParams = find_value(request, "params");
2530 if (valParams.type() == array_type)
2531 params = valParams.get_array();
2532 else if (valParams.type() == null_type)
2535 throw JSONRPCError(-32600, "Params must be an array");
2538 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2539 if (mi == mapCallTable.end())
2540 throw JSONRPCError(-32601, "Method not found");
2542 // Observe safe mode
2543 string strWarning = GetWarnings("rpc");
2544 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2545 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2551 CRITICAL_BLOCK(cs_main)
2552 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2553 result = (*(*mi).second)(params, false);
2556 string strReply = JSONRPCReply(result, Value::null, id);
2557 stream << HTTPReply(200, strReply) << std::flush;
2559 catch (std::exception& e)
2561 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2564 catch (Object& objError)
2566 ErrorReply(stream, objError, id);
2568 catch (std::exception& e)
2570 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2578 Object CallRPC(const string& strMethod, const Array& params)
2580 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2581 throw runtime_error(strprintf(
2582 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2583 "If the file does not exist, create it with owner-readable-only file permissions."),
2584 GetConfigFile().c_str()));
2586 // Connect to localhost
2587 bool fUseSSL = GetBoolArg("-rpcssl");
2589 asio::io_service io_service;
2590 ssl::context context(io_service, ssl::context::sslv23);
2591 context.set_options(ssl::context::no_sslv2);
2592 SSLStream sslStream(io_service, context);
2593 SSLIOStreamDevice d(sslStream, fUseSSL);
2594 iostreams::stream<SSLIOStreamDevice> stream(d);
2595 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2596 throw runtime_error("couldn't connect to server");
2599 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2601 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2603 throw runtime_error("couldn't connect to server");
2607 // HTTP basic authentication
2608 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2609 map<string, string> mapRequestHeaders;
2610 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2613 string strRequest = JSONRPCRequest(strMethod, params, 1);
2614 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2615 stream << strPost << std::flush;
2618 map<string, string> mapHeaders;
2620 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2622 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2623 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2624 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2625 else if (strReply.empty())
2626 throw runtime_error("no response from server");
2630 if (!read_string(strReply, valReply))
2631 throw runtime_error("couldn't parse reply from server");
2632 const Object& reply = valReply.get_obj();
2634 throw runtime_error("expected reply to have result, error and id properties");
2642 template<typename T>
2643 void ConvertTo(Value& value)
2645 if (value.type() == str_type)
2647 // reinterpret string as unquoted json value
2649 if (!read_string(value.get_str(), value2))
2650 throw runtime_error("type mismatch");
2651 value = value2.get_value<T>();
2655 value = value.get_value<T>();
2659 int CommandLineRPC(int argc, char *argv[])
2666 while (argc > 1 && IsSwitchChar(argv[1][0]))
2674 throw runtime_error("too few parameters");
2675 string strMethod = argv[1];
2677 // Parameters default to strings
2679 for (int i = 2; i < argc; i++)
2680 params.push_back(argv[i]);
2681 int n = params.size();
2684 // Special case non-string parameter types
2686 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2687 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2688 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2689 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2690 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2691 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2692 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2693 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2694 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2695 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2696 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2697 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2698 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2699 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2700 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2701 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2702 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2703 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2704 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2705 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2706 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2707 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2708 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2709 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2710 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2711 if (strMethod == "sendmany" && n > 1)
2713 string s = params[1].get_str();
2715 if (!read_string(s, v) || v.type() != obj_type)
2716 throw runtime_error("type mismatch");
2717 params[1] = v.get_obj();
2719 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2720 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2721 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2724 Object reply = CallRPC(strMethod, params);
2727 const Value& result = find_value(reply, "result");
2728 const Value& error = find_value(reply, "error");
2730 if (error.type() != null_type)
2733 strPrint = "error: " + write_string(error, false);
2734 int code = find_value(error.get_obj(), "code").get_int();
2740 if (result.type() == null_type)
2742 else if (result.type() == str_type)
2743 strPrint = result.get_str();
2745 strPrint = write_string(result, true);
2748 catch (std::exception& e)
2750 strPrint = string("error: ") + e.what();
2755 PrintException(NULL, "CommandLineRPC()");
2760 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2769 int main(int argc, char *argv[])
2772 // Turn off microsoft heap dump noise
2773 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2774 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2776 setbuf(stdin, NULL);
2777 setbuf(stdout, NULL);
2778 setbuf(stderr, NULL);
2782 if (argc >= 2 && string(argv[1]) == "-server")
2784 printf("server ready\n");
2785 ThreadRPCServer(NULL);
2789 return CommandLineRPC(argc, argv);
2792 catch (std::exception& e) {
2793 PrintException(&e, "main()");
2795 PrintException(NULL, "main()");