1 // Copyright (c) 2011-2016 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"
14 #include "validation.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
.tx
->nLockTime
< LOCKTIME_THRESHOLD
)
30 return tr("Open for %n more block(s)", "", wtx
.tx
->nLockTime
- chainActive
.Height());
32 return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx
.tx
->nLockTime
));
36 int nDepth
= wtx
.GetDepthInMainChain();
38 return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth
);
39 else if (GetAdjustedTime() - wtx
.nTimeReceived
> 2 * 60 && wtx
.GetRequestCount() == 0)
40 return tr("%1/offline").arg(nDepth
);
42 return tr("0/unconfirmed, %1").arg((wtx
.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx
.isAbandoned() ? ", "+tr("abandoned") : "");
44 return tr("%1/unconfirmed").arg(nDepth
);
46 return tr("%1 confirmations").arg(nDepth
);
50 QString
TransactionDesc::toHTML(CWallet
*wallet
, CWalletTx
&wtx
, TransactionRecord
*rec
, int unit
)
54 LOCK2(cs_main
, wallet
->cs_wallet
);
55 strHTML
.reserve(4000);
56 strHTML
+= "<html><font face='verdana, arial, helvetica, sans-serif'>";
58 int64_t nTime
= wtx
.GetTxTime();
59 CAmount nCredit
= wtx
.GetCredit(ISMINE_ALL
);
60 CAmount nDebit
= wtx
.GetDebit(ISMINE_ALL
);
61 CAmount nNet
= nCredit
- nDebit
;
63 strHTML
+= "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx
);
64 int nRequests
= wtx
.GetRequestCount();
68 strHTML
+= tr(", has not been successfully broadcast yet");
69 else if (nRequests
> 0)
70 strHTML
+= tr(", broadcast through %n node(s)", "", nRequests
);
74 strHTML
+= "<b>" + tr("Date") + ":</b> " + (nTime
? GUIUtil::dateTimeStr(nTime
) : "") + "<br>";
81 strHTML
+= "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
83 else if (wtx
.mapValue
.count("from") && !wtx
.mapValue
["from"].empty())
86 strHTML
+= "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx
.mapValue
["from"]) + "<br>";
90 // Offline transaction
94 CTxDestination address
= DecodeDestination(rec
->address
);
95 if (IsValidDestination(address
)) {
96 if (wallet
->mapAddressBook
.count(address
))
98 strHTML
+= "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
99 strHTML
+= "<b>" + tr("To") + ":</b> ";
100 strHTML
+= GUIUtil::HtmlEscape(rec
->address
);
101 QString addressOwned
= (::IsMine(*wallet
, address
) == ISMINE_SPENDABLE
) ? tr("own address") : tr("watch-only");
102 if (!wallet
->mapAddressBook
[address
].name
.empty())
103 strHTML
+= " (" + addressOwned
+ ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + ")";
105 strHTML
+= " (" + addressOwned
+ ")";
115 if (wtx
.mapValue
.count("to") && !wtx
.mapValue
["to"].empty())
117 // Online transaction
118 std::string strAddress
= wtx
.mapValue
["to"];
119 strHTML
+= "<b>" + tr("To") + ":</b> ";
120 CTxDestination dest
= DecodeDestination(strAddress
);
121 if (wallet
->mapAddressBook
.count(dest
) && !wallet
->mapAddressBook
[dest
].name
.empty())
122 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[dest
].name
) + " ";
123 strHTML
+= GUIUtil::HtmlEscape(strAddress
) + "<br>";
129 if (wtx
.IsCoinBase() && nCredit
== 0)
134 CAmount nUnmatured
= 0;
135 for (const CTxOut
& txout
: wtx
.tx
->vout
)
136 nUnmatured
+= wallet
->GetCredit(txout
, ISMINE_ALL
);
137 strHTML
+= "<b>" + tr("Credit") + ":</b> ";
138 if (wtx
.IsInMainChain())
139 strHTML
+= BitcoinUnits::formatHtmlWithUnit(unit
, nUnmatured
)+ " (" + tr("matures in %n more block(s)", "", wtx
.GetBlocksToMaturity()) + ")";
141 strHTML
+= "(" + tr("not accepted") + ")";
149 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nNet
) + "<br>";
153 isminetype fAllFromMe
= ISMINE_SPENDABLE
;
154 for (const CTxIn
& txin
: wtx
.tx
->vin
)
156 isminetype mine
= wallet
->IsMine(txin
);
157 if(fAllFromMe
> mine
) fAllFromMe
= mine
;
160 isminetype fAllToMe
= ISMINE_SPENDABLE
;
161 for (const CTxOut
& txout
: wtx
.tx
->vout
)
163 isminetype mine
= wallet
->IsMine(txout
);
164 if(fAllToMe
> mine
) fAllToMe
= mine
;
169 if(fAllFromMe
& ISMINE_WATCH_ONLY
)
170 strHTML
+= "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
175 for (const CTxOut
& txout
: wtx
.tx
->vout
)
178 isminetype toSelf
= wallet
->IsMine(txout
);
179 if ((toSelf
== ISMINE_SPENDABLE
) && (fAllFromMe
== ISMINE_SPENDABLE
))
182 if (!wtx
.mapValue
.count("to") || wtx
.mapValue
["to"].empty())
184 // Offline transaction
185 CTxDestination address
;
186 if (ExtractDestination(txout
.scriptPubKey
, address
))
188 strHTML
+= "<b>" + tr("To") + ":</b> ";
189 if (wallet
->mapAddressBook
.count(address
) && !wallet
->mapAddressBook
[address
].name
.empty())
190 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + " ";
191 strHTML
+= GUIUtil::HtmlEscape(EncodeDestination(address
));
192 if(toSelf
== ISMINE_SPENDABLE
)
193 strHTML
+= " (own address)";
194 else if(toSelf
& ISMINE_WATCH_ONLY
)
195 strHTML
+= " (watch-only)";
200 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -txout
.nValue
) + "<br>";
202 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, txout
.nValue
) + "<br>";
208 CAmount nChange
= wtx
.GetChange();
209 CAmount nValue
= nCredit
- nChange
;
210 strHTML
+= "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -nValue
) + "<br>";
211 strHTML
+= "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nValue
) + "<br>";
214 CAmount nTxFee
= nDebit
- wtx
.tx
->GetValueOut();
216 strHTML
+= "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -nTxFee
) + "<br>";
221 // Mixed debit transaction
223 for (const CTxIn
& txin
: wtx
.tx
->vin
)
224 if (wallet
->IsMine(txin
))
225 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -wallet
->GetDebit(txin
, ISMINE_ALL
)) + "<br>";
226 for (const CTxOut
& txout
: wtx
.tx
->vout
)
227 if (wallet
->IsMine(txout
))
228 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, wallet
->GetCredit(txout
, ISMINE_ALL
)) + "<br>";
232 strHTML
+= "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, nNet
, true) + "<br>";
237 if (wtx
.mapValue
.count("message") && !wtx
.mapValue
["message"].empty())
238 strHTML
+= "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx
.mapValue
["message"], true) + "<br>";
239 if (wtx
.mapValue
.count("comment") && !wtx
.mapValue
["comment"].empty())
240 strHTML
+= "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx
.mapValue
["comment"], true) + "<br>";
242 strHTML
+= "<b>" + tr("Transaction ID") + ":</b> " + rec
->getTxID() + "<br>";
243 strHTML
+= "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx
.tx
->GetTotalSize()) + " bytes<br>";
244 strHTML
+= "<b>" + tr("Output index") + ":</b> " + QString::number(rec
->getOutputIndex()) + "<br>";
246 // Message from normal bitcoin:URI (bitcoin:123...?message=example)
247 for (const std::pair
<std::string
, std::string
>& r
: wtx
.vOrderForm
)
248 if (r
.first
== "Message")
249 strHTML
+= "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r
.second
, true) + "<br>";
252 // PaymentRequest info:
254 for (const std::pair
<std::string
, std::string
>& r
: wtx
.vOrderForm
)
256 if (r
.first
== "PaymentRequest")
258 PaymentRequestPlus req
;
259 req
.parse(QByteArray::fromRawData(r
.second
.data(), r
.second
.size()));
261 if (req
.getMerchant(PaymentServer::getCertStore(), merchant
))
262 strHTML
+= "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant
) + "<br>";
266 if (wtx
.IsCoinBase())
268 quint32 numBlocksToMaturity
= COINBASE_MATURITY
+ 1;
269 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>";
275 if (logCategories
!= BCLog::NONE
)
277 strHTML
+= "<hr><br>" + tr("Debug information") + "<br><br>";
278 for (const CTxIn
& txin
: wtx
.tx
->vin
)
279 if(wallet
->IsMine(txin
))
280 strHTML
+= "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, -wallet
->GetDebit(txin
, ISMINE_ALL
)) + "<br>";
281 for (const CTxOut
& txout
: wtx
.tx
->vout
)
282 if(wallet
->IsMine(txout
))
283 strHTML
+= "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit
, wallet
->GetCredit(txout
, ISMINE_ALL
)) + "<br>";
285 strHTML
+= "<br><b>" + tr("Transaction") + ":</b><br>";
286 strHTML
+= GUIUtil::HtmlEscape(wtx
.tx
->ToString(), true);
288 strHTML
+= "<br><b>" + tr("Inputs") + ":</b>";
291 for (const CTxIn
& txin
: wtx
.tx
->vin
)
293 COutPoint prevout
= txin
.prevout
;
296 if(pcoinsTip
->GetCoin(prevout
, prev
))
300 const CTxOut
&vout
= prev
.out
;
301 CTxDestination address
;
302 if (ExtractDestination(vout
.scriptPubKey
, address
))
304 if (wallet
->mapAddressBook
.count(address
) && !wallet
->mapAddressBook
[address
].name
.empty())
305 strHTML
+= GUIUtil::HtmlEscape(wallet
->mapAddressBook
[address
].name
) + " ";
306 strHTML
+= QString::fromStdString(EncodeDestination(address
));
308 strHTML
= strHTML
+ " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit
, vout
.nValue
);
309 strHTML
= strHTML
+ " IsMine=" + (wallet
->IsMine(vout
) & ISMINE_SPENDABLE
? tr("true") : tr("false")) + "</li>";
310 strHTML
= strHTML
+ " IsWatchOnly=" + (wallet
->IsMine(vout
) & ISMINE_WATCH_ONLY
? tr("true") : tr("false")) + "</li>";
318 strHTML
+= "</font></html>";