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