diff options
author | gavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b> | 2010-12-16 01:06:03 +0000 |
---|---|---|
committer | gavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b> | 2010-12-16 01:06:03 +0000 |
commit | 809ee795927f0b9110a5b6e83845f42e3394451d (patch) | |
tree | a906fe04ac63e46d82793d893b8aa69960de6df7 /rpc.cpp | |
parent | 629e37dde1fa93f6ce31544d1ebb5ee5c19052cb (diff) | |
download | bitcoin-809ee795927f0b9110a5b6e83845f42e3394451d.tar.xz |
New RPC command: listaccounts. New RPC setting -rpctimeout. And listtransactions '*'
git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@203 1a98c847-1fd6-4fd8-948a-caf3550aa51b
Diffstat (limited to 'rpc.cpp')
-rw-r--r-- | rpc.cpp | 297 |
1 files changed, 197 insertions, 100 deletions
@@ -84,7 +84,13 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } - +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(-11, "Invalid account name"); + return strAccount; +} @@ -296,7 +302,7 @@ Value getnewaddress(const Array& params, bool fHelp) // Parse the account first so we don't generate a key if there's an error string strAccount; if (params.size() > 0) - strAccount = params[0].get_str(); + strAccount = AccountFromValue(params[0]); // Generate a new key that is added to wallet string strAddress = PubKeyToAddress(GetKeyFromKeyPool()); @@ -314,7 +320,7 @@ Value getaccountaddress(const Array& params, bool fHelp) "Returns the current bitcoin address for receiving payments to this account."); // Parse the account first so we don't generate a key if there's an error - string strAccount = params[0].get_str(); + string strAccount = AccountFromValue(params[0]); CRITICAL_BLOCK(cs_mapWallet) { @@ -365,7 +371,7 @@ Value setaccount(const Array& params, bool fHelp) string strAddress = params[0].get_str(); string strAccount; if (params.size() > 1) - strAccount = params[1].get_str(); + strAccount = AccountFromValue(params[1]); SetAddressBookName(strAddress, strAccount); return Value::null; @@ -399,7 +405,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) "getaddressesbyaccount <account>\n" "Returns the list of addresses for the given account."); - string strAccount = params[0].get_str(); + string strAccount = AccountFromValue(params[0]); // Find all addresses that have the given account Array ret; @@ -436,7 +442,7 @@ Value sendtoaddress(const Array& params, bool fHelp) // Wallet comments CWalletTx wtx; if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["message"] = params[2].get_str(); + wtx.mapValue["comment"] = params[2].get_str(); if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); @@ -522,7 +528,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) nMinDepth = params[1].get_int(); // Get the set of pub keys that have the label - string strAccount = params[0].get_str(); + string strAccount = AccountFromValue(params[0]); set<CScript> setPubKey; GetAccountPubKeys(strAccount, setPubKey); @@ -549,9 +555,6 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) { - set<CScript> setPubKey; - GetAccountPubKeys(strAccount, setPubKey); - int64 nBalance = 0; CRITICAL_BLOCK(cs_mapWallet) { @@ -563,7 +566,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD continue; int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, setPubKey, nGenerated, nReceived, nSent, nFee); + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) nBalance += nReceived; @@ -595,7 +598,7 @@ Value getbalance(const Array& params, bool fHelp) if (params.size() == 0) return ((double)GetBalance() / (double)COIN); - string strAccount = params[0].get_str(); + string strAccount = AccountFromValue(params[0]); int nMinDepth = 1; if (params.size() > 1) nMinDepth = params[1].get_int(); @@ -613,8 +616,8 @@ Value movecmd(const Array& params, bool fHelp) "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n" "Move from one account in your wallet to another."); - string strFrom = params[0].get_str(); - string strTo = params[1].get_str(); + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); int64 nAmount = AmountFromValue(params[2]); int nMinDepth = 1; if (params.size() > 3) @@ -647,19 +650,21 @@ Value movecmd(const Array& params, bool fHelp) // Debit CAccountingEntry debit; + debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; debit.strOtherAccount = strTo; debit.strComment = strComment; - walletdb.WriteAccountingEntry(strFrom, debit); + walletdb.WriteAccountingEntry(debit); // Credit CAccountingEntry credit; + credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; credit.strOtherAccount = strFrom; credit.strComment = strComment; - walletdb.WriteAccountingEntry(strTo, credit); + walletdb.WriteAccountingEntry(credit); walletdb.TxnCommit(); } @@ -674,7 +679,7 @@ Value sendfrom(const Array& params, bool fHelp) "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n" "<amount> is a real and is rounded to the nearest 0.01"); - string strAccount = params[0].get_str(); + string strAccount = AccountFromValue(params[0]); string strAddress = params[1].get_str(); int64 nAmount = AmountFromValue(params[2]); int nMinDepth = 1; @@ -684,7 +689,7 @@ Value sendfrom(const Array& params, bool fHelp) CWalletTx wtx; wtx.strFromAccount = strAccount; if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["message"] = params[4].get_str(); + wtx.mapValue["comment"] = params[4].get_str(); if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); @@ -849,97 +854,180 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) return ListReceived(params, true); } -void ListAccountTransactions(CWalletDB& walletdb, const string& strAccount, int nMinDepth, multimap<int64, Object>& ret) +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, Array& ret) { - set<CScript> setPubKey; - GetAccountPubKeys(strAccount, setPubKey); + int64 nGenerated, nSent, nFee; + string strSentAccount; + list<pair<string, int64> > listReceived; + wtx.GetAmounts(nGenerated, listReceived, nSent, nFee, strSentAccount); - CRITICAL_BLOCK(cs_mapWallet) + bool fAllAccounts = (strAccount == string("*")); + + // Generated blocks assigned to account "" + if (nGenerated != 0 && (fAllAccounts || strAccount == "")) { - // Wallet: generate/send/receive transactions - for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; + Object entry; + entry.push_back(Pair("account", string(""))); + entry.push_back(Pair("category", "generate")); + entry.push_back(Pair("amount", ValueFromAmount(nGenerated))); + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, setPubKey, nGenerated, nReceived, nSent, nFee); + // Sent + if ((nSent != 0 || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-nSent))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } - // Generated blocks count to account "" - if (nGenerated != 0) - { - Object entry; - entry.push_back(Pair("category", "generate")); - entry.push_back(Pair("amount", ValueFromAmount(nGenerated))); - WalletTxToJSON(wtx, entry); - ret.insert(make_pair(wtx.GetTxTime(), entry)); - } + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + CRITICAL_BLOCK(cs_mapAddressBook) + { + foreach(const PAIRTYPE(string, int64)& r, listReceived) + if (mapAddressBook.count(r.first) && (fAllAccounts || r.first == strAccount)) + { + Object entry; + entry.push_back(Pair("account", r.first)); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } - // Sent - if (nSent != 0 || nFee != 0) - { - Object entry; - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-nSent))); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); - WalletTxToJSON(wtx, entry); - ret.insert(make_pair(wtx.GetTxTime(), entry)); - } +} - // Received - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - { - Object entry; - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(nReceived))); - WalletTxToJSON(wtx, entry); - ret.insert(make_pair(wtx.GetTxTime(), entry)); - } - } +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); - // Internal accounting entries - list<CAccountingEntry> acentries; - walletdb.ListAccountCreditDebit(strAccount, acentries); - foreach (const CAccountingEntry& acentry, acentries) - { - Object entry; - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - ret.insert(make_pair(acentry.nTime, entry)); - } + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); } } Value listtransactions(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (fHelp || params.size() > 2) throw runtime_error( - "listtransactions <account> [count=10]\n" + "listtransactions [account] [count=10]\n" "Returns up to [count] most recent transactions for account <account>."); - string strAccount = params[0].get_str(); + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); int nCount = 10; if (params.size() > 1) nCount = params[1].get_int(); + Array ret; CWalletDB walletdb; - multimap<int64, Object> mapByTime; // keys are transaction time - ListAccountTransactions(walletdb, strAccount, 0, mapByTime); - // Return only last nCount items: - int nToErase = mapByTime.size()-nCount; - if (nToErase > 0) + CRITICAL_BLOCK(cs_mapWallet) { - multimap<int64, Object>::iterator end = mapByTime.begin(); - std::advance(end, nToErase); - mapByTime.erase(mapByTime.begin(), end); + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair<CWalletTx*, CAccountingEntry*> TxPair; + typedef multimap<int64, TxPair > TxItems; + TxItems txByTime; + + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, 0))); + } + list<CAccountingEntry> acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + foreach(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair(0, &entry))); + } + + // Now: iterate backwards until we have nCount items to return: + for (TxItems::reverse_iterator it = txByTime.rbegin(); it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; + } + // ret is now newest to oldest + } + + // Make sure we return only last nCount items (sends-to-self might give us an extra): + if (ret.size() > nCount) + { + Array::iterator last = ret.begin(); + std::advance(last, nCount); + ret.erase(last, ret.end()); } + std::reverse(ret.begin(), ret.end()); // oldest to newest - Array ret; - foreach(const PAIRTYPE(int64, Object)& item, mapByTime) - ret.push_back(item.second); + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + map<string, int64> mapAccountBalances; + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + foreach(const PAIRTYPE(string, string)& entry, mapAddressBook) + mapAccountBalances[entry.second] = 0; + + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGenerated, nSent, nFee; + string strSentAccount; + list<pair<string, int64> > listReceived; + wtx.GetAmounts(nGenerated, listReceived, nSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nSent+nFee; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGenerated; + foreach(const PAIRTYPE(string, int64)& r, listReceived) + if (mapAddressBook.count(r.first)) + mapAccountBalances[mapAddressBook[r.first]] += r.second; + } + } + } + + list<CAccountingEntry> acentries; + CWalletDB().ListAccountCreditDebit("*", acentries); + foreach(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + foreach(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } return ret; } @@ -1175,6 +1263,7 @@ pair<string, rpcfn_type> pCallTable[] = make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), }; map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); @@ -1247,7 +1336,7 @@ string HTTPReply(int nStatus, const string& strMsg) "Server: bitcoin-json-rpc\r\n" "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" "Content-Type: text/html\r\n" - "Content-Length: 311\r\n" + "Content-Length: 296\r\n" "\r\n" "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n" @@ -1421,6 +1510,17 @@ string JSONRPCReply(const Value& result, const Value& error, const Value& id) return write_string(Value(reply), false) + "\n"; } +void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +{ + // Send error reply from json-rpc error object + int nStatus = 500; + int code = find_value(objError, "code").get_int(); + if (code == -32600) nStatus = 400; + else if (code == -32601) nStatus = 404; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply) << std::flush; +} + bool ClientAllowed(const string& strAddress) { if (strAddress == asio::ip::address_v4::loopback().to_string()) @@ -1581,10 +1681,16 @@ void ThreadRPCServer2(void* parg) if (!ClientAllowed(peer.address().to_string())) continue; - // Receive request map<string, string> mapHeaders; string strRequest; - ReadHTTP(stream, mapHeaders, strRequest); + + boost::thread api_caller(ReadHTTP, ref(stream), ref(mapHeaders), ref(strRequest)); + if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) + { // Timed out: + acceptor.cancel(); + printf("ThreadRPCServer ReadHTTP timeout\n"); + continue; + } // Check authorization if (mapHeaders.count("Authorization") == 0) @@ -1656,26 +1762,16 @@ void ThreadRPCServer2(void* parg) } catch (std::exception& e) { - // Send error reply from method - string strReply = JSONRPCReply(Value::null, JSONRPCError(-1, e.what()), id); - stream << HTTPReply(500, strReply) << std::flush; + ErrorReply(stream, JSONRPCError(-1, e.what()), id); } } catch (Object& objError) { - // Send error reply from json-rpc error object - int nStatus = 500; - int code = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; + ErrorReply(stream, objError, id); } catch (std::exception& e) { - // Send error reply from other json-rpc parsing errors - string strReply = JSONRPCReply(Value::null, JSONRPCError(-32700, e.what()), id); - stream << HTTPReply(500, strReply) << std::flush; + ErrorReply(stream, JSONRPCError(-32700, e.what()), id); } } } @@ -1812,6 +1908,7 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]); if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]); if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "listaccounts" && n > 1) ConvertTo<boost::int64_t>(params[1]); // Execute Object reply = CallRPC(strMethod, params); |