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;
+}
// 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());
"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)
{
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;
"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;
// 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();
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);
int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
{
- set<CScript> setPubKey;
- GetAccountPubKeys(strAccount, setPubKey);
-
int64 nBalance = 0;
CRITICAL_BLOCK(cs_mapWallet)
{
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;
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();
"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)
// 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();
}
"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;
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();
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;
}
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]));
"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"
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())
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)
}
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);
}
}
}
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);