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 const CBlockIndex* pindexLastProofOfWork = GetLastBlockIndex(pindexBest, false);
220 int nShift = (pindexLastProofOfWork->nBits >> 24) & 0xff;
223 (double)0x0000ffff / (double)(pindexLastProofOfWork->nBits & 0x00ffffff);
239 Value getdifficulty(const Array& params, bool fHelp)
241 if (fHelp || params.size() != 0)
244 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
246 return GetDifficulty();
250 Value getgenerate(const Array& params, bool fHelp)
252 if (fHelp || params.size() != 0)
255 "Returns true or false.");
257 return (bool)fGenerateBitcoins;
261 Value setgenerate(const Array& params, bool fHelp)
263 if (fHelp || params.size() < 1 || params.size() > 2)
265 "setgenerate <generate> [genproclimit]\n"
266 "<generate> is true or false to turn generation on or off.\n"
267 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
269 bool fGenerate = true;
270 if (params.size() > 0)
271 fGenerate = params[0].get_bool();
273 if (params.size() > 1)
275 int nGenProcLimit = params[1].get_int();
276 fLimitProcessors = (nGenProcLimit != -1);
277 WriteSetting("fLimitProcessors", fLimitProcessors);
278 if (nGenProcLimit != -1)
279 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
280 if (nGenProcLimit == 0)
284 GenerateBitcoins(fGenerate, pwalletMain);
289 Value gethashespersec(const Array& params, bool fHelp)
291 if (fHelp || params.size() != 0)
294 "Returns a recent hashes per second performance measurement while generating.");
296 if (GetTimeMillis() - nHPSTimerStart > 8000)
297 return (boost::int64_t)0;
298 return (boost::int64_t)dHashesPerSec;
302 Value getinfo(const Array& params, bool fHelp)
304 if (fHelp || params.size() != 0)
307 "Returns an object containing various state info.");
310 obj.push_back(Pair("version", (int)VERSION));
311 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
312 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
313 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
314 obj.push_back(Pair("blocks", (int)nBestHeight));
315 obj.push_back(Pair("connections", (int)vNodes.size()));
316 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
317 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
318 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
319 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
320 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
321 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
322 obj.push_back(Pair("testnet", fTestNet));
323 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
324 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
325 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
326 if (pwalletMain->IsCrypted())
327 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
328 obj.push_back(Pair("errors", GetWarnings("statusbar")));
333 Value getnewaddress(const Array& params, bool fHelp)
335 if (fHelp || params.size() > 1)
337 "getnewaddress [account]\n"
338 "Returns a new ppcoin address for receiving payments. "
339 "If [account] is specified (recommended), it is added to the address book "
340 "so payments received with the address will be credited to [account].");
342 // Parse the account first so we don't generate a key if there's an error
344 if (params.size() > 0)
345 strAccount = AccountFromValue(params[0]);
347 if (!pwalletMain->IsLocked())
348 pwalletMain->TopUpKeyPool();
350 // Generate a new key that is added to wallet
351 std::vector<unsigned char> newKey;
352 if (!pwalletMain->GetKeyFromPool(newKey, false))
353 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
354 CBitcoinAddress address(newKey);
356 pwalletMain->SetAddressBookName(address, strAccount);
358 return address.ToString();
362 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
364 CWalletDB walletdb(pwalletMain->strWalletFile);
367 walletdb.ReadAccount(strAccount, account);
369 bool bKeyUsed = false;
371 // Check if the current key has been used
372 if (!account.vchPubKey.empty())
374 CScript scriptPubKey;
375 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
376 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
377 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
380 const CWalletTx& wtx = (*it).second;
381 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
382 if (txout.scriptPubKey == scriptPubKey)
387 // Generate a new key
388 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
390 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
391 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
393 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
394 walletdb.WriteAccount(strAccount, account);
397 return CBitcoinAddress(account.vchPubKey);
400 Value getaccountaddress(const Array& params, bool fHelp)
402 if (fHelp || params.size() != 1)
404 "getaccountaddress <account>\n"
405 "Returns the current ppcoin address for receiving payments to this account.");
407 // Parse the account first so we don't generate a key if there's an error
408 string strAccount = AccountFromValue(params[0]);
412 ret = GetAccountAddress(strAccount).ToString();
419 Value setaccount(const Array& params, bool fHelp)
421 if (fHelp || params.size() < 1 || params.size() > 2)
423 "setaccount <ppcoinaddress> <account>\n"
424 "Sets the account associated with the given address.");
426 CBitcoinAddress address(params[0].get_str());
427 if (!address.IsValid())
428 throw JSONRPCError(-5, "Invalid ppcoin address");
432 if (params.size() > 1)
433 strAccount = AccountFromValue(params[1]);
435 // Detect when changing the account of an address that is the 'unused current key' of another account:
436 if (pwalletMain->mapAddressBook.count(address))
438 string strOldAccount = pwalletMain->mapAddressBook[address];
439 if (address == GetAccountAddress(strOldAccount))
440 GetAccountAddress(strOldAccount, true);
443 pwalletMain->SetAddressBookName(address, strAccount);
449 Value getaccount(const Array& params, bool fHelp)
451 if (fHelp || params.size() != 1)
453 "getaccount <ppcoinaddress>\n"
454 "Returns the account associated with the given address.");
456 CBitcoinAddress address(params[0].get_str());
457 if (!address.IsValid())
458 throw JSONRPCError(-5, "Invalid ppcoin address");
461 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
462 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
463 strAccount = (*mi).second;
468 Value getaddressesbyaccount(const Array& params, bool fHelp)
470 if (fHelp || params.size() != 1)
472 "getaddressesbyaccount <account>\n"
473 "Returns the list of addresses for the given account.");
475 string strAccount = AccountFromValue(params[0]);
477 // Find all addresses that have the given account
479 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
481 const CBitcoinAddress& address = item.first;
482 const string& strName = item.second;
483 if (strName == strAccount)
484 ret.push_back(address.ToString());
489 Value settxfee(const Array& params, bool fHelp)
491 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
493 "settxfee <amount>\n"
494 "<amount> is a real and is rounded to 0.01 (cent)\n"
495 "Minimum and default transaction fee per KB is 1 cent");
497 nTransactionFee = AmountFromValue(params[0]);
498 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
502 Value sendtoaddress(const Array& params, bool fHelp)
504 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
506 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
507 "<amount> is a real and is rounded to the nearest 0.000001\n"
508 "requires wallet passphrase to be set with walletpassphrase first");
509 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
511 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
512 "<amount> is a real and is rounded to the nearest 0.000001");
514 CBitcoinAddress address(params[0].get_str());
515 if (!address.IsValid())
516 throw JSONRPCError(-5, "Invalid ppcoin address");
519 int64 nAmount = AmountFromValue(params[1]);
523 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
524 wtx.mapValue["comment"] = params[2].get_str();
525 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
526 wtx.mapValue["to"] = params[3].get_str();
528 if (pwalletMain->IsLocked())
529 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
531 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
533 throw JSONRPCError(-4, strError);
535 return wtx.GetHash().GetHex();
538 static const string strMessageMagic = "Bitcoin Signed Message:\n";
540 Value signmessage(const Array& params, bool fHelp)
542 if (fHelp || params.size() != 2)
544 "signmessage <ppcoinaddress> <message>\n"
545 "Sign a message with the private key of an address");
547 if (pwalletMain->IsLocked())
548 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
550 string strAddress = params[0].get_str();
551 string strMessage = params[1].get_str();
553 CBitcoinAddress addr(strAddress);
555 throw JSONRPCError(-3, "Invalid address");
558 if (!pwalletMain->GetKey(addr, key))
559 throw JSONRPCError(-4, "Private key not available");
561 CDataStream ss(SER_GETHASH);
562 ss << strMessageMagic;
565 vector<unsigned char> vchSig;
566 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
567 throw JSONRPCError(-5, "Sign failed");
569 return EncodeBase64(&vchSig[0], vchSig.size());
572 Value verifymessage(const Array& params, bool fHelp)
574 if (fHelp || params.size() != 3)
576 "verifymessage <ppcoinaddress> <signature> <message>\n"
577 "Verify a signed message");
579 string strAddress = params[0].get_str();
580 string strSign = params[1].get_str();
581 string strMessage = params[2].get_str();
583 CBitcoinAddress addr(strAddress);
585 throw JSONRPCError(-3, "Invalid address");
587 bool fInvalid = false;
588 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
591 throw JSONRPCError(-5, "Malformed base64 encoding");
593 CDataStream ss(SER_GETHASH);
594 ss << strMessageMagic;
598 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
601 return (key.GetAddress() == addr);
605 Value getreceivedbyaddress(const Array& params, bool fHelp)
607 if (fHelp || params.size() < 1 || params.size() > 2)
609 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
610 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
613 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
614 CScript scriptPubKey;
615 if (!address.IsValid())
616 throw JSONRPCError(-5, "Invalid ppcoin address");
617 scriptPubKey.SetBitcoinAddress(address);
618 if (!IsMine(*pwalletMain,scriptPubKey))
621 // Minimum confirmations
623 if (params.size() > 1)
624 nMinDepth = params[1].get_int();
628 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
630 const CWalletTx& wtx = (*it).second;
631 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
634 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
635 if (txout.scriptPubKey == scriptPubKey)
636 if (wtx.GetDepthInMainChain() >= nMinDepth)
637 nAmount += txout.nValue;
640 return ValueFromAmount(nAmount);
644 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
646 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
648 const CBitcoinAddress& address = item.first;
649 const string& strName = item.second;
650 if (strName == strAccount)
651 setAddress.insert(address);
656 Value getreceivedbyaccount(const Array& params, bool fHelp)
658 if (fHelp || params.size() < 1 || params.size() > 2)
660 "getreceivedbyaccount <account> [minconf=1]\n"
661 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
663 // Minimum confirmations
665 if (params.size() > 1)
666 nMinDepth = params[1].get_int();
668 // Get the set of pub keys that have the label
669 string strAccount = AccountFromValue(params[0]);
670 set<CBitcoinAddress> setAddress;
671 GetAccountAddresses(strAccount, setAddress);
675 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
677 const CWalletTx& wtx = (*it).second;
678 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
681 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
683 CBitcoinAddress address;
684 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
685 if (wtx.GetDepthInMainChain() >= nMinDepth)
686 nAmount += txout.nValue;
690 return (double)nAmount / (double)COIN;
694 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
698 // Tally wallet transactions
699 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
701 const CWalletTx& wtx = (*it).second;
705 int64 nGenerated, nReceived, nSent, nFee;
706 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
708 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
709 nBalance += nReceived;
710 nBalance += nGenerated - nSent - nFee;
713 // Tally internal accounting entries
714 nBalance += walletdb.GetAccountCreditDebit(strAccount);
719 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
721 CWalletDB walletdb(pwalletMain->strWalletFile);
722 return GetAccountBalance(walletdb, strAccount, nMinDepth);
726 Value getbalance(const Array& params, bool fHelp)
728 if (fHelp || params.size() > 2)
730 "getbalance [account] [minconf=1]\n"
731 "If [account] is not specified, returns the server's total available balance.\n"
732 "If [account] is specified, returns the balance in the account.");
734 if (params.size() == 0)
735 return ValueFromAmount(pwalletMain->GetBalance());
738 if (params.size() > 1)
739 nMinDepth = params[1].get_int();
741 if (params[0].get_str() == "*") {
742 // Calculate total balance a different way from GetBalance()
743 // (GetBalance() sums up all unspent TxOuts)
744 // getbalance and getbalance '*' should always return the same number.
746 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
748 const CWalletTx& wtx = (*it).second;
752 int64 allGeneratedImmature, allGeneratedMature, allFee;
753 allGeneratedImmature = allGeneratedMature = allFee = 0;
754 string strSentAccount;
755 list<pair<CBitcoinAddress, int64> > listReceived;
756 list<pair<CBitcoinAddress, int64> > listSent;
757 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
758 if (wtx.GetDepthInMainChain() >= nMinDepth)
759 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
760 nBalance += r.second;
761 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
762 nBalance -= r.second;
764 nBalance += allGeneratedMature;
766 return ValueFromAmount(nBalance);
769 string strAccount = AccountFromValue(params[0]);
771 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
773 return ValueFromAmount(nBalance);
777 Value movecmd(const Array& params, bool fHelp)
779 if (fHelp || params.size() < 3 || params.size() > 5)
781 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
782 "Move from one account in your wallet to another.");
784 string strFrom = AccountFromValue(params[0]);
785 string strTo = AccountFromValue(params[1]);
786 int64 nAmount = AmountFromValue(params[2]);
787 if (params.size() > 3)
788 // unused parameter, used to be nMinDepth, keep type-checking it though
789 (void)params[3].get_int();
791 if (params.size() > 4)
792 strComment = params[4].get_str();
794 CWalletDB walletdb(pwalletMain->strWalletFile);
797 int64 nNow = GetAdjustedTime();
800 CAccountingEntry debit;
801 debit.strAccount = strFrom;
802 debit.nCreditDebit = -nAmount;
804 debit.strOtherAccount = strTo;
805 debit.strComment = strComment;
806 walletdb.WriteAccountingEntry(debit);
809 CAccountingEntry credit;
810 credit.strAccount = strTo;
811 credit.nCreditDebit = nAmount;
813 credit.strOtherAccount = strFrom;
814 credit.strComment = strComment;
815 walletdb.WriteAccountingEntry(credit);
817 walletdb.TxnCommit();
823 Value sendfrom(const Array& params, bool fHelp)
825 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
827 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
828 "<amount> is a real and is rounded to the nearest 0.000001\n"
829 "requires wallet passphrase to be set with walletpassphrase first");
830 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
832 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
833 "<amount> is a real and is rounded to the nearest 0.000001");
835 string strAccount = AccountFromValue(params[0]);
836 CBitcoinAddress address(params[1].get_str());
837 if (!address.IsValid())
838 throw JSONRPCError(-5, "Invalid ppcoin address");
839 int64 nAmount = AmountFromValue(params[2]);
841 if (params.size() > 3)
842 nMinDepth = params[3].get_int();
845 wtx.strFromAccount = strAccount;
846 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
847 wtx.mapValue["comment"] = params[4].get_str();
848 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
849 wtx.mapValue["to"] = params[5].get_str();
851 if (pwalletMain->IsLocked())
852 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
855 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
856 if (nAmount > nBalance)
857 throw JSONRPCError(-6, "Account has insufficient funds");
860 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
862 throw JSONRPCError(-4, strError);
864 return wtx.GetHash().GetHex();
868 Value sendmany(const Array& params, bool fHelp)
870 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
872 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
873 "amounts are double-precision floating point numbers\n"
874 "requires wallet passphrase to be set with walletpassphrase first");
875 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
877 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
878 "amounts are double-precision floating point numbers");
880 string strAccount = AccountFromValue(params[0]);
881 Object sendTo = params[1].get_obj();
883 if (params.size() > 2)
884 nMinDepth = params[2].get_int();
887 wtx.strFromAccount = strAccount;
888 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
889 wtx.mapValue["comment"] = params[3].get_str();
891 set<CBitcoinAddress> setAddress;
892 vector<pair<CScript, int64> > vecSend;
894 int64 totalAmount = 0;
895 BOOST_FOREACH(const Pair& s, sendTo)
897 CBitcoinAddress address(s.name_);
898 if (!address.IsValid())
899 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
901 if (setAddress.count(address))
902 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
903 setAddress.insert(address);
905 CScript scriptPubKey;
906 scriptPubKey.SetBitcoinAddress(address);
907 int64 nAmount = AmountFromValue(s.value_);
908 totalAmount += nAmount;
910 vecSend.push_back(make_pair(scriptPubKey, nAmount));
913 if (pwalletMain->IsLocked())
914 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
915 if (fWalletUnlockStakeOnly)
916 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
919 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
920 if (totalAmount > nBalance)
921 throw JSONRPCError(-6, "Account has insufficient funds");
924 CReserveKey keyChange(pwalletMain);
925 int64 nFeeRequired = 0;
926 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
929 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
930 throw JSONRPCError(-6, "Insufficient funds");
931 throw JSONRPCError(-4, "Transaction creation failed");
933 if (!pwalletMain->CommitTransaction(wtx, keyChange))
934 throw JSONRPCError(-4, "Transaction commit failed");
936 return wtx.GetHash().GetHex();
951 Value ListReceived(const Array& params, bool fByAccounts)
953 // Minimum confirmations
955 if (params.size() > 0)
956 nMinDepth = params[0].get_int();
958 // Whether to include empty accounts
959 bool fIncludeEmpty = false;
960 if (params.size() > 1)
961 fIncludeEmpty = params[1].get_bool();
964 map<CBitcoinAddress, tallyitem> mapTally;
965 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
967 const CWalletTx& wtx = (*it).second;
968 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
971 int nDepth = wtx.GetDepthInMainChain();
972 if (nDepth < nMinDepth)
975 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
977 CBitcoinAddress address;
978 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
981 tallyitem& item = mapTally[address];
982 item.nAmount += txout.nValue;
983 item.nConf = min(item.nConf, nDepth);
989 map<string, tallyitem> mapAccountTally;
990 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
992 const CBitcoinAddress& address = item.first;
993 const string& strAccount = item.second;
994 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
995 if (it == mapTally.end() && !fIncludeEmpty)
1000 if (it != mapTally.end())
1002 nAmount = (*it).second.nAmount;
1003 nConf = (*it).second.nConf;
1008 tallyitem& item = mapAccountTally[strAccount];
1009 item.nAmount += nAmount;
1010 item.nConf = min(item.nConf, nConf);
1015 obj.push_back(Pair("address", address.ToString()));
1016 obj.push_back(Pair("account", strAccount));
1017 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1018 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1025 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1027 int64 nAmount = (*it).second.nAmount;
1028 int nConf = (*it).second.nConf;
1030 obj.push_back(Pair("account", (*it).first));
1031 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1032 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1040 Value listreceivedbyaddress(const Array& params, bool fHelp)
1042 if (fHelp || params.size() > 2)
1043 throw runtime_error(
1044 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1045 "[minconf] is the minimum number of confirmations before payments are included.\n"
1046 "[includeempty] whether to include addresses that haven't received any payments.\n"
1047 "Returns an array of objects containing:\n"
1048 " \"address\" : receiving address\n"
1049 " \"account\" : the account of the receiving address\n"
1050 " \"amount\" : total amount received by the address\n"
1051 " \"confirmations\" : number of confirmations of the most recent transaction included");
1053 return ListReceived(params, false);
1056 Value listreceivedbyaccount(const Array& params, bool fHelp)
1058 if (fHelp || params.size() > 2)
1059 throw runtime_error(
1060 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1061 "[minconf] is the minimum number of confirmations before payments are included.\n"
1062 "[includeempty] whether to include accounts that haven't received any payments.\n"
1063 "Returns an array of objects containing:\n"
1064 " \"account\" : the account of the receiving addresses\n"
1065 " \"amount\" : total amount received by addresses with this account\n"
1066 " \"confirmations\" : number of confirmations of the most recent transaction included");
1068 return ListReceived(params, true);
1071 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1073 int64 nGeneratedImmature, nGeneratedMature, nFee;
1074 string strSentAccount;
1075 list<pair<CBitcoinAddress, int64> > listReceived;
1076 list<pair<CBitcoinAddress, int64> > listSent;
1077 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1079 bool fAllAccounts = (strAccount == string("*"));
1081 // Generated blocks assigned to account ""
1082 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1085 entry.push_back(Pair("account", string("")));
1086 if (nGeneratedImmature)
1088 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1089 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1093 entry.push_back(Pair("category", "generate"));
1094 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1097 WalletTxToJSON(wtx, entry);
1098 ret.push_back(entry);
1102 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1104 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1107 entry.push_back(Pair("account", strSentAccount));
1108 entry.push_back(Pair("address", s.first.ToString()));
1109 entry.push_back(Pair("category", "send"));
1110 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1111 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1113 WalletTxToJSON(wtx, entry);
1114 ret.push_back(entry);
1119 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1120 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1123 if (pwalletMain->mapAddressBook.count(r.first))
1124 account = pwalletMain->mapAddressBook[r.first];
1125 if (fAllAccounts || (account == strAccount))
1128 entry.push_back(Pair("account", account));
1129 entry.push_back(Pair("address", r.first.ToString()));
1130 entry.push_back(Pair("category", "receive"));
1131 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1133 WalletTxToJSON(wtx, entry);
1134 ret.push_back(entry);
1139 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1141 bool fAllAccounts = (strAccount == string("*"));
1143 if (fAllAccounts || acentry.strAccount == strAccount)
1146 entry.push_back(Pair("account", acentry.strAccount));
1147 entry.push_back(Pair("category", "move"));
1148 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1149 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1150 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1151 entry.push_back(Pair("comment", acentry.strComment));
1152 ret.push_back(entry);
1156 Value listtransactions(const Array& params, bool fHelp)
1158 if (fHelp || params.size() > 3)
1159 throw runtime_error(
1160 "listtransactions [account] [count=10] [from=0]\n"
1161 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1163 string strAccount = "*";
1164 if (params.size() > 0)
1165 strAccount = params[0].get_str();
1167 if (params.size() > 1)
1168 nCount = params[1].get_int();
1170 if (params.size() > 2)
1171 nFrom = params[2].get_int();
1174 CWalletDB walletdb(pwalletMain->strWalletFile);
1176 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1177 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1178 typedef multimap<int64, TxPair > TxItems;
1181 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1183 CWalletTx* wtx = &((*it).second);
1184 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1186 list<CAccountingEntry> acentries;
1187 walletdb.ListAccountCreditDebit(strAccount, acentries);
1188 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1190 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1193 // Now: iterate backwards until we have nCount items to return:
1194 TxItems::reverse_iterator it = txByTime.rbegin();
1195 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1196 for (; it != txByTime.rend(); ++it)
1198 CWalletTx *const pwtx = (*it).second.first;
1200 ListTransactions(*pwtx, strAccount, 0, true, ret);
1201 CAccountingEntry *const pacentry = (*it).second.second;
1203 AcentryToJSON(*pacentry, strAccount, ret);
1205 if (ret.size() >= nCount) break;
1207 // ret is now newest to oldest
1209 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1210 if (ret.size() > nCount)
1212 Array::iterator last = ret.begin();
1213 std::advance(last, nCount);
1214 ret.erase(last, ret.end());
1216 std::reverse(ret.begin(), ret.end()); // oldest to newest
1221 Value listaccounts(const Array& params, bool fHelp)
1223 if (fHelp || params.size() > 1)
1224 throw runtime_error(
1225 "listaccounts [minconf=1]\n"
1226 "Returns Object that has account names as keys, account balances as values.");
1229 if (params.size() > 0)
1230 nMinDepth = params[0].get_int();
1232 map<string, int64> mapAccountBalances;
1233 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1234 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1235 mapAccountBalances[entry.second] = 0;
1238 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1240 const CWalletTx& wtx = (*it).second;
1241 int64 nGeneratedImmature, nGeneratedMature, nFee;
1242 string strSentAccount;
1243 list<pair<CBitcoinAddress, int64> > listReceived;
1244 list<pair<CBitcoinAddress, int64> > listSent;
1245 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1246 mapAccountBalances[strSentAccount] -= nFee;
1247 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1248 mapAccountBalances[strSentAccount] -= s.second;
1249 if (wtx.GetDepthInMainChain() >= nMinDepth)
1251 mapAccountBalances[""] += nGeneratedMature;
1252 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1253 if (pwalletMain->mapAddressBook.count(r.first))
1254 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1256 mapAccountBalances[""] += r.second;
1260 list<CAccountingEntry> acentries;
1261 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1262 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1263 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1266 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1267 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1272 Value listsinceblock(const Array& params, bool fHelp)
1275 throw runtime_error(
1276 "listsinceblock [blockid] [target-confirmations]\n"
1277 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1279 CBlockIndex *pindex = NULL;
1280 int target_confirms = 1;
1282 if (params.size() > 0)
1284 uint256 blockId = 0;
1286 blockId.SetHex(params[0].get_str());
1287 pindex = CBlockLocator(blockId).GetBlockIndex();
1290 if (params.size() > 1)
1292 target_confirms = params[1].get_int();
1294 if (target_confirms < 1)
1295 throw JSONRPCError(-8, "Invalid parameter");
1298 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1302 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1304 CWalletTx tx = (*it).second;
1306 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1307 ListTransactions(tx, "*", 0, true, transactions);
1312 if (target_confirms == 1)
1315 lastblock = hashBestChain;
1319 int target_height = pindexBest->nHeight + 1 - target_confirms;
1322 for (block = pindexBest;
1323 block && block->nHeight > target_height;
1324 block = block->pprev);
1326 lastblock = block ? block->GetBlockHash() : 0;
1330 ret.push_back(Pair("transactions", transactions));
1331 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1336 Value gettransaction(const Array& params, bool fHelp)
1338 if (fHelp || params.size() != 1)
1339 throw runtime_error(
1340 "gettransaction <txid>\n"
1341 "Get detailed information about <txid>");
1344 hash.SetHex(params[0].get_str());
1348 if (!pwalletMain->mapWallet.count(hash))
1349 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1350 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1352 int64 nCredit = wtx.GetCredit();
1353 int64 nDebit = wtx.GetDebit();
1354 int64 nNet = nCredit - nDebit;
1355 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1357 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1359 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1361 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1364 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1365 entry.push_back(Pair("details", details));
1371 Value backupwallet(const Array& params, bool fHelp)
1373 if (fHelp || params.size() != 1)
1374 throw runtime_error(
1375 "backupwallet <destination>\n"
1376 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1378 string strDest = params[0].get_str();
1379 BackupWallet(*pwalletMain, strDest);
1385 Value keypoolrefill(const Array& params, bool fHelp)
1387 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1388 throw runtime_error(
1390 "Fills the keypool, requires wallet passphrase to be set.");
1391 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1392 throw runtime_error(
1394 "Fills the keypool.");
1396 if (pwalletMain->IsLocked())
1397 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1399 pwalletMain->TopUpKeyPool();
1401 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1402 throw JSONRPCError(-4, "Error refreshing keypool.");
1408 void ThreadTopUpKeyPool(void* parg)
1410 pwalletMain->TopUpKeyPool();
1413 void ThreadCleanWalletPassphrase(void* parg)
1415 int64 nMyWakeTime = GetTime() + *((int*)parg);
1417 if (nWalletUnlockTime == 0)
1419 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1421 nWalletUnlockTime = nMyWakeTime;
1424 while (GetTime() < nWalletUnlockTime)
1425 Sleep(GetTime() - nWalletUnlockTime);
1427 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1429 nWalletUnlockTime = 0;
1434 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1436 if (nWalletUnlockTime < nMyWakeTime)
1437 nWalletUnlockTime = nMyWakeTime;
1443 pwalletMain->Lock();
1448 Value walletpassphrase(const Array& params, bool fHelp)
1450 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1451 throw runtime_error(
1452 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1453 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1454 "stakeonly is optional true/false allowing only stake creation.");
1457 if (!pwalletMain->IsCrypted())
1458 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1460 if (!pwalletMain->IsLocked())
1461 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1463 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1464 SecureString strWalletPass;
1465 strWalletPass.reserve(100);
1466 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1467 // Alternately, find a way to make params[0] mlock()'d to begin with.
1468 strWalletPass = params[0].get_str().c_str();
1470 if (strWalletPass.length() > 0)
1472 if (!pwalletMain->Unlock(strWalletPass))
1473 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1476 throw runtime_error(
1477 "walletpassphrase <passphrase> <timeout>\n"
1478 "Stores the wallet decryption key in memory for <timeout> seconds.");
1480 CreateThread(ThreadTopUpKeyPool, NULL);
1481 int* pnSleepTime = new int(params[1].get_int());
1482 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1484 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1485 if (params.size() > 2)
1486 fWalletUnlockStakeOnly = params[2].get_bool();
1488 fWalletUnlockStakeOnly = false;
1494 Value walletpassphrasechange(const Array& params, bool fHelp)
1496 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1497 throw runtime_error(
1498 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1499 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1502 if (!pwalletMain->IsCrypted())
1503 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1505 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1506 // Alternately, find a way to make params[0] mlock()'d to begin with.
1507 SecureString strOldWalletPass;
1508 strOldWalletPass.reserve(100);
1509 strOldWalletPass = params[0].get_str().c_str();
1511 SecureString strNewWalletPass;
1512 strNewWalletPass.reserve(100);
1513 strNewWalletPass = params[1].get_str().c_str();
1515 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1516 throw runtime_error(
1517 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1518 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1520 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1521 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1527 Value walletlock(const Array& params, bool fHelp)
1529 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1530 throw runtime_error(
1532 "Removes the wallet encryption key from memory, locking the wallet.\n"
1533 "After calling this method, you will need to call walletpassphrase again\n"
1534 "before being able to call any methods which require the wallet to be unlocked.");
1537 if (!pwalletMain->IsCrypted())
1538 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1540 pwalletMain->Lock();
1541 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1543 nWalletUnlockTime = 0;
1550 Value encryptwallet(const Array& params, bool fHelp)
1552 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1553 throw runtime_error(
1554 "encryptwallet <passphrase>\n"
1555 "Encrypts the wallet with <passphrase>.");
1558 if (pwalletMain->IsCrypted())
1559 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1562 // shutting down via RPC while the GUI is running does not work (yet):
1563 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1566 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1567 // Alternately, find a way to make params[0] mlock()'d to begin with.
1568 SecureString strWalletPass;
1569 strWalletPass.reserve(100);
1570 strWalletPass = params[0].get_str().c_str();
1572 if (strWalletPass.length() < 1)
1573 throw runtime_error(
1574 "encryptwallet <passphrase>\n"
1575 "Encrypts the wallet with <passphrase>.");
1577 if (!pwalletMain->EncryptWallet(strWalletPass))
1578 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1580 // BDB seems to have a bad habit of writing old data into
1581 // slack space in .dat files; that is bad if the old data is
1582 // unencrypted private keys. So:
1583 CreateThread(Shutdown, NULL);
1584 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1588 Value validateaddress(const Array& params, bool fHelp)
1590 if (fHelp || params.size() != 1)
1591 throw runtime_error(
1592 "validateaddress <ppcoinaddress>\n"
1593 "Return information about <ppcoinaddress>.");
1595 CBitcoinAddress address(params[0].get_str());
1596 bool isValid = address.IsValid();
1599 ret.push_back(Pair("isvalid", isValid));
1602 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1603 // version of the address:
1604 string currentAddress = address.ToString();
1605 ret.push_back(Pair("address", currentAddress));
1606 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1607 if (pwalletMain->mapAddressBook.count(address))
1608 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1614 Value getwork(const Array& params, bool fHelp)
1616 if (fHelp || params.size() > 1)
1617 throw runtime_error(
1619 "If [data] is not specified, returns formatted hash data to work on:\n"
1620 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1621 " \"data\" : block data\n"
1622 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1623 " \"target\" : little endian hash target\n"
1624 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1627 throw JSONRPCError(-9, "PPCoin is not connected!");
1629 if (IsInitialBlockDownload())
1630 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1632 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1633 static mapNewBlock_t mapNewBlock;
1634 static vector<CBlock*> vNewBlock;
1635 static CReserveKey reservekey(pwalletMain);
1637 if (params.size() == 0)
1640 static unsigned int nTransactionsUpdatedLast;
1641 static CBlockIndex* pindexPrev;
1642 static int64 nStart;
1643 static CBlock* pblock;
1644 if (pindexPrev != pindexBest ||
1645 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1647 if (pindexPrev != pindexBest)
1649 // Deallocate old blocks since they're obsolete now
1650 mapNewBlock.clear();
1651 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1655 nTransactionsUpdatedLast = nTransactionsUpdated;
1656 pindexPrev = pindexBest;
1660 pblock = CreateNewBlock(pwalletMain, true);
1662 throw JSONRPCError(-7, "Out of memory");
1663 vNewBlock.push_back(pblock);
1667 pblock->nTime = max(pblock->GetBlockTime(), GetAdjustedTime());
1670 // Update nExtraNonce
1671 static unsigned int nExtraNonce = 0;
1672 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1675 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1677 // Prebuild hash buffers
1681 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1683 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1686 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1687 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1688 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1689 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1695 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1696 if (vchData.size() != 128)
1697 throw JSONRPCError(-8, "Invalid parameter");
1698 CBlock* pdata = (CBlock*)&vchData[0];
1701 for (int i = 0; i < 128/4; i++)
1702 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1705 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1707 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1709 pblock->nTime = pdata->nTime;
1710 pblock->nNonce = pdata->nNonce;
1711 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1712 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1713 if (!pblock->SignBlock(*pwalletMain))
1714 throw JSONRPCError(-100, "Unable to sign block");
1716 return CheckWork(pblock, *pwalletMain, reservekey);
1721 Value getmemorypool(const Array& params, bool fHelp)
1723 if (fHelp || params.size() > 1)
1724 throw runtime_error(
1725 "getmemorypool [data]\n"
1726 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1727 " \"version\" : block version\n"
1728 " \"previousblockhash\" : hash of current highest block\n"
1729 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1730 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1731 " \"time\" : timestamp appropriate for next block\n"
1732 " \"bits\" : compressed target of next block\n"
1733 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1735 if (params.size() == 0)
1738 throw JSONRPCError(-9, "PPCoin is not connected!");
1740 if (IsInitialBlockDownload())
1741 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1743 static CReserveKey reservekey(pwalletMain);
1746 static unsigned int nTransactionsUpdatedLast;
1747 static CBlockIndex* pindexPrev;
1748 static int64 nStart;
1749 static CBlock* pblock;
1750 if (pindexPrev != pindexBest ||
1751 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1753 nTransactionsUpdatedLast = nTransactionsUpdated;
1754 pindexPrev = pindexBest;
1760 pblock = CreateNewBlock(pwalletMain);
1762 throw JSONRPCError(-7, "Out of memory");
1766 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1770 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1771 if(tx.IsCoinBase() || tx.IsCoinStake())
1777 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1781 result.push_back(Pair("version", pblock->nVersion));
1782 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1783 result.push_back(Pair("transactions", transactions));
1784 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1785 result.push_back(Pair("time", (int64_t)pblock->nTime));
1791 uBits.nBits = htonl((int32_t)pblock->nBits);
1792 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1799 CDataStream ssBlock(ParseHex(params[0].get_str()));
1803 return ProcessBlock(NULL, &pblock);
1808 // ppcoin: get information of sync-checkpoint
1809 Value getcheckpoint(const Array& params, bool fHelp)
1811 if (fHelp || params.size() != 0)
1812 throw runtime_error(
1814 "Show info of synchronized checkpoint.\n");
1817 CBlockIndex* pindexCheckpoint;
1819 result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
1820 pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint];
1821 result.push_back(Pair("height", pindexCheckpoint->nHeight));
1822 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str()));
1828 // ppcoin: reserve balance from being staked for network protection
1829 Value reservebalance(const Array& params, bool fHelp)
1831 if (fHelp || params.size() > 2)
1832 throw runtime_error(
1833 "reservebalance [<reserve> [amount]]\n"
1834 "<reserve> is true or false to turn balance reserve on or off.\n"
1835 "<amount> is a real and rounded to cent.\n"
1836 "Set reserve amount not participating in network protection.\n"
1837 "If no parameters provided current setting is printed.\n");
1839 if (params.size() > 0)
1841 bool fReserve = params[0].get_bool();
1844 if (params.size() == 1)
1845 throw runtime_error("must provide amount to reserve balance.\n");
1846 int64 nAmount = AmountFromValue(params[1]);
1847 nAmount = (nAmount / CENT) * CENT; // round to cent
1849 throw runtime_error("amount cannot be negative.\n");
1850 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1854 if (params.size() > 1)
1855 throw runtime_error("cannot specify amount to turn off reserve.\n");
1856 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1861 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1862 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1867 // ppcoin: check wallet integrity
1868 Value checkwallet(const Array& params, bool fHelp)
1870 if (fHelp || params.size() > 0)
1871 throw runtime_error(
1873 "Check wallet for integrity.\n");
1876 int64 nBalanceInQuestion;
1877 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1880 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1881 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1888 // ppcoin: repair wallet
1889 Value repairwallet(const Array& params, bool fHelp)
1891 if (fHelp || params.size() > 0)
1892 throw runtime_error(
1894 "Repair wallet if checkwallet reports any problem.\n");
1897 int64 nBalanceInQuestion;
1898 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1900 if (nMismatchSpent == 0)
1902 result.push_back(Pair("wallet check passed", true));
1906 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1907 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1912 // ppcoin: make a public-private key pair
1913 Value makekeypair(const Array& params, bool fHelp)
1915 if (fHelp || params.size() > 1)
1916 throw runtime_error(
1917 "makekeypair [prefix]\n"
1918 "Make a public/private key pair.\n"
1919 "[prefix] is optional preferred prefix for the public key.\n");
1921 string strPrefix = "";
1922 if (params.size() > 0)
1923 strPrefix = params[0].get_str();
1929 } while (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
1931 CPrivKey vchPrivKey = key.GetPrivKey();
1933 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1934 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1938 extern CCriticalSection cs_mapAlerts;
1939 extern map<uint256, CAlert> mapAlerts;
1941 // ppcoin: send alert.
1942 // There is a known deadlock situation with ThreadMessageHandler
1943 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1944 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1945 Value sendalert(const Array& params, bool fHelp)
1947 if (fHelp || params.size() < 5)
1948 throw runtime_error(
1949 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1950 "<message> is the alert text message\n"
1951 "<privatekey> is hex string of alert master private key\n"
1952 "<minver> is the minimum applicable client version\n"
1953 "<maxver> is the maximum applicable client version\n"
1954 "<id> is the alert id\n"
1955 "[cancelupto] cancels all alert id's up to this number\n"
1956 "Returns true or false.");
1961 alert.strStatusBar = params[0].get_str();
1962 alert.nMinVer = params[2].get_int();
1963 alert.nMaxVer = params[3].get_int();
1964 alert.nID = params[4].get_int();
1965 if (params.size() > 5)
1966 alert.nCancel = params[5].get_int();
1967 alert.nVersion = VERSION;
1968 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1969 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1970 alert.nPriority = 1;
1973 sMsg << (CUnsignedAlert)alert;
1974 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1976 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1977 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1978 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1979 throw runtime_error(
1980 "Unable to sign alert, check private key?\n");
1981 if(!alert.ProcessAlert())
1982 throw runtime_error(
1983 "Failed to process alert.\n");
1985 CRITICAL_BLOCK(cs_vNodes)
1986 BOOST_FOREACH(CNode* pnode, vNodes)
1987 alert.RelayTo(pnode);
1990 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1991 result.push_back(Pair("nVersion", alert.nVersion));
1992 result.push_back(Pair("nMinVer", alert.nMinVer));
1993 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1994 result.push_back(Pair("nID", alert.nID));
1995 if (alert.nCancel > 0)
1996 result.push_back(Pair("nCancel", alert.nCancel));
2000 // ppcoin: send checkpoint
2001 Value sendcheckpoint(const Array& params, bool fHelp)
2003 if (fHelp || params.size() > 2 || params.size() < 1 )
2004 throw runtime_error(
2005 "sendcheckpoint <privatekey> [checkpointhash]\n"
2006 "<privatekey> is hex string of checkpoint master private key\n"
2007 "<checkpointhash> is the hash of checkpoint block\n");
2009 CSyncCheckpoint checkpoint;
2012 // TODO: omit checkpointhash parameter
2013 if (params.size() > 1)
2015 checkpoint.hashCheckpoint = uint256(params[1].get_str());
2016 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
2017 throw runtime_error(
2018 "Provided checkpoint block is not on main chain\n");
2022 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
2023 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
2024 throw runtime_error(
2025 "Unable to select a more recent sync-checkpoint");
2029 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2030 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2032 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2033 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2034 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2035 throw runtime_error(
2036 "Unable to sign checkpoint, check private key?\n");
2038 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2039 throw runtime_error(
2040 "Failed to process checkpoint.\n");
2042 CRITICAL_BLOCK(cs_vNodes)
2043 BOOST_FOREACH(CNode* pnode, vNodes)
2044 checkpoint.RelayTo(pnode);
2047 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2048 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2049 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
2058 pair<string, rpcfn_type> pCallTable[] =
2060 make_pair("help", &help),
2061 make_pair("stop", &stop),
2062 make_pair("getblockcount", &getblockcount),
2063 make_pair("getblocknumber", &getblocknumber),
2064 make_pair("getconnectioncount", &getconnectioncount),
2065 make_pair("getdifficulty", &getdifficulty),
2066 make_pair("getgenerate", &getgenerate),
2067 make_pair("setgenerate", &setgenerate),
2068 make_pair("gethashespersec", &gethashespersec),
2069 make_pair("getinfo", &getinfo),
2070 make_pair("getnewaddress", &getnewaddress),
2071 make_pair("getaccountaddress", &getaccountaddress),
2072 make_pair("setaccount", &setaccount),
2073 make_pair("getaccount", &getaccount),
2074 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2075 make_pair("sendtoaddress", &sendtoaddress),
2076 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2077 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2078 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2079 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2080 make_pair("backupwallet", &backupwallet),
2081 make_pair("keypoolrefill", &keypoolrefill),
2082 make_pair("walletpassphrase", &walletpassphrase),
2083 make_pair("walletpassphrasechange", &walletpassphrasechange),
2084 make_pair("walletlock", &walletlock),
2085 make_pair("encryptwallet", &encryptwallet),
2086 make_pair("validateaddress", &validateaddress),
2087 make_pair("getbalance", &getbalance),
2088 make_pair("move", &movecmd),
2089 make_pair("sendfrom", &sendfrom),
2090 make_pair("sendmany", &sendmany),
2091 make_pair("gettransaction", &gettransaction),
2092 make_pair("listtransactions", &listtransactions),
2093 make_pair("signmessage", &signmessage),
2094 make_pair("verifymessage", &verifymessage),
2095 make_pair("getwork", &getwork),
2096 make_pair("listaccounts", &listaccounts),
2097 make_pair("settxfee", &settxfee),
2098 make_pair("getmemorypool", &getmemorypool),
2099 make_pair("listsinceblock", &listsinceblock),
2100 make_pair("getcheckpoint", &getcheckpoint),
2101 make_pair("reservebalance", &reservebalance),
2102 make_pair("checkwallet", &checkwallet),
2103 make_pair("repairwallet", &repairwallet),
2104 make_pair("makekeypair", &makekeypair),
2105 make_pair("sendalert", &sendalert),
2106 make_pair("sendcheckpoint", &sendcheckpoint),
2108 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2110 string pAllowInSafeMode[] =
2115 "getblocknumber", // deprecated
2116 "getconnectioncount",
2123 "getaccountaddress",
2125 "getaddressesbyaccount",
2135 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2143 // This ain't Apache. We're just using HTTP header for the length field
2144 // and to be compatible with other JSON-RPC implementations.
2147 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2150 s << "POST / HTTP/1.1\r\n"
2151 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2152 << "Host: 127.0.0.1\r\n"
2153 << "Content-Type: application/json\r\n"
2154 << "Content-Length: " << strMsg.size() << "\r\n"
2155 << "Connection: close\r\n"
2156 << "Accept: application/json\r\n";
2157 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2158 s << item.first << ": " << item.second << "\r\n";
2159 s << "\r\n" << strMsg;
2164 string rfc1123Time()
2169 struct tm* now_gmt = gmtime(&now);
2170 string locale(setlocale(LC_TIME, NULL));
2171 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2172 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2173 setlocale(LC_TIME, locale.c_str());
2174 return string(buffer);
2177 static string HTTPReply(int nStatus, const string& strMsg)
2180 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2182 "Server: ppcoin-json-rpc/%s\r\n"
2183 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2184 "Content-Type: text/html\r\n"
2185 "Content-Length: 296\r\n"
2187 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2188 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2191 "<TITLE>Error</TITLE>\r\n"
2192 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2194 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2195 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2196 const char *cStatus;
2197 if (nStatus == 200) cStatus = "OK";
2198 else if (nStatus == 400) cStatus = "Bad Request";
2199 else if (nStatus == 403) cStatus = "Forbidden";
2200 else if (nStatus == 404) cStatus = "Not Found";
2201 else if (nStatus == 500) cStatus = "Internal Server Error";
2204 "HTTP/1.1 %d %s\r\n"
2206 "Connection: close\r\n"
2207 "Content-Length: %d\r\n"
2208 "Content-Type: application/json\r\n"
2209 "Server: ppcoin-json-rpc/%s\r\n"
2214 rfc1123Time().c_str(),
2216 FormatFullVersion().c_str(),
2220 int ReadHTTPStatus(std::basic_istream<char>& stream)
2223 getline(stream, str);
2224 vector<string> vWords;
2225 boost::split(vWords, str, boost::is_any_of(" "));
2226 if (vWords.size() < 2)
2228 return atoi(vWords[1].c_str());
2231 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2237 std::getline(stream, str);
2238 if (str.empty() || str == "\r")
2240 string::size_type nColon = str.find(":");
2241 if (nColon != string::npos)
2243 string strHeader = str.substr(0, nColon);
2244 boost::trim(strHeader);
2245 boost::to_lower(strHeader);
2246 string strValue = str.substr(nColon+1);
2247 boost::trim(strValue);
2248 mapHeadersRet[strHeader] = strValue;
2249 if (strHeader == "content-length")
2250 nLen = atoi(strValue.c_str());
2256 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2258 mapHeadersRet.clear();
2262 int nStatus = ReadHTTPStatus(stream);
2265 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2266 if (nLen < 0 || nLen > MAX_SIZE)
2272 vector<char> vch(nLen);
2273 stream.read(&vch[0], nLen);
2274 strMessageRet = string(vch.begin(), vch.end());
2280 bool HTTPAuthorized(map<string, string>& mapHeaders)
2282 string strAuth = mapHeaders["authorization"];
2283 if (strAuth.substr(0,6) != "Basic ")
2285 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2286 string strUserPass = DecodeBase64(strUserPass64);
2287 return strUserPass == strRPCUserColonPass;
2291 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2292 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2293 // unspecified (HTTP errors and contents of 'error').
2295 // 1.0 spec: http://json-rpc.org/wiki/specification
2296 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2297 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2300 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2303 request.push_back(Pair("method", strMethod));
2304 request.push_back(Pair("params", params));
2305 request.push_back(Pair("id", id));
2306 return write_string(Value(request), false) + "\n";
2309 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2312 if (error.type() != null_type)
2313 reply.push_back(Pair("result", Value::null));
2315 reply.push_back(Pair("result", result));
2316 reply.push_back(Pair("error", error));
2317 reply.push_back(Pair("id", id));
2318 return write_string(Value(reply), false) + "\n";
2321 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2323 // Send error reply from json-rpc error object
2325 int code = find_value(objError, "code").get_int();
2326 if (code == -32600) nStatus = 400;
2327 else if (code == -32601) nStatus = 404;
2328 string strReply = JSONRPCReply(Value::null, objError, id);
2329 stream << HTTPReply(nStatus, strReply) << std::flush;
2332 bool ClientAllowed(const string& strAddress)
2334 if (strAddress == asio::ip::address_v4::loopback().to_string())
2336 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2337 BOOST_FOREACH(string strAllow, vAllow)
2338 if (WildcardMatch(strAddress, strAllow))
2345 // IOStream device that speaks SSL but can also speak non-SSL
2347 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2349 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2351 fUseSSL = fUseSSLIn;
2352 fNeedHandshake = fUseSSLIn;
2355 void handshake(ssl::stream_base::handshake_type role)
2357 if (!fNeedHandshake) return;
2358 fNeedHandshake = false;
2359 stream.handshake(role);
2361 std::streamsize read(char* s, std::streamsize n)
2363 handshake(ssl::stream_base::server); // HTTPS servers read first
2364 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2365 return stream.next_layer().read_some(asio::buffer(s, n));
2367 std::streamsize write(const char* s, std::streamsize n)
2369 handshake(ssl::stream_base::client); // HTTPS clients write first
2370 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2371 return asio::write(stream.next_layer(), asio::buffer(s, n));
2373 bool connect(const std::string& server, const std::string& port)
2375 ip::tcp::resolver resolver(stream.get_io_service());
2376 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2377 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2378 ip::tcp::resolver::iterator end;
2379 boost::system::error_code error = asio::error::host_not_found;
2380 while (error && endpoint_iterator != end)
2382 stream.lowest_layer().close();
2383 stream.lowest_layer().connect(*endpoint_iterator++, error);
2391 bool fNeedHandshake;
2397 void ThreadRPCServer(void* parg)
2399 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2402 vnThreadsRunning[4]++;
2403 ThreadRPCServer2(parg);
2404 vnThreadsRunning[4]--;
2406 catch (std::exception& e) {
2407 vnThreadsRunning[4]--;
2408 PrintException(&e, "ThreadRPCServer()");
2410 vnThreadsRunning[4]--;
2411 PrintException(NULL, "ThreadRPCServer()");
2413 printf("ThreadRPCServer exiting\n");
2416 void ThreadRPCServer2(void* parg)
2418 printf("ThreadRPCServer started\n");
2420 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2421 if (strRPCUserColonPass == ":")
2423 string strWhatAmI = "To use ppcoind";
2424 if (mapArgs.count("-server"))
2425 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2426 else if (mapArgs.count("-daemon"))
2427 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2429 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2430 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2432 GetConfigFile().c_str());
2434 CreateThread(Shutdown, NULL);
2439 bool fUseSSL = GetBoolArg("-rpcssl");
2440 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2442 asio::io_service io_service;
2443 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2444 ip::tcp::acceptor acceptor(io_service, endpoint);
2446 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2449 ssl::context context(io_service, ssl::context::sslv23);
2452 context.set_options(ssl::context::no_sslv2);
2453 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2454 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2455 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2456 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2457 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2458 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2459 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2460 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2462 string ciphers = GetArg("-rpcsslciphers",
2463 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2464 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2468 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2473 // Accept connection
2475 SSLStream sslStream(io_service, context);
2476 SSLIOStreamDevice d(sslStream, fUseSSL);
2477 iostreams::stream<SSLIOStreamDevice> stream(d);
2479 ip::tcp::iostream stream;
2482 ip::tcp::endpoint peer;
2483 vnThreadsRunning[4]--;
2485 acceptor.accept(sslStream.lowest_layer(), peer);
2487 acceptor.accept(*stream.rdbuf(), peer);
2489 vnThreadsRunning[4]++;
2493 // Restrict callers by IP
2494 if (!ClientAllowed(peer.address().to_string()))
2496 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2498 stream << HTTPReply(403, "") << std::flush;
2502 map<string, string> mapHeaders;
2505 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2506 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2509 printf("ThreadRPCServer ReadHTTP timeout\n");
2513 // Check authorization
2514 if (mapHeaders.count("authorization") == 0)
2516 stream << HTTPReply(401, "") << std::flush;
2519 if (!HTTPAuthorized(mapHeaders))
2521 // Deter brute-forcing short passwords
2522 if (mapArgs["-rpcpassword"].size() < 15)
2525 stream << HTTPReply(401, "") << std::flush;
2526 printf("ThreadRPCServer incorrect password attempt\n");
2530 Value id = Value::null;
2535 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2536 throw JSONRPCError(-32700, "Parse error");
2537 const Object& request = valRequest.get_obj();
2539 // Parse id now so errors from here on will have the id
2540 id = find_value(request, "id");
2543 Value valMethod = find_value(request, "method");
2544 if (valMethod.type() == null_type)
2545 throw JSONRPCError(-32600, "Missing method");
2546 if (valMethod.type() != str_type)
2547 throw JSONRPCError(-32600, "Method must be a string");
2548 string strMethod = valMethod.get_str();
2549 if (strMethod != "getwork" && strMethod != "getmemorypool")
2550 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2553 Value valParams = find_value(request, "params");
2555 if (valParams.type() == array_type)
2556 params = valParams.get_array();
2557 else if (valParams.type() == null_type)
2560 throw JSONRPCError(-32600, "Params must be an array");
2563 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2564 if (mi == mapCallTable.end())
2565 throw JSONRPCError(-32601, "Method not found");
2567 // Observe safe mode
2568 string strWarning = GetWarnings("rpc");
2569 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2570 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2576 CRITICAL_BLOCK(cs_main)
2577 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2578 result = (*(*mi).second)(params, false);
2581 string strReply = JSONRPCReply(result, Value::null, id);
2582 stream << HTTPReply(200, strReply) << std::flush;
2584 catch (std::exception& e)
2586 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2589 catch (Object& objError)
2591 ErrorReply(stream, objError, id);
2593 catch (std::exception& e)
2595 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2603 Object CallRPC(const string& strMethod, const Array& params)
2605 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2606 throw runtime_error(strprintf(
2607 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2608 "If the file does not exist, create it with owner-readable-only file permissions."),
2609 GetConfigFile().c_str()));
2611 // Connect to localhost
2612 bool fUseSSL = GetBoolArg("-rpcssl");
2614 asio::io_service io_service;
2615 ssl::context context(io_service, ssl::context::sslv23);
2616 context.set_options(ssl::context::no_sslv2);
2617 SSLStream sslStream(io_service, context);
2618 SSLIOStreamDevice d(sslStream, fUseSSL);
2619 iostreams::stream<SSLIOStreamDevice> stream(d);
2620 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2621 throw runtime_error("couldn't connect to server");
2624 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2626 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2628 throw runtime_error("couldn't connect to server");
2632 // HTTP basic authentication
2633 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2634 map<string, string> mapRequestHeaders;
2635 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2638 string strRequest = JSONRPCRequest(strMethod, params, 1);
2639 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2640 stream << strPost << std::flush;
2643 map<string, string> mapHeaders;
2645 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2647 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2648 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2649 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2650 else if (strReply.empty())
2651 throw runtime_error("no response from server");
2655 if (!read_string(strReply, valReply))
2656 throw runtime_error("couldn't parse reply from server");
2657 const Object& reply = valReply.get_obj();
2659 throw runtime_error("expected reply to have result, error and id properties");
2667 template<typename T>
2668 void ConvertTo(Value& value)
2670 if (value.type() == str_type)
2672 // reinterpret string as unquoted json value
2674 if (!read_string(value.get_str(), value2))
2675 throw runtime_error("type mismatch");
2676 value = value2.get_value<T>();
2680 value = value.get_value<T>();
2684 int CommandLineRPC(int argc, char *argv[])
2691 while (argc > 1 && IsSwitchChar(argv[1][0]))
2699 throw runtime_error("too few parameters");
2700 string strMethod = argv[1];
2702 // Parameters default to strings
2704 for (int i = 2; i < argc; i++)
2705 params.push_back(argv[i]);
2706 int n = params.size();
2709 // Special case non-string parameter types
2711 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2712 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2713 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2714 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2715 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2716 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2717 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2718 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2719 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2720 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2721 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2722 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2723 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2724 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2725 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2726 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2727 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2728 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2729 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2730 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2731 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2732 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2733 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2734 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2735 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2736 if (strMethod == "sendmany" && n > 1)
2738 string s = params[1].get_str();
2740 if (!read_string(s, v) || v.type() != obj_type)
2741 throw runtime_error("type mismatch");
2742 params[1] = v.get_obj();
2744 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2745 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2746 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2749 Object reply = CallRPC(strMethod, params);
2752 const Value& result = find_value(reply, "result");
2753 const Value& error = find_value(reply, "error");
2755 if (error.type() != null_type)
2758 strPrint = "error: " + write_string(error, false);
2759 int code = find_value(error.get_obj(), "code").get_int();
2765 if (result.type() == null_type)
2767 else if (result.type() == str_type)
2768 strPrint = result.get_str();
2770 strPrint = write_string(result, true);
2773 catch (std::exception& e)
2775 strPrint = string("error: ") + e.what();
2780 PrintException(NULL, "CommandLineRPC()");
2785 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2794 int main(int argc, char *argv[])
2797 // Turn off microsoft heap dump noise
2798 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2799 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2801 setbuf(stdin, NULL);
2802 setbuf(stdout, NULL);
2803 setbuf(stderr, NULL);
2807 if (argc >= 2 && string(argv[1]) == "-server")
2809 printf("server ready\n");
2810 ThreadRPCServer(NULL);
2814 return CommandLineRPC(argc, argv);
2817 catch (std::exception& e) {
2818 PrintException(&e, "main()");
2820 PrintException(NULL, "main()");