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 vector<unsigned char> vchPubKey;
129 if (ExtractPubKey(txout.scriptPubKey, wallet, vchPubKey))
131 string strAddress = PubKeyToAddress(vchPubKey);
132 if (wallet->mapAddressBook.count(strAddress))
134 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
135 strHTML += _("<b>To:</b> ");
136 strHTML += HtmlEscape(strAddress);
137 if (!wallet->mapAddressBook[strAddress].empty())
138 strHTML += _(" (yours, label: ") + wallet->mapAddressBook[strAddress] + ")";
140 strHTML += _(" (yours)");
155 if (!wtx.mapValue["to"].empty())
157 // Online transaction
158 strAddress = wtx.mapValue["to"];
159 strHTML += _("<b>To:</b> ");
160 if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty())
161 strHTML += wallet->mapAddressBook[strAddress] + " ";
162 strHTML += HtmlEscape(strAddress) + "<br>";
169 if (wtx.IsCoinBase() && nCredit == 0)
174 int64 nUnmatured = 0;
175 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
176 nUnmatured += wallet->GetCredit(txout);
177 strHTML += _("<b>Credit:</b> ");
178 if (wtx.IsInMainChain())
179 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
181 strHTML += _("(not accepted)");
189 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
193 bool fAllFromMe = true;
194 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
195 fAllFromMe = fAllFromMe && wallet->IsMine(txin);
197 bool fAllToMe = true;
198 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
199 fAllToMe = fAllToMe && wallet->IsMine(txout);
206 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
208 if (wallet->IsMine(txout))
211 if (wtx.mapValue["to"].empty())
213 // Offline transaction
215 if (ExtractHash160(txout.scriptPubKey, hash160))
217 string strAddress = Hash160ToAddress(hash160);
218 strHTML += _("<b>To:</b> ");
219 if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty())
220 strHTML += wallet->mapAddressBook[strAddress] + " ";
221 strHTML += strAddress;
226 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
232 int64 nChange = wtx.GetChange();
233 int64 nValue = nCredit - nChange;
234 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
235 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
238 int64 nTxFee = nDebit - wtx.GetValueOut();
240 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
245 // Mixed debit transaction
247 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
248 if (wallet->IsMine(txin))
249 strHTML += _("<b>Debit:</b> ") + FormatMoney(-wallet->GetDebit(txin)) + "<br>";
250 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
251 if (wallet->IsMine(txout))
252 strHTML += _("<b>Credit:</b> ") + FormatMoney(wallet->GetCredit(txout)) + "<br>";
256 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
262 if (!wtx.mapValue["message"].empty())
263 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
264 if (!wtx.mapValue["comment"].empty())
265 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
267 if (wtx.IsCoinBase())
268 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>";
276 strHTML += "<hr><br>debug print<br><br>";
277 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
278 if(wallet->IsMine(txin))
279 strHTML += "<b>Debit:</b> " + FormatMoney(-wallet->IsMine(txin)) + "<br>";
280 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
281 if(wallet->IsMine(txout))
282 strHTML += "<b>Credit:</b> " + FormatMoney(wallet->IsMine(txout)) + "<br>";
284 strHTML += "<br><b>Transaction:</b><br>";
285 strHTML += HtmlEscape(wtx.ToString(), true);
287 strHTML += "<br><b>Inputs:</b><br>";
288 CRITICAL_BLOCK(wallet->cs_mapWallet)
290 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
292 COutPoint prevout = txin.prevout;
293 map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(prevout.hash);
294 if (mi != wallet->mapWallet.end())
296 const CWalletTx& prev = (*mi).second;
297 if (prevout.n < prev.vout.size())
299 strHTML += HtmlEscape(prev.ToString(), true);
300 strHTML += " " + FormatTxStatus(prev) + ", ";
301 strHTML = strHTML + "IsMine=" + (wallet->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
310 strHTML += "</font></html>";