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