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