aboutsummaryrefslogtreecommitdiff
path: root/rpc.cpp
diff options
context:
space:
mode:
authorgavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-12-16 01:06:03 +0000
committergavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-12-16 01:06:03 +0000
commit809ee795927f0b9110a5b6e83845f42e3394451d (patch)
treea906fe04ac63e46d82793d893b8aa69960de6df7 /rpc.cpp
parent629e37dde1fa93f6ce31544d1ebb5ee5c19052cb (diff)
downloadbitcoin-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.cpp297
1 files changed, 197 insertions, 100 deletions
diff --git a/rpc.cpp b/rpc.cpp
index b9ed61de73..2cb73aad7a 100644
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -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);