1 #include <transactiondesc.h>
10 // Taken straight from ui.cpp
11 // TODO: Convert to use QStrings, Qt::Escape and tr()
12 // or: refactor and put describeAsHTML() into bitcoin core but that is unneccesary with better
13 // UI<->core API, no need to put display logic in core.
17 static string HtmlEscape(const char* psz, bool fMultiLine=false)
20 for (const char* p = psz; *p; p++)
22 if (*p == '<') len += 4;
23 else if (*p == '>') len += 4;
24 else if (*p == '&') len += 5;
25 else if (*p == '"') len += 6;
26 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
27 else if (*p == '\n' && fMultiLine) len += 5;
33 for (const char* p = psz; *p; p++)
35 if (*p == '<') str += "<";
36 else if (*p == '>') str += ">";
37 else if (*p == '&') str += "&";
38 else if (*p == '"') str += """;
39 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
40 else if (*p == '\n' && fMultiLine) str += "<br>\n";
47 static string HtmlEscape(const string& str, bool fMultiLine=false)
49 return HtmlEscape(str.c_str(), fMultiLine);
52 static string FormatTxStatus(const CWalletTx& wtx)
57 if (wtx.nLockTime < 500000000)
58 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
60 return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str());
64 int nDepth = wtx.GetDepthInMainChain();
65 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
66 return strprintf(_("%d/offline?"), nDepth);
68 return strprintf(_("%d/unconfirmed"), nDepth);
70 return strprintf(_("%d confirmations"), nDepth);
74 string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
77 CRITICAL_BLOCK(wallet->cs_mapAddressBook)
79 strHTML.reserve(4000);
80 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
82 int64 nTime = wtx.GetTxTime();
83 int64 nCredit = wtx.GetCredit();
84 int64 nDebit = wtx.GetDebit();
85 int64 nNet = nCredit - nDebit;
89 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
90 int nRequests = wtx.GetRequestCount();
94 strHTML += _(", has not been successfully broadcast yet");
95 else if (nRequests == 1)
96 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
98 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
102 strHTML += _("<b>Date:</b> ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "<br>";
108 if (wtx.IsCoinBase())
110 strHTML += _("<b>Source:</b> Generated<br>");
112 else if (!wtx.mapValue["from"].empty())
114 // Online transaction
115 if (!wtx.mapValue["from"].empty())
116 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
120 // Offline transaction
124 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
126 if (wallet->IsMine(txout))
128 CBitcoinAddress address;
129 if (ExtractAddress(txout.scriptPubKey, wallet, address))
131 if (wallet->mapAddressBook.count(address))
133 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
134 strHTML += _("<b>To:</b> ");
135 strHTML += HtmlEscape(address.ToString());
136 if (!wallet->mapAddressBook[address].empty())
137 strHTML += _(" (yours, label: ") + HtmlEscape(wallet->mapAddressBook[address]) + ")";
139 strHTML += _(" (yours)");
154 if (!wtx.mapValue["to"].empty())
156 // Online transaction
157 strAddress = wtx.mapValue["to"];
158 strHTML += _("<b>To:</b> ");
159 if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty())
160 strHTML += HtmlEscape(wallet->mapAddressBook[strAddress]) + " ";
161 strHTML += HtmlEscape(strAddress) + "<br>";
168 if (wtx.IsCoinBase() && nCredit == 0)
173 int64 nUnmatured = 0;
174 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
175 nUnmatured += wallet->GetCredit(txout);
176 strHTML += _("<b>Credit:</b> ");
177 if (wtx.IsInMainChain())
178 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
180 strHTML += _("(not accepted)");
188 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
192 bool fAllFromMe = true;
193 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
194 fAllFromMe = fAllFromMe && wallet->IsMine(txin);
196 bool fAllToMe = true;
197 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
198 fAllToMe = fAllToMe && wallet->IsMine(txout);
205 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
207 if (wallet->IsMine(txout))
210 if (wtx.mapValue["to"].empty())
212 // Offline transaction
213 CBitcoinAddress address;
214 if (ExtractAddress(txout.scriptPubKey, 0, address))
216 strHTML += _("<b>To:</b> ");
217 if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
218 strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " ";
219 strHTML += HtmlEscape(address.ToString());
224 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
230 int64 nChange = wtx.GetChange();
231 int64 nValue = nCredit - nChange;
232 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
233 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
236 int64 nTxFee = nDebit - wtx.GetValueOut();
238 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
243 // Mixed debit transaction
245 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
246 if (wallet->IsMine(txin))
247 strHTML += _("<b>Debit:</b> ") + FormatMoney(-wallet->GetDebit(txin)) + "<br>";
248 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
249 if (wallet->IsMine(txout))
250 strHTML += _("<b>Credit:</b> ") + FormatMoney(wallet->GetCredit(txout)) + "<br>";
254 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
260 if (!wtx.mapValue["message"].empty())
261 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
262 if (!wtx.mapValue["comment"].empty())
263 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
265 if (wtx.IsCoinBase())
266 strHTML += string() + "<br>" + _("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.") + "<br>";
274 strHTML += "<hr><br>Debug information<br><br>";
275 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
276 if(wallet->IsMine(txin))
277 strHTML += "<b>Debit:</b> " + FormatMoney(-wallet->GetDebit(txin)) + "<br>";
278 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
279 if(wallet->IsMine(txout))
280 strHTML += "<b>Credit:</b> " + FormatMoney(wallet->GetCredit(txout)) + "<br>";
282 strHTML += "<br><b>Transaction:</b><br>";
283 strHTML += HtmlEscape(wtx.ToString(), true);
285 CTxDB txdb("r"); // To fetch source txouts
287 strHTML += "<br><b>Inputs:</b>";
289 CRITICAL_BLOCK(wallet->cs_mapWallet)
291 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
293 COutPoint prevout = txin.prevout;
296 if(txdb.ReadDiskTx(prevout.hash, prev))
298 if (prevout.n < prev.vout.size())
301 const CTxOut &vout = prev.vout[prevout.n];
302 CBitcoinAddress address;
303 if (ExtractAddress(vout.scriptPubKey, 0, address))
305 if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
306 strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " ";
307 strHTML += address.ToString();
309 strHTML = strHTML + " Amount=" + FormatMoney(vout.nValue);
310 strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? "true" : "false") + "</li>";
320 strHTML += "</font></html>";