1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include "transactiondesc.h"
7 #include "bitcoinunits.h"
13 #include "paymentserver.h"
14 #include "script/script.h"
15 #include "transactionrecord.h"
17 #include "ui_interface.h"
26 QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
28 AssertLockHeld(cs_main);
29 if (!IsFinalTx(wtx, chainActive.Height() + 1))
31 if (wtx.nLockTime < LOCKTIME_THRESHOLD)
32 return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height());
34 return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
38 int nDepth = wtx.GetDepthInMainChain();
40 return tr("conflicted");
41 else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
42 return tr("%1/offline").arg(nDepth);
44 return tr("%1/unconfirmed").arg(nDepth);
46 return tr("%1 confirmations").arg(nDepth);
50 QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit)
54 LOCK2(cs_main, wallet->cs_wallet);
55 strHTML.reserve(4000);
56 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
58 int64_t nTime = wtx.GetTxTime();
59 int64_t nCredit = wtx.GetCredit(ISMINE_ALL);
60 int64_t nDebit = wtx.GetDebit(ISMINE_ALL);
61 int64_t nNet = nCredit - nDebit;
63 strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx);
64 int nRequests = wtx.GetRequestCount();
68 strHTML += tr(", has not been successfully broadcast yet");
69 else if (nRequests > 0)
70 strHTML += tr(", broadcast through %n node(s)", "", nRequests);
74 strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
81 strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
83 else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty())
86 strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>";
90 // Offline transaction
94 if (CBitcoinAddress(rec->address).IsValid())
96 CTxDestination address = CBitcoinAddress(rec->address).Get();
97 if (wallet->mapAddressBook.count(address))
99 strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
100 strHTML += "<b>" + tr("To") + ":</b> ";
101 strHTML += GUIUtil::HtmlEscape(rec->address);
102 QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only");
103 if (!wallet->mapAddressBook[address].name.empty())
104 strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
106 strHTML += " (" + addressOwned + ")";
116 if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty())
118 // Online transaction
119 std::string strAddress = wtx.mapValue["to"];
120 strHTML += "<b>" + tr("To") + ":</b> ";
121 CTxDestination dest = CBitcoinAddress(strAddress).Get();
122 if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty())
123 strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " ";
124 strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
130 if (wtx.IsCoinBase() && nCredit == 0)
135 int64_t nUnmatured = 0;
136 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
137 nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
138 strHTML += "<b>" + tr("Credit") + ":</b> ";
139 if (wtx.IsInMainChain())
140 strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
142 strHTML += "(" + tr("not accepted") + ")";
150 strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>";
154 isminetype fAllFromMe = ISMINE_SPENDABLE;
155 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
157 isminetype mine = wallet->IsMine(txin);
158 if(fAllFromMe > mine) fAllFromMe = mine;
161 isminetype fAllToMe = ISMINE_SPENDABLE;
162 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
164 isminetype mine = wallet->IsMine(txout);
165 if(fAllToMe > mine) fAllToMe = mine;
170 if(fAllFromMe == ISMINE_WATCH_ONLY)
171 strHTML += "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
176 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
179 isminetype toSelf = wallet->IsMine(txout);
180 if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
183 if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
185 // Offline transaction
186 CTxDestination address;
187 if (ExtractDestination(txout.scriptPubKey, address))
189 strHTML += "<b>" + tr("To") + ":</b> ";
190 if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
191 strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
192 strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString());
193 if(toSelf == ISMINE_SPENDABLE)
194 strHTML += " (own address)";
195 else if(toSelf == ISMINE_WATCH_ONLY)
196 strHTML += " (watch-only)";
201 strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
203 strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
209 int64_t nChange = wtx.GetChange();
210 int64_t nValue = nCredit - nChange;
211 strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
212 strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
215 int64_t nTxFee = nDebit - wtx.GetValueOut();
217 strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
222 // Mixed debit transaction
224 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
225 if (wallet->IsMine(txin))
226 strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
227 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
228 if (wallet->IsMine(txout))
229 strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
233 strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>";
238 if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty())
239 strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "<br>";
240 if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
241 strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
243 strHTML += "<b>" + tr("Transaction ID") + ":</b> " + TransactionRecord::formatSubTxId(wtx.GetHash(), rec->idx) + "<br>";
245 // Message from normal bitcoin:URI (bitcoin:123...?message=example)
246 foreach (const PAIRTYPE(string, string)& r, wtx.vOrderForm)
247 if (r.first == "Message")
248 strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
251 // PaymentRequest info:
253 foreach (const PAIRTYPE(string, string)& r, wtx.vOrderForm)
255 if (r.first == "PaymentRequest")
257 PaymentRequestPlus req;
258 req.parse(QByteArray::fromRawData(r.second.data(), r.second.size()));
260 if (req.getMerchant(PaymentServer::getCertStore(), merchant))
261 strHTML += "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant) + "<br>";
265 if (wtx.IsCoinBase())
267 quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
268 strHTML += "<br>" + tr("Generated coins must mature %1 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, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
276 strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
277 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
278 if(wallet->IsMine(txin))
279 strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
280 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
281 if(wallet->IsMine(txout))
282 strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
284 strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
285 strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
287 strHTML += "<br><b>" + tr("Inputs") + ":</b>";
290 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
292 COutPoint prevout = txin.prevout;
295 if(pcoinsTip->GetCoins(prevout.hash, prev))
297 if (prevout.n < prev.vout.size())
300 const CTxOut &vout = prev.vout[prevout.n];
301 CTxDestination address;
302 if (ExtractDestination(vout.scriptPubKey, address))
304 if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
305 strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
306 strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
308 strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
309 strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
310 strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
318 strHTML += "</font></html>";