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