Bitcoin Core  31.0.0
P2P Digital Currency
transactiondesc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-present 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 #include <qt/transactiondesc.h>
6 
7 #include <qt/bitcoinunits.h>
8 #include <qt/guiutil.h>
9 #include <qt/paymentserver.h>
10 #include <qt/transactionrecord.h>
11 
12 #include <common/system.h>
13 #include <consensus/consensus.h>
14 #include <interfaces/node.h>
15 #include <interfaces/wallet.h>
16 #include <key_io.h>
17 #include <logging.h>
18 #include <policy/policy.h>
19 #include <validation.h>
20 
21 #include <cstdint>
22 #include <string>
23 
24 #include <QLatin1String>
25 
26 QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool)
27 {
28  int depth = status.depth_in_main_chain;
29  if (depth < 0) {
30  /*: Text explaining the current status of a transaction, shown in the
31  status field of the details window for this transaction. This status
32  represents an unconfirmed transaction that conflicts with a confirmed
33  transaction. */
34  return tr("conflicted with a transaction with %1 confirmations").arg(-depth);
35  } else if (depth == 0) {
36  QString s;
37  if (inMempool) {
38  /*: Text explaining the current status of a transaction, shown in the
39  status field of the details window for this transaction. This status
40  represents an unconfirmed transaction that is in the memory pool. */
41  s = tr("0/unconfirmed, in memory pool");
42  } else {
43  /*: Text explaining the current status of a transaction, shown in the
44  status field of the details window for this transaction. This status
45  represents an unconfirmed transaction that is not in the memory pool. */
46  s = tr("0/unconfirmed, not in memory pool");
47  }
48  if (status.is_abandoned) {
49  /*: Text explaining the current status of a transaction, shown in the
50  status field of the details window for this transaction. This
51  status represents an abandoned transaction. */
52  s += QLatin1String(", ") + tr("abandoned");
53  }
54  return s;
55  } else if (depth < 6) {
56  /*: Text explaining the current status of a transaction, shown in the
57  status field of the details window for this transaction. This
58  status represents a transaction confirmed in at least one block,
59  but less than 6 blocks. */
60  return tr("%1/unconfirmed").arg(depth);
61  } else {
62  /*: Text explaining the current status of a transaction, shown in the
63  status field of the details window for this transaction. This status
64  represents a transaction confirmed in 6 or more blocks. */
65  return tr("%1 confirmations").arg(depth);
66  }
67 }
68 
69 // Takes an encoded PaymentRequest as a string and tries to find the Common Name of the X.509 certificate
70 // used to sign the PaymentRequest.
71 bool GetPaymentRequestMerchant(const std::string& pr, QString& merchant)
72 {
73  // Search for the supported pki type strings
74  if (pr.find(std::string({0x12, 0x0b}) + "x509+sha256") != std::string::npos || pr.find(std::string({0x12, 0x09}) + "x509+sha1") != std::string::npos) {
75  // We want the common name of the Subject of the cert. This should be the second occurrence
76  // of the bytes 0x0603550403. The first occurrence of those is the common name of the issuer.
77  // After those bytes will be either 0x13 or 0x0C, then length, then either the ascii or utf8
78  // string with the common name which is the merchant name
79  size_t cn_pos = pr.find({0x06, 0x03, 0x55, 0x04, 0x03});
80  if (cn_pos != std::string::npos) {
81  cn_pos = pr.find({0x06, 0x03, 0x55, 0x04, 0x03}, cn_pos + 5);
82  if (cn_pos != std::string::npos) {
83  cn_pos += 5;
84  if (pr[cn_pos] == 0x13 || pr[cn_pos] == 0x0c) {
85  cn_pos++; // Consume the type
86  int str_len = pr[cn_pos];
87  cn_pos++; // Consume the string length
88  merchant = QString::fromUtf8(pr.data() + cn_pos, str_len);
89  return true;
90  }
91  }
92  }
93  }
94  return false;
95 }
96 
98 {
99  int numBlocks;
101  interfaces::WalletOrderForm orderForm;
102  bool inMempool;
103  interfaces::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks);
104 
105  QString strHTML;
106 
107  strHTML.reserve(4000);
108  strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
109 
110  int64_t nTime = wtx.time;
111  CAmount nCredit = wtx.credit;
112  CAmount nDebit = wtx.debit;
113  CAmount nNet = nCredit - nDebit;
114 
115  strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(status, inMempool);
116  strHTML += "<br>";
117 
118  strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
119 
120  //
121  // From
122  //
123  if (wtx.is_coinbase)
124  {
125  strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
126  }
127  else if (wtx.value_map.contains("from") && !wtx.value_map["from"].empty())
128  {
129  // Online transaction
130  strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.value_map["from"]) + "<br>";
131  }
132  else
133  {
134  // Offline transaction
135  if (nNet > 0)
136  {
137  // Credit
138  CTxDestination address = DecodeDestination(rec->address);
139  if (IsValidDestination(address)) {
140  std::string name;
141  if (wallet.getAddress(address, &name, /*purpose=*/nullptr))
142  {
143  strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
144  strHTML += "<b>" + tr("To") + ":</b> ";
145  strHTML += GUIUtil::HtmlEscape(rec->address);
146  QString addressOwned = tr("own address");
147  if (!name.empty())
148  strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(name) + ")";
149  else
150  strHTML += " (" + addressOwned + ")";
151  strHTML += "<br>";
152  }
153  }
154  }
155  }
156 
157  //
158  // To
159  //
160  if (wtx.value_map.contains("to") && !wtx.value_map["to"].empty())
161  {
162  // Online transaction
163  std::string strAddress = wtx.value_map["to"];
164  strHTML += "<b>" + tr("To") + ":</b> ";
165  CTxDestination dest = DecodeDestination(strAddress);
166  std::string name;
167  if (wallet.getAddress(
168  dest, &name, /*purpose=*/nullptr) && !name.empty())
169  strHTML += GUIUtil::HtmlEscape(name) + " ";
170  strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
171  }
172 
173  //
174  // Amount
175  //
176  if (wtx.is_coinbase && nCredit == 0)
177  {
178  //
179  // Coinbase
180  //
181  CAmount nUnmatured = 0;
182  for (const CTxOut& txout : wtx.tx->vout)
183  nUnmatured += wallet.getCredit(txout);
184  strHTML += "<b>" + tr("Credit") + ":</b> ";
185  if (status.is_in_main_chain)
186  strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")";
187  else
188  strHTML += "(" + tr("not accepted") + ")";
189  strHTML += "<br>";
190  }
191  else if (nNet > 0)
192  {
193  //
194  // Credit
195  //
196  strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>";
197  }
198  else
199  {
200  bool all_from_me = std::all_of(wtx.txin_is_mine.begin(), wtx.txin_is_mine.end(), [](bool mine) { return mine; });
201  bool all_to_me = std::all_of(wtx.txout_is_mine.begin(), wtx.txout_is_mine.end(), [](bool mine) { return mine; });
202 
203  if (all_from_me)
204  {
205  // Debit
206  //
207  auto mine = wtx.txout_is_mine.begin();
208  for (const CTxOut& txout : wtx.tx->vout)
209  {
210  // Ignore change
211  bool toSelf = *(mine++);
212  if (toSelf && all_from_me)
213  continue;
214 
215  if (!wtx.value_map.contains("to") || wtx.value_map["to"].empty())
216  {
217  // Offline transaction
218  CTxDestination address;
219  if (ExtractDestination(txout.scriptPubKey, address))
220  {
221  strHTML += "<b>" + tr("To") + ":</b> ";
222  std::string name;
223  if (wallet.getAddress(
224  address, &name, /*purpose=*/nullptr) && !name.empty())
225  strHTML += GUIUtil::HtmlEscape(name) + " ";
226  strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
227  if(toSelf)
228  strHTML += " (" + tr("own address") + ")";
229  strHTML += "<br>";
230  }
231  }
232 
233  strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
234  if(toSelf)
235  strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
236  }
237 
238  if (all_to_me)
239  {
240  // Payment to self
241  CAmount nChange = wtx.change;
242  CAmount nValue = nCredit - nChange;
243  strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
244  strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
245  }
246 
247  CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
248  if (nTxFee > 0)
249  strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
250  }
251  else
252  {
253  //
254  // Mixed debit transaction
255  //
256  auto mine = wtx.txin_is_mine.begin();
257  for (const CTxIn& txin : wtx.tx->vin) {
258  if (*(mine++)) {
259  strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin)) + "<br>";
260  }
261  }
262  mine = wtx.txout_is_mine.begin();
263  for (const CTxOut& txout : wtx.tx->vout) {
264  if (*(mine++)) {
265  strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout)) + "<br>";
266  }
267  }
268  }
269  }
270 
271  strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>";
272 
273  //
274  // Message
275  //
276  if (wtx.value_map.contains("message") && !wtx.value_map["message"].empty())
277  strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["message"], true) + "<br>";
278  if (wtx.value_map.contains("comment") && !wtx.value_map["comment"].empty())
279  strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["comment"], true) + "<br>";
280 
281  strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>";
282  strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->ComputeTotalSize()) + " bytes<br>";
283  strHTML += "<b>" + tr("Transaction virtual size") + ":</b> " + QString::number(GetVirtualTransactionSize(*wtx.tx)) + " bytes<br>";
284  strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
285 
286  // Message from normal bitcoin:URI (bitcoin:123...?message=example)
287  for (const std::pair<std::string, std::string>& r : orderForm) {
288  if (r.first == "Message")
289  strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
290 
291  //
292  // PaymentRequest info:
293  //
294  if (r.first == "PaymentRequest")
295  {
296  QString merchant;
297  if (!GetPaymentRequestMerchant(r.second, merchant)) {
298  merchant.clear();
299  } else {
300  merchant = tr("%1 (Certificate was not verified)").arg(merchant);
301  }
302  if (!merchant.isNull()) {
303  strHTML += "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant) + "<br>";
304  }
305  }
306  }
307 
308  if (wtx.is_coinbase)
309  {
310  quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
311  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>";
312  }
313 
314  //
315  // Debug view
316  //
317  if (node.getLogCategories() != BCLog::NONE)
318  {
319  strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
320  for (const CTxIn& txin : wtx.tx->vin)
321  if(wallet.txinIsMine(txin))
322  strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin)) + "<br>";
323  for (const CTxOut& txout : wtx.tx->vout)
324  if(wallet.txoutIsMine(txout))
325  strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout)) + "<br>";
326 
327  strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
328  strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
329 
330  strHTML += "<br><b>" + tr("Inputs") + ":</b>";
331  strHTML += "<ul>";
332 
333  for (const CTxIn& txin : wtx.tx->vin)
334  {
335  COutPoint prevout = txin.prevout;
336 
337  if (auto prev{node.getUnspentOutput(prevout)}) {
338  {
339  strHTML += "<li>";
340  const CTxOut& vout = prev->out;
341  CTxDestination address;
342  if (ExtractDestination(vout.scriptPubKey, address))
343  {
344  std::string name;
345  if (wallet.getAddress(address, &name, /*purpose=*/nullptr) && !name.empty())
346  strHTML += GUIUtil::HtmlEscape(name) + " ";
347  strHTML += QString::fromStdString(EncodeDestination(address));
348  }
349  strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
350  strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) ? tr("true") : tr("false")) + "</li>";
351  }
352  }
353  }
354 
355  strHTML += "</ul>";
356  }
357 
358  strHTML += "</font></html>";
359  return strHTML;
360 }
CAmount nValue
Definition: transaction.h:142
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
CScript scriptPubKey
Definition: transaction.h:143
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
QString getTxHash() const
Return the unique identifier for this transaction (part)
static QString FormatTxStatus(const interfaces::WalletTxStatus &status, bool inMempool)
std::vector< bool > txout_is_mine
Definition: wallet.h:385
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:90
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule) ...
Definition: consensus.h:19
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:249
CTransactionRef tx
Definition: wallet.h:383
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Compute the virtual transaction size (weight reinterpreted as bytes).
Definition: policy.cpp:381
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
int getOutputIndex() const
Return the output index of the subtransaction.
UI model for a transaction.
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a scriptPubKey for the destination.
Definition: addresstype.cpp:49
An input of a transaction.
Definition: transaction.h:61
const char * name
Definition: rest.cpp:48
Interface for accessing a wallet.
Definition: wallet.h:65
An output of a transaction.
Definition: transaction.h:139
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:28
Definition: messages.h:21
std::vector< bool > txin_is_mine
Definition: wallet.h:384
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:143
std::vector< std::pair< std::string, std::string > > WalletOrderForm
Definition: wallet.h:61
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:294
bool GetPaymentRequestMerchant(const std::string &pr, QString &merchant)
static QString formatHtmlWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
std::map< std::string, std::string > value_map
Definition: wallet.h:393
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:299
COutPoint prevout
Definition: transaction.h:64
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:69
Updated transaction status.
Definition: wallet.h:400
static QString toHTML(interfaces::Node &node, interfaces::Wallet &wallet, TransactionRecord *rec, BitcoinUnit unit)