Bitcoin ABC  0.28.12
P2P Digital Currency
transactiondesc.cpp
Go to the documentation of this file.
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.
4 
5 #ifdef HAVE_CONFIG_H
6 #include <config/bitcoin-config.h>
7 #endif
8 
9 #include <qt/transactiondesc.h>
10 
11 #include <cashaddrenc.h>
12 #include <chain.h>
13 #include <consensus/consensus.h>
14 #include <interfaces/node.h>
15 #include <interfaces/wallet.h>
16 #include <key_io.h>
17 #include <policy/policy.h>
18 #include <qt/bitcoinunits.h>
19 #include <qt/guiutil.h>
20 #include <qt/paymentserver.h>
21 #include <qt/transactionrecord.h>
22 #include <util/system.h>
23 #include <validation.h>
24 #include <wallet/ismine.h>
25 
26 #include <cstdint>
27 #include <string>
28 
29 QString
31  const interfaces::WalletTxStatus &status,
32  bool inMempool, int numBlocks) {
33  int nDepth = status.depth_in_main_chain;
34  if (nDepth < 0) {
35  return tr("conflicted with a transaction with %1 confirmations")
36  .arg(-nDepth);
37  } else if (nDepth == 0) {
38  return tr("0/unconfirmed, %1")
39  .arg((inMempool ? tr("in memory pool")
40  : tr("not in memory pool"))) +
41  (status.is_abandoned ? ", " + tr("abandoned") : "");
42  } else if (nDepth < 6) {
43  return tr("%1/unconfirmed").arg(nDepth);
44  } else {
45  return tr("%1 confirmations").arg(nDepth);
46  }
47 }
48 
49 #ifndef ENABLE_BIP70
50 // Takes an encoded PaymentRequest as a string and tries to find the Common Name
51 // of the X.509 certificate used to sign the PaymentRequest.
52 bool GetPaymentRequestMerchant(const std::string &pr, QString &merchant) {
53  // Search for the supported pki type strings
54  if (pr.find(std::string({0x12, 0x0b}) + "x509+sha256") !=
55  std::string::npos ||
56  pr.find(std::string({0x12, 0x09}) + "x509+sha1") != std::string::npos) {
57  // We want the common name of the Subject of the cert. This should be
58  // the second occurrence of the bytes 0x0603550403. The first occurrence
59  // of those is the common name of the issuer. After those bytes will be
60  // either 0x13 or 0x0C, then length, then either the ascii or utf8
61  // string with the common name which is the merchant name
62  size_t cn_pos = pr.find({0x06, 0x03, 0x55, 0x04, 0x03});
63  if (cn_pos != std::string::npos) {
64  cn_pos = pr.find({0x06, 0x03, 0x55, 0x04, 0x03}, cn_pos + 5);
65  if (cn_pos != std::string::npos) {
66  cn_pos += 5;
67  if (pr[cn_pos] == 0x13 || pr[cn_pos] == 0x0c) {
68  cn_pos++; // Consume the type
69  int str_len = pr[cn_pos];
70  cn_pos++; // Consume the string length
71  merchant = QString::fromUtf8(pr.data() + cn_pos, str_len);
72  return true;
73  }
74  }
75  }
76  }
77  return false;
78 }
79 #endif
80 
83  TransactionRecord *rec, int unit) {
84  int numBlocks;
87  bool inMempool;
88  interfaces::WalletTx wtx = wallet.getWalletTxDetails(
89  rec->txid, status, orderForm, inMempool, numBlocks);
90 
91  QString strHTML;
92 
93  strHTML.reserve(4000);
94  strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
95 
96  int64_t nTime = wtx.time;
97  Amount nCredit = wtx.credit;
98  Amount nDebit = wtx.debit;
99  Amount nNet = nCredit - nDebit;
100 
101  strHTML += "<b>" + tr("Status") + ":</b> " +
102  FormatTxStatus(wtx, status, inMempool, numBlocks);
103  strHTML += "<br>";
104 
105  strHTML += "<b>" + tr("Date") + ":</b> " +
106  (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
107 
108  //
109  // From
110  //
111  if (wtx.is_coinbase) {
112  strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
113  } else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty()) {
114  // Online transaction
115  strHTML += "<b>" + tr("From") + ":</b> " +
116  GUIUtil::HtmlEscape(wtx.value_map["from"]) + "<br>";
117  } else {
118  // Offline transaction
119  if (nNet > Amount::zero()) {
120  // Credit
121  CTxDestination address =
122  DecodeDestination(rec->address, wallet.getChainParams());
123  if (IsValidDestination(address)) {
124  std::string name;
125  isminetype ismine;
126  if (wallet.getAddress(address, &name, &ismine,
127  /* purpose= */ nullptr)) {
128  strHTML +=
129  "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
130  strHTML += "<b>" + tr("To") + ":</b> ";
131  strHTML += GUIUtil::HtmlEscape(rec->address);
132  QString addressOwned = ismine == ISMINE_SPENDABLE
133  ? tr("own address")
134  : tr("watch-only");
135  if (!name.empty()) {
136  strHTML += " (" + addressOwned + ", " + tr("label") +
137  ": " + GUIUtil::HtmlEscape(name) + ")";
138  } else {
139  strHTML += " (" + addressOwned + ")";
140  }
141  strHTML += "<br>";
142  }
143  }
144  }
145  }
146 
147  //
148  // To
149  //
150  if (wtx.value_map.count("to") && !wtx.value_map["to"].empty()) {
151  // Online transaction
152  std::string strAddress = wtx.value_map["to"];
153  strHTML += "<b>" + tr("To") + ":</b> ";
154  CTxDestination dest =
155  DecodeDestination(strAddress, wallet.getChainParams());
156  std::string name;
157  if (wallet.getAddress(dest, &name, /* is_mine= */ nullptr,
158  /* purpose= */ nullptr) &&
159  !name.empty()) {
160  strHTML += GUIUtil::HtmlEscape(name) + " ";
161  }
162  strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
163  }
164 
165  //
166  // Amount
167  //
168  if (wtx.is_coinbase && nCredit == Amount::zero()) {
169  //
170  // Coinbase
171  //
172  Amount nUnmatured = Amount::zero();
173  for (const CTxOut &txout : wtx.tx->vout) {
174  nUnmatured += wallet.getCredit(txout, ISMINE_ALL);
175  }
176  strHTML += "<b>" + tr("Credit") + ":</b> ";
177  if (status.is_in_main_chain) {
178  strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured) +
179  " (" +
180  tr("matures in %n more block(s)", "",
181  status.blocks_to_maturity) +
182  ")";
183  } else {
184  strHTML += "(" + tr("not accepted") + ")";
185  }
186  strHTML += "<br>";
187  } else if (nNet > Amount::zero()) {
188  //
189  // Credit
190  //
191  strHTML += "<b>" + tr("Credit") + ":</b> " +
192  BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>";
193  } else {
194  isminetype fAllFromMe = ISMINE_SPENDABLE;
195  for (const isminetype mine : wtx.txin_is_mine) {
196  if (fAllFromMe > mine) {
197  fAllFromMe = mine;
198  }
199  }
200 
201  isminetype fAllToMe = ISMINE_SPENDABLE;
202  for (const isminetype mine : wtx.txout_is_mine) {
203  if (fAllToMe > mine) {
204  fAllToMe = mine;
205  }
206  }
207 
208  if (fAllFromMe) {
209  if (fAllFromMe & ISMINE_WATCH_ONLY) {
210  strHTML +=
211  "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
212  }
213 
214  //
215  // Debit
216  //
217  auto mine = wtx.txout_is_mine.begin();
218  for (const CTxOut &txout : wtx.tx->vout) {
219  // Ignore change
220  isminetype toSelf = *(mine++);
221  if ((toSelf == ISMINE_SPENDABLE) &&
222  (fAllFromMe == ISMINE_SPENDABLE)) {
223  continue;
224  }
225 
226  if (!wtx.value_map.count("to") || wtx.value_map["to"].empty()) {
227  // Offline transaction
228  CTxDestination address;
229  if (ExtractDestination(txout.scriptPubKey, address)) {
230  strHTML += "<b>" + tr("To") + ":</b> ";
231  std::string name;
232  if (wallet.getAddress(address, &name,
233  /* is_mine= */ nullptr,
234  /* purpose= */ nullptr) &&
235  !name.empty()) {
236  strHTML += GUIUtil::HtmlEscape(name) + " ";
237  }
238  strHTML += GUIUtil::HtmlEscape(
239  EncodeCashAddr(address, wallet.getChainParams()));
240  if (toSelf == ISMINE_SPENDABLE) {
241  strHTML += " (own address)";
242  } else if (toSelf & ISMINE_WATCH_ONLY) {
243  strHTML += " (watch-only)";
244  }
245  strHTML += "<br>";
246  }
247  }
248 
249  strHTML +=
250  "<b>" + tr("Debit") + ":</b> " +
251  BitcoinUnits::formatHtmlWithUnit(unit, -1 * txout.nValue) +
252  "<br>";
253  if (toSelf) {
254  strHTML +=
255  "<b>" + tr("Credit") + ":</b> " +
257  "<br>";
258  }
259  }
260 
261  if (fAllToMe) {
262  // Payment to self
263  Amount nChange = wtx.change;
264  Amount nValue = nCredit - nChange;
265  strHTML += "<b>" + tr("Total debit") + ":</b> " +
266  BitcoinUnits::formatHtmlWithUnit(unit, -1 * nValue) +
267  "<br>";
268  strHTML += "<b>" + tr("Total credit") + ":</b> " +
269  BitcoinUnits::formatHtmlWithUnit(unit, nValue) +
270  "<br>";
271  }
272 
273  Amount nTxFee = nDebit - wtx.tx->GetValueOut();
274  if (nTxFee > Amount::zero()) {
275  strHTML += "<b>" + tr("Transaction fee") + ":</b> " +
276  BitcoinUnits::formatHtmlWithUnit(unit, -1 * nTxFee) +
277  "<br>";
278  }
279  } else {
280  //
281  // Mixed debit transaction
282  //
283  auto mine = wtx.txin_is_mine.begin();
284  for (const CTxIn &txin : wtx.tx->vin) {
285  if (*(mine++)) {
286  strHTML += "<b>" + tr("Debit") + ":</b> " +
288  unit, -wallet.getDebit(txin, ISMINE_ALL)) +
289  "<br>";
290  }
291  }
292  mine = wtx.txout_is_mine.begin();
293  for (const CTxOut &txout : wtx.tx->vout) {
294  if (*(mine++)) {
295  strHTML += "<b>" + tr("Credit") + ":</b> " +
297  unit, wallet.getCredit(txout, ISMINE_ALL)) +
298  "<br>";
299  }
300  }
301  }
302  }
303 
304  strHTML += "<b>" + tr("Net amount") + ":</b> " +
305  BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>";
306 
307  //
308  // Message
309  //
310  if (wtx.value_map.count("message") && !wtx.value_map["message"].empty()) {
311  strHTML += "<br><b>" + tr("Message") + ":</b><br>" +
312  GUIUtil::HtmlEscape(wtx.value_map["message"], true) + "<br>";
313  }
314  if (wtx.value_map.count("comment") && !wtx.value_map["comment"].empty()) {
315  strHTML += "<br><b>" + tr("Comment") + ":</b><br>" +
316  GUIUtil::HtmlEscape(wtx.value_map["comment"], true) + "<br>";
317  }
318  strHTML +=
319  "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
320  strHTML += "<b>" + tr("Transaction total size") + ":</b> " +
321  QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
322  strHTML += "<b>" + tr("Output index") + ":</b> " +
323  QString::number(rec->getOutputIndex()) + "<br>";
324 
325  // Message from normal bitcoincash:URI (bitcoincash:123...?message=example)
326  for (const std::pair<std::string, std::string> &r : orderForm) {
327  if (r.first == "Message") {
328  strHTML += "<br><b>" + tr("Message") + ":</b><br>" +
329  GUIUtil::HtmlEscape(r.second, true) + "<br>";
330  }
331 
332  //
333  // PaymentRequest info:
334  //
335  if (r.first == "PaymentRequest") {
336  QString merchant;
337 #ifdef ENABLE_BIP70
338  PaymentRequestPlus req;
339  req.parse(
340  QByteArray::fromRawData(r.second.data(), r.second.size()));
341  if (!req.getMerchant(PaymentServer::getCertStore(), merchant)) {
342  merchant.clear();
343  }
344 #else
345  if (!GetPaymentRequestMerchant(r.second, merchant)) {
346  merchant.clear();
347  } else {
348  merchant += tr(" (Certificate was not verified)");
349  }
350 #endif
351  if (!merchant.isNull()) {
352  strHTML += "<b>" + tr("Merchant") + ":</b> " +
353  GUIUtil::HtmlEscape(merchant) + "<br>";
354  }
355  }
356  }
357 
358  if (wtx.is_coinbase) {
359  quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
360  strHTML +=
361  "<br>" +
362  tr("Generated coins must mature %1 blocks before they can be "
363  "spent. When you generated this block, it was broadcast to the "
364  "network to be added to the block chain. If it fails to get "
365  "into the chain, its state will change to \"not accepted\" and "
366  "it won't be spendable. This may occasionally happen if another "
367  "node generates a block within a few seconds of yours.")
368  .arg(QString::number(numBlocksToMaturity)) +
369  "<br>";
370  }
371 
372  //
373  // Debug view
374  //
375  if (gArgs.GetBoolArg("-debug", false)) {
376  strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
377  for (const CTxIn &txin : wtx.tx->vin) {
378  if (wallet.txinIsMine(txin)) {
379  strHTML += "<b>" + tr("Debit") + ":</b> " +
381  unit, -wallet.getDebit(txin, ISMINE_ALL)) +
382  "<br>";
383  }
384  }
385  for (const CTxOut &txout : wtx.tx->vout) {
386  if (wallet.txoutIsMine(txout)) {
387  strHTML += "<b>" + tr("Credit") + ":</b> " +
389  unit, wallet.getCredit(txout, ISMINE_ALL)) +
390  "<br>";
391  }
392  }
393 
394  strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
395  strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
396 
397  strHTML += "<br><b>" + tr("Inputs") + ":</b>";
398  strHTML += "<ul>";
399 
400  for (const CTxIn &txin : wtx.tx->vin) {
401  COutPoint prevout = txin.prevout;
402 
403  Coin prev;
404  if (node.getUnspentOutput(prevout, prev)) {
405  strHTML += "<li>";
406  const CTxOut &vout = prev.GetTxOut();
407  CTxDestination address;
408  if (ExtractDestination(vout.scriptPubKey, address)) {
409  std::string name;
410  if (wallet.getAddress(address, &name,
411  /* is_mine= */ nullptr,
412  /* purpose= */ nullptr) &&
413  !name.empty()) {
414  strHTML += GUIUtil::HtmlEscape(name) + " ";
415  }
416  strHTML += QString::fromStdString(
417  EncodeCashAddr(address, wallet.getChainParams()));
418  }
419  strHTML = strHTML + " " + tr("Amount") + "=" +
421  strHTML = strHTML + " IsMine=" +
422  (wallet.txoutIsMine(vout) & ISMINE_SPENDABLE
423  ? tr("true")
424  : tr("false")) +
425  "</li>";
426  strHTML = strHTML + " IsWatchOnly=" +
427  (wallet.txoutIsMine(vout) & ISMINE_WATCH_ONLY
428  ? tr("true")
429  : tr("false")) +
430  "</li>";
431  }
432  }
433 
434  strHTML += "</ul>";
435  }
436 
437  strHTML += "</font></html>";
438  return strHTML;
439 }
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:90
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: system.cpp:665
static QString formatHtmlWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
An input of a transaction.
Definition: transaction.h:59
COutPoint prevout
Definition: transaction.h:61
An output of a transaction.
Definition: transaction.h:128
CScript scriptPubKey
Definition: transaction.h:131
Amount nValue
Definition: transaction.h:130
A UTXO entry.
Definition: coins.h:27
CTxOut & GetTxOut()
Definition: coins.h:48
bool getMerchant(X509_STORE *certStore, QString &merchant) const
bool parse(const QByteArray &data)
static QString toHTML(interfaces::Node &node, interfaces::Wallet &wallet, TransactionRecord *rec, int unit)
static QString FormatTxStatus(const interfaces::WalletTx &wtx, const interfaces::WalletTxStatus &status, bool inMempool, int numBlocks)
UI model for a transaction.
int getOutputIndex() const
Return the output index of the subtransaction
QString getTxID() const
Return the unique identifier for this transaction (part)
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:58
Interface for accessing a wallet.
Definition: wallet.h:59
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule).
Definition: consensus.h:32
isminetype
IsMine() return codes.
Definition: ismine.h:18
@ ISMINE_ALL
Definition: ismine.h:23
@ ISMINE_SPENDABLE
Definition: ismine.h:21
@ ISMINE_WATCH_ONLY
Definition: ismine.h:20
CTxDestination DecodeDestination(const std::string &addr, const CChainParams &params)
Definition: key_io.cpp:174
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:248
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:75
std::vector< std::pair< std::string, std::string > > WalletOrderForm
Definition: wallet.h:55
Definition: init.h:28
const char * name
Definition: rest.cpp:48
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:260
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
std::vector< isminetype > txout_is_mine
Definition: wallet.h:371
CTransactionRef tx
Definition: wallet.h:369
std::map< std::string, std::string > value_map
Definition: wallet.h:378
std::vector< isminetype > txin_is_mine
Definition: wallet.h:370
Updated transaction status.
Definition: wallet.h:383
ArgsManager gArgs
Definition: system.cpp:80
bool GetPaymentRequestMerchant(const std::string &pr, QString &merchant)