Bitcoin Core  0.19.99
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 }
CAmount nValue
Definition: transaction.h:136
virtual bool getAddress(const CTxDestination &dest, std::string *name, isminetype *is_mine, std::string *purpose)=0
Look up address in wallet, return whether exists.
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:156
virtual WalletTx getWalletTxDetails(const uint256 &txid, WalletTxStatus &tx_status, WalletOrderForm &order_form, bool &in_mempool, int &num_blocks)=0
Get transaction details.
CScript scriptPubKey
Definition: transaction.h:137
A UTXO entry.
Definition: coins.h:30
virtual isminetype txoutIsMine(const CTxOut &txout)=0
Return whether transaction output belongs to wallet.
QString getTxHash() const
Return the unique identifier for this transaction (part)
std::vector< isminetype > txin_is_mine
Definition: wallet.h:337
std::vector< isminetype > txout_is_mine
Definition: wallet.h:338
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as HTML string (with unit)
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:325
CTxOut out
unspent transaction output
Definition: coins.h:34
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:71
virtual uint32_t getLogCategories()=0
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule) ...
Definition: consensus.h:19
static QString toHTML(interfaces::Node &node, interfaces::Wallet &wallet, TransactionRecord *rec, int unit)
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:218
CTransactionRef tx
Definition: wallet.h:336
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:238
virtual isminetype txinIsMine(const CTxIn &txin)=0
Return whether transaction input belongs to wallet.
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.
virtual CAmount getCredit(const CTxOut &txout, isminefilter filter)=0
Return credit amount if transaction input belongs to wallet.
An input of a transaction.
Definition: transaction.h:63
const char * name
Definition: rest.cpp:40
Interface for accessing a wallet.
Definition: wallet.h:48
static QString FormatTxStatus(const interfaces::WalletTx &wtx, const interfaces::WalletTxStatus &status, bool inMempool, int numBlocks)
isminetype
IsMine() return codes.
Definition: ismine.h:18
An output of a transaction.
Definition: transaction.h:133
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:18
virtual CAmount getDebit(const CTxIn &txin, isminefilter filter)=0
Return debit amount if transaction input belongs to wallet.
CTxDestination DecodeDestination(const std::string &str)
Definition: key_io.cpp:215
std::vector< std::pair< std::string, std::string > > WalletOrderForm
Definition: wallet.h:44
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:210
bool GetPaymentRequestMerchant(const std::string &pr, QString &merchant)
std::map< std::string, std::string > value_map
Definition: wallet.h:345
static const unsigned int LOCKTIME_THRESHOLD
Definition: script.h:39
boost::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:143
COutPoint prevout
Definition: transaction.h:66
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:39
Updated transaction status.
Definition: wallet.h:350
virtual bool getUnspentOutput(const COutPoint &output, Coin &coin)=0
Get unspent outputs associated with a transaction.