diff --git a/db.cpp b/db.cpp index 8f02fffa..38b1d6e5 100644 --- a/db.cpp +++ b/db.cpp @@ -592,9 +592,9 @@ bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) return Write(make_pair(string("acc"), strAccount), account); } -bool CWalletDB::WriteAccountingEntry(const string& strAccount, const CAccountingEntry& acentry) +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) { - return Write(make_tuple(string("acentry"), strAccount, ++nAccountingEntryNumber), acentry); + return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); } int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) @@ -613,6 +613,8 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list> strType; if (strType != "acentry") break; - string strAccountName; - ssKey >> strAccountName; - if (strAccountName != strAccount) + CAccountingEntry acentry; + ssKey >> acentry.strAccount; + if (!fAllAccounts && acentry.strAccount != strAccount) break; - CAccountingEntry acentry; ssValue >> acentry; entries.push_back(acentry); } @@ -652,6 +653,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, listclose(); } + bool CWalletDB::LoadWallet() { vchDefaultKey.clear(); diff --git a/db.h b/db.h index 72fe0d9f..c9c40d58 100644 --- a/db.h +++ b/db.h @@ -433,7 +433,7 @@ public: bool ReadAccount(const string& strAccount, CAccount& account); bool WriteAccount(const string& strAccount, const CAccount& account); - bool WriteAccountingEntry(const string& strAccount, const CAccountingEntry& acentry); + bool WriteAccountingEntry(const CAccountingEntry& acentry); int64 GetAccountCreditDebit(const string& strAccount); void ListAccountCreditDebit(const string& strAccount, list& acentries); diff --git a/main.cpp b/main.cpp index 1d9b35b5..8db6c394 100644 --- a/main.cpp +++ b/main.cpp @@ -394,6 +394,63 @@ int CWalletTx::GetRequestCount() const return nRequests; } +void CWalletTx::GetAmounts(int64& nGenerated, list >& listReceived, + int64& nSent, int64& nFee, string& strSentAccount) const +{ + nGenerated = nSent = nFee = 0; + + if (IsCoinBase()) + { + if (GetBlocksToMaturity() == 0) + nGenerated = GetCredit(); + return; + } + + // Received. Standard client will never generate a send-to-multiple-recipients, + // but non-standard clients might (so return a list of address/amount pairs) + foreach(const CTxOut& txout, vout) + { + vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + listReceived.push_back(make_pair(PubKeyToAddress(vchPubKey), txout.nValue)); + } + + // Sent + int64 nDebit = GetDebit(); + if (nDebit > 0) + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + nSent = nValueOut - GetChange(); + strSentAccount = strFromAccount; + } +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64 allGenerated, allSent, allFee; + allGenerated = allSent = allFee = 0; + string strSentAccount; + list > listReceived; + GetAmounts(allGenerated, listReceived, allSent, allFee, strSentAccount); + + if (strAccount == "") + nGenerated = allGenerated; + if (strAccount == strSentAccount) + { + nSent = allSent; + nFee = allFee; + } + CRITICAL_BLOCK(cs_mapAddressBook) + { + foreach(const PAIRTYPE(string,int64)& r, listReceived) + if (mapAddressBook.count(r.first) && mapAddressBook[r.first] == strAccount) + nReceived += r.second; + } +} diff --git a/main.h b/main.h index 5934350f..11a5862c 100644 --- a/main.h +++ b/main.h @@ -873,36 +873,11 @@ public: return nChangeCached; } - void GetAccountAmounts(string strAccount, const set& 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; + void GetAmounts(int64& nGenerated, list >& listReceived, + int64& nSent, int64& nFee, string& strSentAccount) const; - // Sent - if (strFromAccount == strAccount) - { - int64 nDebit = GetDebit(); - if (nDebit > 0) - { - int64 nValueOut = GetValueOut(); - nFee = nDebit - nValueOut; - nSent = nValueOut - GetChange(); - } - } - } + void GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const; bool IsFromMe() const { @@ -1695,6 +1670,7 @@ public: class CAccountingEntry { public: + string strAccount; int64 nCreditDebit; int64 nTime; string strOtherAccount; @@ -1709,6 +1685,7 @@ public: { nCreditDebit = 0; nTime = 0; + strAccount.clear(); strOtherAccount.clear(); strComment.clear(); } @@ -1717,6 +1694,7 @@ public: ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. READWRITE(nCreditDebit); READWRITE(nTime); READWRITE(strOtherAccount); diff --git a/rpc.cpp b/rpc.cpp index b9ed61de..2cb73aad 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 \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 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 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 [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 [minconf=1] [comment] [comment-to]\n" " 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& ret) +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, Array& ret) { - set setPubKey; - GetAccountPubKeys(strAccount, setPubKey); + int64 nGenerated, nSent, nFee; + string strSentAccount; + list > 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::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 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 [count=10]\n" + "listtransactions [account] [count=10]\n" "Returns up to [count] most recent transactions for 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 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::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 TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, 0))); + } + list 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 mapAccountBalances; + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + foreach(const PAIRTYPE(string, string)& entry, mapAddressBook) + mapAccountBalances[entry.second] = 0; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGenerated, nSent, nFee; + string strSentAccount; + list > 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 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 pCallTable[] = make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), }; map 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" "\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 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(params[2]); if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listaccounts" && n > 1) ConvertTo(params[1]); // Execute Object reply = CallRPC(strMethod, params); diff --git a/serialize.h b/serialize.h index 34a339f7..8153382e 100644 --- a/serialize.h +++ b/serialize.h @@ -25,7 +25,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 31901; +static const int VERSION = 31902; static const char* pszSubVer = ""; diff --git a/ui.cpp b/ui.cpp index 677e6c2a..1cb922fa 100644 --- a/ui.cpp +++ b/ui.cpp @@ -746,6 +746,12 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) strDescription += " - "; strDescription += mapValue["message"]; } + else if (!mapValue["comment"].empty()) + { + if (!strDescription.empty()) + strDescription += " - "; + strDescription += mapValue["comment"]; + } int64 nValue = txout.nValue; if (nTxFee > 0) @@ -1405,6 +1411,8 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails // if (!wtx.mapValue["message"].empty()) strHTML += string() + "
" + _("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; + if (!wtx.mapValue["comment"].empty()) + strHTML += string() + "
" + _("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; if (wtx.IsCoinBase()) strHTML += string() + "
" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
";