aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-11-30 18:58:11 +0000
committergavinandresen <gavinandresen@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-11-30 18:58:11 +0000
commitbfd471f53e14c4218ae7a1544beb7f1de3e695b2 (patch)
treece973160466913efe4a091eb47da3e1f8b90f388
parent84d7c981dc52cc738053f9ec48648ab6fbf9311b (diff)
JSON methods: listtransactions, gettransaction, move, sendfrom and getbalance <account>
git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@193 1a98c847-1fd6-4fd8-948a-caf3550aa51b
-rw-r--r--db.cpp19
-rw-r--r--db.h1
-rw-r--r--main.cpp2
-rw-r--r--main.h82
-rw-r--r--rpc.cpp245
-rw-r--r--ui.cpp17
6 files changed, 268 insertions, 98 deletions
diff --git a/db.cpp b/db.cpp
index 2833a8be91..40998fb740 100644
--- a/db.cpp
+++ b/db.cpp
@@ -597,11 +597,23 @@ bool CWalletDB::WriteAccountingEntry(const string& strAccount, const CAccounting
int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
{
+ list<CAccountingEntry> entries;
+ ListAccountCreditDebit(strAccount, entries);
+
+ int64 nCreditDebit = 0;
+ foreach (const CAccountingEntry& entry, entries)
+ nCreditDebit += entry.nCreditDebit;
+
+ return nCreditDebit;
+}
+
+void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
+{
int64 nCreditDebit = 0;
Dbc* pcursor = GetCursor();
if (!pcursor)
- throw runtime_error("CWalletDB::GetAccountCreditDebit() : cannot create DB cursor");
+ throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
unsigned int fFlags = DB_SET_RANGE;
loop
{
@@ -617,7 +629,7 @@ int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
else if (ret != 0)
{
pcursor->close();
- throw runtime_error("CWalletDB::GetAccountCreditDebit() : error scanning DB");
+ throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
}
// Unserialize
@@ -632,11 +644,10 @@ int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
CAccountingEntry acentry;
ssValue >> acentry;
- nCreditDebit += acentry.nCreditDebit;
+ entries.push_back(acentry);
}
pcursor->close();
- return nCreditDebit;
}
bool CWalletDB::LoadWallet()
diff --git a/db.h b/db.h
index 0f705a863d..e3ffc40c65 100644
--- a/db.h
+++ b/db.h
@@ -435,6 +435,7 @@ public:
bool WriteAccount(const string& strAccount, const CAccount& account);
bool WriteAccountingEntry(const string& strAccount, const CAccountingEntry& acentry);
int64 GetAccountCreditDebit(const string& strAccount);
+ void ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& acentries);
bool LoadWallet();
protected:
diff --git a/main.cpp b/main.cpp
index acfcbc90fd..a1865a4674 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3492,7 +3492,7 @@ int64 GetBalance()
CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed())
continue;
- nTotal += pcoin->GetCredit(true);
+ nTotal += pcoin->GetCredit();
}
}
diff --git a/main.h b/main.h
index e5000d7579..2c24eba2d5 100644
--- a/main.h
+++ b/main.h
@@ -345,9 +345,25 @@ public:
{
if (!MoneyRange(nValue))
throw runtime_error("CTxOut::GetCredit() : value out of range");
- if (IsMine())
- return nValue;
- return 0;
+ return (IsMine() ? nValue : 0);
+ }
+
+ bool IsChange() const
+ {
+ // On a debit transaction, a txout that's mine but isn't in the address book is change
+ vector<unsigned char> vchPubKey;
+ if (ExtractPubKey(scriptPubKey, true, vchPubKey))
+ CRITICAL_BLOCK(cs_mapAddressBook)
+ if (!mapAddressBook.count(PubKeyToAddress(vchPubKey)))
+ return true;
+ return false;
+ }
+
+ int64 GetChange() const
+ {
+ if (!MoneyRange(nValue))
+ throw runtime_error("CTxOut::GetChange() : value out of range");
+ return (IsChange() ? nValue : 0);
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
@@ -520,6 +536,20 @@ public:
return nCredit;
}
+ int64 GetChange() const
+ {
+ if (IsCoinBase())
+ return 0;
+ int64 nChange = 0;
+ foreach(const CTxOut& txout, vout)
+ {
+ nChange += txout.GetChange();
+ if (!MoneyRange(nChange))
+ throw runtime_error("CTransaction::GetChange() : value out of range");
+ }
+ return nChange;
+ }
+
int64 GetValueOut() const
{
int64 nValueOut = 0;
@@ -731,8 +761,10 @@ public:
// memory only
mutable char fDebitCached;
mutable char fCreditCached;
+ mutable char fChangeCached;
mutable int64 nDebitCached;
mutable int64 nCreditCached;
+ mutable int64 nChangeCached;
// memory only UI hints
mutable unsigned int nTimeDisplayed;
@@ -768,8 +800,10 @@ public:
strFromAccount.clear();
fDebitCached = false;
fCreditCached = false;
+ fChangeCached = false;
nDebitCached = 0;
nCreditCached = 0;
+ nChangeCached = 0;
nTimeDisplayed = 0;
nLinesDisplayed = 0;
fConfirmedDisplayed = false;
@@ -808,7 +842,7 @@ public:
return nDebitCached;
}
- int64 GetCredit(bool fUseCache=false) const
+ int64 GetCredit(bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
@@ -822,11 +856,51 @@ public:
return nCreditCached;
}
+ int64 GetChange() const
+ {
+ if (fChangeCached)
+ return nChangeCached;
+ nChangeCached = CTransaction::GetChange();
+ fChangeCached = true;
+ return nChangeCached;
+ }
+
bool IsFromMe() const
{
return (GetDebit() > 0);
}
+ void GetAccountAmounts(string strAccount, const set<CScript>& setPubKey,
+ int64& nGenerated, int64& nReceived, int64& nSent, int64& nFee) const
+ {
+ nGenerated = nReceived = nSent = nFee = 0;
+
+ // Generated blocks count to account ""
+ if (IsCoinBase())
+ {
+ if (strAccount == "" && GetBlocksToMaturity() == 0)
+ nGenerated = GetCredit();
+ return;
+ }
+
+ // Received
+ foreach(const CTxOut& txout, vout)
+ if (setPubKey.count(txout.scriptPubKey))
+ nReceived += txout.nValue;
+
+ // Sent
+ if (strFromAccount == strAccount)
+ {
+ int64 nDebit = GetDebit();
+ if (nDebit > 0)
+ {
+ int64 nValueOut = GetValueOut();
+ nFee = nDebit - nValueOut;
+ nSent = nValueOut - GetChange();
+ }
+ }
+ }
+
bool IsConfirmed() const
{
// Quick answer in most cases
diff --git a/rpc.cpp b/rpc.cpp
index a7066ab3e9..0d00f4cc83 100644
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -71,6 +71,18 @@ int64 AmountFromValue(const Value& value)
return nAmount;
}
+Value ValueFromAmount(int64 amount)
+{
+ return (double)amount / (double)COIN;
+}
+
+void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
+{
+ entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
+ entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
+ foreach(const PAIRTYPE(string,string)& item, wtx.mapValue)
+ entry.push_back(Pair(item.first, item.second));
+}
@@ -435,27 +447,6 @@ Value sendtoaddress(const Array& params, bool fHelp)
}
-Value listtransactions(const Array& params, bool fHelp)
-{
- if (fHelp || params.size() > 2)
- throw runtime_error(
- "listtransactions [count=10] [includegenerated=false]\n"
- "Returns up to [count] most recent transactions.");
-
- int64 nCount = 10;
- if (params.size() > 0)
- nCount = params[0].get_int64();
- bool fGenerated = false;
- if (params.size() > 1)
- fGenerated = params[1].get_bool();
-
- Array ret;
- //// not finished
- ret.push_back("not implemented yet");
- return ret;
-}
-
-
Value getreceivedbyaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
@@ -497,21 +488,8 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
}
-Value getreceivedbyaccount(const Array& params, bool fHelp)
+void GetAccountPubKeys(string strAccount, set<CScript>& setPubKey)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
- throw runtime_error(
- "getreceivedbyaccount <account> [minconf=1]\n"
- "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
-
- // Minimum confirmations
- int nMinDepth = 1;
- if (params.size() > 1)
- nMinDepth = params[1].get_int();
-
- // Get the set of pub keys that have the label
- string strAccount = params[0].get_str();
- set<CScript> setPubKey;
CRITICAL_BLOCK(cs_mapAddressBook)
{
foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
@@ -528,6 +506,25 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
}
}
}
+}
+
+
+Value getreceivedbyaccount(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "getreceivedbyaccount <account> [minconf=1]\n"
+ "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
+
+ // Minimum confirmations
+ int nMinDepth = 1;
+ if (params.size() > 1)
+ nMinDepth = params[1].get_int();
+
+ // Get the set of pub keys that have the label
+ string strAccount = params[0].get_str();
+ set<CScript> setPubKey;
+ GetAccountPubKeys(strAccount, setPubKey);
// Tally
int64 nAmount = 0;
@@ -552,24 +549,8 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
{
- // Get the set of pub keys that have the account
set<CScript> setPubKey;
- CRITICAL_BLOCK(cs_mapAddressBook)
- {
- foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
- {
- const string& strAddress = item.first;
- const string& strName = item.second;
- if (strName == strAccount)
- {
- // We're only counting our own valid bitcoin addresses and not ip addresses
- CScript scriptPubKey;
- if (scriptPubKey.SetBitcoinAddress(strAddress))
- if (IsMine(scriptPubKey))
- setPubKey.insert(scriptPubKey);
- }
- }
- }
+ GetAccountPubKeys(strAccount, setPubKey);
int64 nBalance = 0;
CRITICAL_BLOCK(cs_mapWallet)
@@ -581,26 +562,12 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD
if (!wtx.IsFinal())
continue;
- // Count generated blocks to account ""
- if (wtx.IsCoinBase() && strAccount == "" && wtx.GetBlocksToMaturity() == 0)
- {
- nBalance += wtx.GetCredit();
- continue;
- }
+ int64 nGenerated, nReceived, nSent, nFee;
+ wtx.GetAccountAmounts(strAccount, setPubKey, nGenerated, nReceived, nSent, nFee);
- // Tally received
- foreach(const CTxOut& txout, wtx.vout)
- if (setPubKey.count(txout.scriptPubKey))
- if (wtx.GetDepthInMainChain() >= nMinDepth)
- nBalance += txout.nValue;
-
- // Tally sent
- if (wtx.strFromAccount == strAccount)
- {
- int64 nNet = wtx.GetCredit() - wtx.GetDebit();
- if (nNet < 0)
- nBalance += nNet;
- }
+ if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
+ nBalance += nReceived;
+ nBalance += nGenerated - nSent - nFee;
}
// Tally internal accounting entries
@@ -628,8 +595,6 @@ Value getbalance(const Array& params, bool fHelp)
if (params.size() == 0)
return ((double)GetBalance() / (double)COIN);
- throw runtime_error("under construction"); //// to be released soon
-
string strAccount = params[0].get_str();
int nMinDepth = 1;
if (params.size() > 1)
@@ -648,8 +613,6 @@ Value movecmd(const Array& params, bool fHelp)
"move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
"Move from one account in your wallet to another.");
- throw runtime_error("under construction");
-
string strFrom = params[0].get_str();
string strTo = params[1].get_str();
int64 nAmount = AmountFromValue(params[2]);
@@ -711,8 +674,6 @@ 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");
- throw runtime_error("under construction");
-
string strAccount = params[0].get_str();
string strAddress = params[1].get_str();
int64 nAmount = AmountFromValue(params[2]);
@@ -888,6 +849,131 @@ Value listreceivedbyaccount(const Array& params, bool fHelp)
return ListReceived(params, true);
}
+void ListAccountTransactions(CWalletDB& walletdb, const string& strAccount, int nMinDepth, multimap<int64, Object>& ret)
+{
+ set<CScript> setPubKey;
+ GetAccountPubKeys(strAccount, setPubKey);
+
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ // 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;
+
+ int64 nGenerated, nReceived, nSent, nFee;
+ wtx.GetAccountAmounts(strAccount, setPubKey, nGenerated, nReceived, nSent, nFee);
+
+ // 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));
+ }
+
+ // 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));
+ }
+ }
+
+ // 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));
+ }
+ }
+}
+
+Value listtransactions(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "listtransactions <account> [count=10]\n"
+ "Returns up to [count] most recent transactions for account <account>.");
+
+ string strAccount = params[0].get_str();
+ int nCount = 10;
+ if (params.size() > 1)
+ nCount = params[1].get_int();
+
+ 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)
+ {
+ multimap<int64, Object>::iterator end = mapByTime.begin();
+ std::advance(end, nToErase);
+ mapByTime.erase(mapByTime.begin(), end);
+ }
+
+ Array ret;
+ foreach(const PAIRTYPE(int64, Object)& item, mapByTime)
+ ret.push_back(item.second);
+ return ret;
+}
+
+Value gettransaction(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "gettransaction <txid>\n"
+ "Get detailed information about <txid>");
+
+ uint256 hash;
+ hash.SetHex(params[0].get_str());
+
+ Object entry;
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ if (!mapWallet.count(hash))
+ throw JSONRPCError(-5, "Invalid transaction id");
+ const CWalletTx& wtx = mapWallet[hash];
+
+ int64 nCredit = wtx.GetCredit();
+ int64 nDebit = wtx.GetDebit();
+ int64 nNet = nCredit - nDebit;
+ int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
+
+ entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
+ if (wtx.IsFromMe())
+ entry.push_back(Pair("fee", ValueFromAmount(nFee)));
+ WalletTxToJSON(mapWallet[hash], entry);
+ }
+
+ return entry;
+}
+
Value backupwallet(const Array& params, bool fHelp)
{
@@ -1086,6 +1172,8 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("getbalance", &getbalance),
make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom),
+ make_pair("gettransaction", &gettransaction),
+ make_pair("listtransactions", &listtransactions),
make_pair("getwork", &getwork),
};
map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
@@ -1706,8 +1794,6 @@ int CommandLineRPC(int argc, char *argv[])
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
- if (strMethod == "listtransactions" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
@@ -1725,6 +1811,7 @@ int CommandLineRPC(int argc, char *argv[])
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
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]);
// Execute
Object reply = CallRPC(strMethod, params);
diff --git a/ui.cpp b/ui.cpp
index 321214fe51..213cf7666d 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -697,16 +697,13 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
if (fAllFromMe && fAllToMe)
{
// Payment to self
- int64 nValue = wtx.vout[0].nValue;
+ int64 nChange = wtx.GetChange();
InsertLine(fNew, nIndex, hash, strSort, colour,
strStatus,
nTime ? DateTimeStr(nTime) : "",
_("Payment to yourself"),
- "",
- "");
- /// issue: can't tell which is the payment and which is the change anymore
- // FormatMoney(nNet - nValue, true),
- // FormatMoney(nValue, true));
+ FormatMoney(-(nDebit - nChange), true),
+ FormatMoney(nCredit - nChange, true));
}
else if (fAllFromMe)
{
@@ -1376,10 +1373,10 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails
if (fAllToMe)
{
// Payment to self
- /// issue: can't tell which is the payment and which is the change anymore
- //int64 nValue = wtx.vout[0].nValue;
- //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
- //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
+ int64 nChange = wtx.GetChange();
+ int64 nValue = nCredit - nChange;
+ strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
+ strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
}
int64 nTxFee = nDebit - wtx.GetValueOut();