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