Somewhat confident now, tested on GNOME+KDE, with all types of transactions. Next...
[novacoin.git] / gui / src / transactiondesc.cpp
1 #include <transactiondesc.h>
2
3 #include "guiutil.h"
4 #include "main.h"
5
6 #include <QString>
7
8 /* Taken straight from ui.cpp
9    TODO: Convert to use QStrings, Qt::Escape and tr()
10  */
11
12 using namespace std;
13
14 static string HtmlEscape(const char* psz, bool fMultiLine=false)
15 {
16     int len = 0;
17     for (const char* p = psz; *p; p++)
18     {
19              if (*p == '<') len += 4;
20         else if (*p == '>') len += 4;
21         else if (*p == '&') len += 5;
22         else if (*p == '"') len += 6;
23         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
24         else if (*p == '\n' && fMultiLine) len += 5;
25         else
26             len++;
27     }
28     string str;
29     str.reserve(len);
30     for (const char* p = psz; *p; p++)
31     {
32              if (*p == '<') str += "&lt;";
33         else if (*p == '>') str += "&gt;";
34         else if (*p == '&') str += "&amp;";
35         else if (*p == '"') str += "&quot;";
36         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
37         else if (*p == '\n' && fMultiLine) str += "<br>\n";
38         else
39             str += *p;
40     }
41     return str;
42 }
43
44 static string HtmlEscape(const string& str, bool fMultiLine=false)
45 {
46     return HtmlEscape(str.c_str(), fMultiLine);
47 }
48
49 static string FormatTxStatus(const CWalletTx& wtx)
50 {
51     // Status
52     if (!wtx.IsFinal())
53     {
54         if (wtx.nLockTime < 500000000)
55             return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
56         else
57             return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str());
58     }
59     else
60     {
61         int nDepth = wtx.GetDepthInMainChain();
62         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
63             return strprintf(_("%d/offline?"), nDepth);
64         else if (nDepth < 6)
65             return strprintf(_("%d/unconfirmed"), nDepth);
66         else
67             return strprintf(_("%d confirmations"), nDepth);
68     }
69 }
70
71 string TransactionDesc::toHTML(CWalletTx &wtx)
72 {
73     string strHTML;
74     CRITICAL_BLOCK(cs_mapAddressBook)
75     {
76         strHTML.reserve(4000);
77         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
78
79         int64 nTime = wtx.GetTxTime();
80         int64 nCredit = wtx.GetCredit();
81         int64 nDebit = wtx.GetDebit();
82         int64 nNet = nCredit - nDebit;
83
84
85
86         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
87         int nRequests = wtx.GetRequestCount();
88         if (nRequests != -1)
89         {
90             if (nRequests == 0)
91                 strHTML += _(", has not been successfully broadcast yet");
92             else if (nRequests == 1)
93                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
94             else
95                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
96         }
97         strHTML += "<br>";
98
99         strHTML += _("<b>Date:</b> ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "<br>";
100
101
102         //
103         // From
104         //
105         if (wtx.IsCoinBase())
106         {
107             strHTML += _("<b>Source:</b> Generated<br>");
108         }
109         else if (!wtx.mapValue["from"].empty())
110         {
111             // Online transaction
112             if (!wtx.mapValue["from"].empty())
113                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
114         }
115         else
116         {
117             // Offline transaction
118             if (nNet > 0)
119             {
120                 // Credit
121                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
122                 {
123                     if (txout.IsMine())
124                     {
125                         vector<unsigned char> vchPubKey;
126                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
127                         {
128                             string strAddress = PubKeyToAddress(vchPubKey);
129                             if (mapAddressBook.count(strAddress))
130                             {
131                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
132                                 strHTML += _("<b>To:</b> ");
133                                 strHTML += HtmlEscape(strAddress);
134                                 if (!mapAddressBook[strAddress].empty())
135                                     strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
136                                 else
137                                     strHTML += _(" (yours)");
138                                 strHTML += "<br>";
139                             }
140                         }
141                         break;
142                     }
143                 }
144             }
145         }
146
147
148         //
149         // To
150         //
151         string strAddress;
152         if (!wtx.mapValue["to"].empty())
153         {
154             // Online transaction
155             strAddress = wtx.mapValue["to"];
156             strHTML += _("<b>To:</b> ");
157             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
158                 strHTML += mapAddressBook[strAddress] + " ";
159             strHTML += HtmlEscape(strAddress) + "<br>";
160         }
161
162
163         //
164         // Amount
165         //
166         if (wtx.IsCoinBase() && nCredit == 0)
167         {
168             //
169             // Coinbase
170             //
171             int64 nUnmatured = 0;
172             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
173                 nUnmatured += txout.GetCredit();
174             strHTML += _("<b>Credit:</b> ");
175             if (wtx.IsInMainChain())
176                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
177             else
178                 strHTML += _("(not accepted)");
179             strHTML += "<br>";
180         }
181         else if (nNet > 0)
182         {
183             //
184             // Credit
185             //
186             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
187         }
188         else
189         {
190             bool fAllFromMe = true;
191             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
192                 fAllFromMe = fAllFromMe && txin.IsMine();
193
194             bool fAllToMe = true;
195             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
196                 fAllToMe = fAllToMe && txout.IsMine();
197
198             if (fAllFromMe)
199             {
200                 //
201                 // Debit
202                 //
203                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
204                 {
205                     if (txout.IsMine())
206                         continue;
207
208                     if (wtx.mapValue["to"].empty())
209                     {
210                         // Offline transaction
211                         uint160 hash160;
212                         if (ExtractHash160(txout.scriptPubKey, hash160))
213                         {
214                             string strAddress = Hash160ToAddress(hash160);
215                             strHTML += _("<b>To:</b> ");
216                             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
217                                 strHTML += mapAddressBook[strAddress] + " ";
218                             strHTML += strAddress;
219                             strHTML += "<br>";
220                         }
221                     }
222
223                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
224                 }
225
226                 if (fAllToMe)
227                 {
228                     // Payment to self
229                     int64 nChange = wtx.GetChange();
230                     int64 nValue = nCredit - nChange;
231                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
232                     strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
233                 }
234
235                 int64 nTxFee = nDebit - wtx.GetValueOut();
236                 if (nTxFee > 0)
237                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
238             }
239             else
240             {
241                 //
242                 // Mixed debit transaction
243                 //
244                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
245                     if (txin.IsMine())
246                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
247                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
248                     if (txout.IsMine())
249                         strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
250             }
251         }
252
253         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
254
255
256         //
257         // Message
258         //
259         if (!wtx.mapValue["message"].empty())
260             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
261         if (!wtx.mapValue["comment"].empty())
262             strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
263
264         if (wtx.IsCoinBase())
265             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>";
266
267
268         //
269         // Debug view
270         //
271         if (fDebug)
272         {
273             strHTML += "<hr><br>debug print<br><br>";
274             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
275                 if (txin.IsMine())
276                     strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
277             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
278                 if (txout.IsMine())
279                     strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
280
281             strHTML += "<br><b>Transaction:</b><br>";
282             strHTML += HtmlEscape(wtx.ToString(), true);
283
284             strHTML += "<br><b>Inputs:</b><br>";
285             CRITICAL_BLOCK(cs_mapWallet)
286             {
287                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
288                 {
289                     COutPoint prevout = txin.prevout;
290                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
291                     if (mi != mapWallet.end())
292                     {
293                         const CWalletTx& prev = (*mi).second;
294                         if (prevout.n < prev.vout.size())
295                         {
296                             strHTML += HtmlEscape(prev.ToString(), true);
297                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
298                             strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
299                         }
300                     }
301                 }
302             }
303         }
304
305
306
307         strHTML += "</font></html>";
308     }
309     return strHTML;
310 }