1 // Copyright (c) 2011-2014 The Bitcoin Core developers
2 // Distributed under the MIT 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"
9 #include "paymentserver.h"
10 #include "transactionrecord.h"
13 #include "consensus/consensus.h"
15 #include "script/script.h"
18 #include "wallet/db.h"
19 #include "wallet/wallet.h"
24 QString
TransactionDesc::FormatTxStatus(const CWalletTx
& wtx
)
26 AssertLockHeld(cs_main
);
27 if (!CheckFinalTx(wtx
))
29 if (wtx
.nLockTime
< LOCKTIME_THRESHOLD
)
30 return tr("Open for %n more block(s)", "", wtx
.nLockTime
- chainActive
.Height());
32 return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx
.nLockTime
));
36 int nDepth
= wtx
.GetDepthInMainChain();
38 return tr("conflicted");
39 else if (GetAdjustedTime() - wtx
.nTimeReceived
> 2 * 60 && wtx
.GetRequestCount() == 0)
40 return tr("%1/offline").arg(nDepth
);
42 return tr("%1/unconfirmed").arg(nDepth
);
44 return tr("%1 confirmations").arg(nDepth
);
48 QString
TransactionDesc::toHTML(CWallet
*wallet
, CWalletTx
&wtx
, TransactionRecord
*rec
, int unit
)
52 LOCK2(cs_main
, wallet
->cs_wallet
);
53 strHTML
.reserve(4000);
54 strHTML
+= "<html><font face='verdana, arial, helvetica, sans-serif'>";
56 int64_t nTime
= wtx
.GetTxTime();
57 CAmount nCredit
= wtx
.GetCredit(ISMINE_ALL
);
58 CAmount nDebit
= wtx
.GetDebit(ISMINE_ALL
);
59 CAmount nNet
= nCredit
- nDebit
;
61 strHTML
+= "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx
);
62 int nRequests
= wtx
.GetRequestCount();
66 strHTML
+= tr(", has not been successfully broadcast yet");
67 else if (nRequests
> 0)
68 strHTML
+= tr(", broadcast through %n node(s)", "", nRequests
);
72 strHTML
+= "<b>" + tr("Date") + ":</b> " + (nTime
? GUIUtil::dateTimeStr(nTime
) : "") + "<br>";
79 strHTML
+= "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
81 else if (wtx
.mapValue
.count("from") && !wtx
.mapValue
["from"].empty())
84 strHTML
+= "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx
.mapValue
["from"]) + "<br>";
88 // Offline transaction
92 if (CBitcoinAddress(rec
->address
).IsValid())
94 CTxDestination address
= CBitcoinAddress(rec
->address
).Get();
95 if (wallet
->mapAddressBook
.count(address
))
97 strHTML
+= "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
98 strHTML
+= "<b>" + tr("To") + ":</b> ";
99 strHTML
+= GUIUtil::HtmlEscape(rec
->address
);
100 QString addressOwned
= (::IsMine(*wallet
, address
) == ISMINE_SPENDABLE
) ? tr("own address") : tr("watch-only");
101 if (!wallet
->mapAddressBook
[address
].name
.empty())
102 strHTML
+= " (" + addressOwned
+ ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + ")";
104 strHTML
+= " (" + addressOwned
+ ")";
114 if (wtx
.mapValue
.count("to") && !wtx
.mapValue
["to"].empty())
116 // Online transaction
117 std::string strAddress
= wtx
.mapValue
["to"];
118 strHTML
+= "<b>" + tr("To") + ":</b> ";
119 CTxDestination dest
= CBitcoinAddress(strAddress
).Get();
120 if (wallet
->mapAddressBook
.count(dest
) && !wallet
->mapAddressBook
[dest
].name
.empty())
121 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[dest
].name
) + " ";
122 strHTML
+= GUIUtil::HtmlEscape(strAddress
) + "<br>";
128 if (wtx
.IsCoinBase() && nCredit
== 0)
133 CAmount nUnmatured
= 0;
134 BOOST_FOREACH(const CTxOut
& txout
, wtx
.vout
)
135 nUnmatured
+= wallet
->GetCredit(txout
, ISMINE_ALL
);
136 strHTML
+= "<b>" + tr("Credit") + ":</b> ";
137 if (wtx
.IsInMainChain())
138 strHTML
+= BitcoinUnits::formatHtmlWithUnit(unit
, nUnmatured
)+ " (" + tr("matures in %n more block(s)", "", wtx
.GetBlocksToMaturity()) + ")";
140 strHTML
+= "(" + tr("not accepted") + ")";
148 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nNet
) + "<br>";
152 isminetype fAllFromMe
= ISMINE_SPENDABLE
;
153 BOOST_FOREACH(const CTxIn
& txin
, wtx
.vin
)
155 isminetype mine
= wallet
->IsMine(txin
);
156 if(fAllFromMe
> mine
) fAllFromMe
= mine
;
159 isminetype fAllToMe
= ISMINE_SPENDABLE
;
160 BOOST_FOREACH(const CTxOut
& txout
, wtx
.vout
)
162 isminetype mine
= wallet
->IsMine(txout
);
163 if(fAllToMe
> mine
) fAllToMe
= mine
;
168 if(fAllFromMe
& ISMINE_WATCH_ONLY
)
169 strHTML
+= "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
174 BOOST_FOREACH(const CTxOut
& txout
, wtx
.vout
)
177 isminetype toSelf
= wallet
->IsMine(txout
);
178 if ((toSelf
== ISMINE_SPENDABLE
) && (fAllFromMe
== ISMINE_SPENDABLE
))
181 if (!wtx
.mapValue
.count("to") || wtx
.mapValue
["to"].empty())
183 // Offline transaction
184 CTxDestination address
;
185 if (ExtractDestination(txout
.scriptPubKey
, address
))
187 strHTML
+= "<b>" + tr("To") + ":</b> ";
188 if (wallet
->mapAddressBook
.count(address
) && !wallet
->mapAddressBook
[address
].name
.empty())
189 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + " ";
190 strHTML
+= GUIUtil::HtmlEscape(CBitcoinAddress(address
).ToString());
191 if(toSelf
== ISMINE_SPENDABLE
)
192 strHTML
+= " (own address)";
193 else if(toSelf
& ISMINE_WATCH_ONLY
)
194 strHTML
+= " (watch-only)";
199 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -txout
.nValue
) + "<br>";
201 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, txout
.nValue
) + "<br>";
207 CAmount nChange
= wtx
.GetChange();
208 CAmount nValue
= nCredit
- nChange
;
209 strHTML
+= "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -nValue
) + "<br>";
210 strHTML
+= "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nValue
) + "<br>";
213 CAmount nTxFee
= nDebit
- wtx
.GetValueOut();
215 strHTML
+= "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -nTxFee
) + "<br>";
220 // Mixed debit transaction
222 BOOST_FOREACH(const CTxIn
& txin
, wtx
.vin
)
223 if (wallet
->IsMine(txin
))
224 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -wallet
->GetDebit(txin
, ISMINE_ALL
)) + "<br>";
225 BOOST_FOREACH(const CTxOut
& txout
, wtx
.vout
)
226 if (wallet
->IsMine(txout
))
227 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, wallet
->GetCredit(txout
, ISMINE_ALL
)) + "<br>";
231 strHTML
+= "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nNet
, true) + "<br>";
236 if (wtx
.mapValue
.count("message") && !wtx
.mapValue
["message"].empty())
237 strHTML
+= "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx
.mapValue
["message"], true) + "<br>";
238 if (wtx
.mapValue
.count("comment") && !wtx
.mapValue
["comment"].empty())
239 strHTML
+= "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx
.mapValue
["comment"], true) + "<br>";
241 strHTML
+= "<b>" + tr("Transaction ID") + ":</b> " + TransactionRecord::formatSubTxId(wtx
.GetHash(), rec
->idx
) + "<br>";
243 // Message from normal bitcoin:URI (bitcoin:123...?message=example)
244 Q_FOREACH (const PAIRTYPE(std::string
, std::string
)& r
, wtx
.vOrderForm
)
245 if (r
.first
== "Message")
246 strHTML
+= "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r
.second
, true) + "<br>";
249 // PaymentRequest info:
251 Q_FOREACH (const PAIRTYPE(std::string
, std::string
)& r
, wtx
.vOrderForm
)
253 if (r
.first
== "PaymentRequest")
255 PaymentRequestPlus req
;
256 req
.parse(QByteArray::fromRawData(r
.second
.data(), r
.second
.size()));
258 if (req
.getMerchant(PaymentServer::getCertStore(), merchant
))
259 strHTML
+= "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant
) + "<br>";
263 if (wtx
.IsCoinBase())
265 quint32 numBlocksToMaturity
= COINBASE_MATURITY
+ 1;
266 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>";
274 strHTML
+= "<hr><br>" + tr("Debug information") + "<br><br>";
275 BOOST_FOREACH(const CTxIn
& txin
, wtx
.vin
)
276 if(wallet
->IsMine(txin
))
277 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -wallet
->GetDebit(txin
, ISMINE_ALL
)) + "<br>";
278 BOOST_FOREACH(const CTxOut
& txout
, wtx
.vout
)
279 if(wallet
->IsMine(txout
))
280 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, wallet
->GetCredit(txout
, ISMINE_ALL
)) + "<br>";
282 strHTML
+= "<br><b>" + tr("Transaction") + ":</b><br>";
283 strHTML
+= GUIUtil::HtmlEscape(wtx
.ToString(), true);
285 strHTML
+= "<br><b>" + tr("Inputs") + ":</b>";
288 BOOST_FOREACH(const CTxIn
& txin
, wtx
.vin
)
290 COutPoint prevout
= txin
.prevout
;
293 if(pcoinsTip
->GetCoins(prevout
.hash
, prev
))
295 if (prevout
.n
< prev
.vout
.size())
298 const CTxOut
&vout
= prev
.vout
[prevout
.n
];
299 CTxDestination address
;
300 if (ExtractDestination(vout
.scriptPubKey
, address
))
302 if (wallet
->mapAddressBook
.count(address
) && !wallet
->mapAddressBook
[address
].name
.empty())
303 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + " ";
304 strHTML
+= QString::fromStdString(CBitcoinAddress(address
).ToString());
306 strHTML
= strHTML
+ " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit
, vout
.nValue
);
307 strHTML
= strHTML
+ " IsMine=" + (wallet
->IsMine(vout
) & ISMINE_SPENDABLE
? tr("true") : tr("false")) + "</li>";
308 strHTML
= strHTML
+ " IsWatchOnly=" + (wallet
->IsMine(vout
) & ISMINE_WATCH_ONLY
? tr("true") : tr("false")) + "</li>";
316 strHTML
+= "</font></html>";