diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 455c61735..b370f6972 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,380 +1,403 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "transactiondesc.h" #include "bitcoinunits.h" #include "guiutil.h" #include "paymentserver.h" #include "transactionrecord.h" #include "consensus/consensus.h" #include "dstencode.h" #include "script/script.h" #include "timedata.h" #include "util.h" #include "validation.h" #include "wallet/db.h" #include "wallet/finaltx.h" #include "wallet/wallet.h" #include #include QString TransactionDesc::FormatTxStatus(const CWalletTx &wtx) { AssertLockHeld(cs_main); if (!CheckFinalTx(wtx)) { - if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) + if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) { return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height()); - else + } else { return tr("Open until %1") .arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime)); + } } else { int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) + if (nDepth < 0) { return tr("conflicted with a transaction with %1 confirmations") .arg(-nDepth); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && - wtx.GetRequestCount() == 0) + } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && + wtx.GetRequestCount() == 0) { return tr("%1/offline").arg(nDepth); - else if (nDepth == 0) + } else if (nDepth == 0) { return tr("0/unconfirmed, %1") .arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", " + tr("abandoned") : ""); - else if (nDepth < 6) + } else if (nDepth < 6) { return tr("%1/unconfirmed").arg(nDepth); - else + } else { return tr("%1 confirmations").arg(nDepth); + } } } QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit) { QString strHTML; LOCK2(cs_main, wallet->cs_wallet); strHTML.reserve(4000); strHTML += ""; int64_t nTime = wtx.GetTxTime(); Amount nCredit = wtx.GetCredit(ISMINE_ALL); Amount nDebit = wtx.GetDebit(ISMINE_ALL); Amount nNet = nCredit - nDebit; strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { - if (nRequests == 0) + if (nRequests == 0) { strHTML += tr(", has not been successfully broadcast yet"); - else if (nRequests > 0) + } else if (nRequests > 0) { strHTML += tr(", broadcast through %n node(s)", "", nRequests); + } } strHTML += "
"; strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
"; // // From // if (wtx.IsCoinBase()) { strHTML += "" + tr("Source") + ": " + tr("Generated") + "
"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
"; } else { // Offline transaction if (nNet > Amount(0)) { // Credit CTxDestination address = DecodeDestination(rec->address, wallet->chainParams); if (IsValidDestination(address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; strHTML += "" + tr("To") + ": "; strHTML += GUIUtil::HtmlEscape(rec->address); QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only"); - if (!wallet->mapAddressBook[address].name.empty()) + if (!wallet->mapAddressBook[address].name.empty()) { strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape( wallet->mapAddressBook[address].name) + ")"; - else + } else { strHTML += " (" + addressOwned + ")"; + } strHTML += "
"; } } } } // // To // if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) { // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "" + tr("To") + ": "; CTxDestination dest = DecodeDestination(strAddress, wallet->chainParams); if (wallet->mapAddressBook.count(dest) && - !wallet->mapAddressBook[dest].name.empty()) + !wallet->mapAddressBook[dest].name.empty()) { strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; + } strHTML += GUIUtil::HtmlEscape(strAddress) + "
"; } // // Amount // if (wtx.IsCoinBase() && nCredit == Amount(0)) { // // Coinbase // Amount nUnmatured(0); for (const CTxOut &txout : wtx.tx->vout) { nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); } strHTML += "" + tr("Credit") + ": "; - if (wtx.IsInMainChain()) + if (wtx.IsInMainChain()) { strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured) + " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; - else + } else { strHTML += "(" + tr("not accepted") + ")"; + } strHTML += "
"; } else if (nNet > Amount(0)) { // // Credit // strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "
"; } else { isminetype fAllFromMe = ISMINE_SPENDABLE; for (const CTxIn &txin : wtx.tx->vin) { isminetype mine = wallet->IsMine(txin); - if (fAllFromMe > mine) fAllFromMe = mine; + if (fAllFromMe > mine) { + fAllFromMe = mine; + } } isminetype fAllToMe = ISMINE_SPENDABLE; for (const CTxOut &txout : wtx.tx->vout) { isminetype mine = wallet->IsMine(txout); - if (fAllToMe > mine) fAllToMe = mine; + if (fAllToMe > mine) { + fAllToMe = mine; + } } if (fAllFromMe) { - if (fAllFromMe & ISMINE_WATCH_ONLY) + if (fAllFromMe & ISMINE_WATCH_ONLY) { strHTML += "" + tr("From") + ": " + tr("watch-only") + "
"; + } // // Debit // for (const CTxOut &txout : wtx.tx->vout) { // Ignore change isminetype toSelf = wallet->IsMine(txout); if ((toSelf == ISMINE_SPENDABLE) && - (fAllFromMe == ISMINE_SPENDABLE)) + (fAllFromMe == ISMINE_SPENDABLE)) { continue; + } if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) { // Offline transaction CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address)) { strHTML += "" + tr("To") + ": "; if (wallet->mapAddressBook.count(address) && - !wallet->mapAddressBook[address].name.empty()) + !wallet->mapAddressBook[address].name.empty()) { strHTML += GUIUtil::HtmlEscape( wallet->mapAddressBook[address].name) + " "; + } strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); - if (toSelf == ISMINE_SPENDABLE) + if (toSelf == ISMINE_SPENDABLE) { strHTML += " (own address)"; - else if (toSelf & ISMINE_WATCH_ONLY) + } else if (toSelf & ISMINE_WATCH_ONLY) { strHTML += " (watch-only)"; + } strHTML += "
"; } } strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -1 * txout.nValue) + "
"; - if (toSelf) + if (toSelf) { strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "
"; + } } if (fAllToMe) { // Payment to self Amount nChange = wtx.GetChange(); Amount nValue = nCredit - nChange; strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -1 * nValue) + "
"; strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; } Amount nTxFee = nDebit - wtx.tx->GetValueOut(); if (nTxFee > Amount(0)) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -1 * nTxFee) + "
"; } else { // // Mixed debit transaction // for (const CTxIn &txin : wtx.tx->vin) { - if (wallet->IsMine(txin)) + if (wallet->IsMine(txin)) { strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit( unit, -1 * wallet->GetDebit(txin, ISMINE_ALL)) + "
"; + } } for (const CTxOut &txout : wtx.tx->vout) { - if (wallet->IsMine(txout)) + if (wallet->IsMine(txout)) { strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit( unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; + } } } } strHTML += "" + tr("Net amount") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "
"; // // Message // - if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) + if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) { strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
"; - if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) + } + if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) { strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
"; + } strHTML += "" + tr("Transaction ID") + ": " + rec->getTxID() + "
"; strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.tx->GetTotalSize()) + " bytes
"; strHTML += "" + tr("Output index") + ": " + QString::number(rec->getOutputIndex()) + "
"; // Message from normal bitcoincash:URI (bitcoincash:123...?message=example) for (const std::pair &r : wtx.vOrderForm) { - if (r.first == "Message") + if (r.first == "Message") { strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(r.second, true) + "
"; + } } // // PaymentRequest info: // for (const std::pair &r : wtx.vOrderForm) { if (r.first == "PaymentRequest") { PaymentRequestPlus req; req.parse( QByteArray::fromRawData(r.second.data(), r.second.size())); QString merchant; - if (req.getMerchant(PaymentServer::getCertStore(), merchant)) + if (req.getMerchant(PaymentServer::getCertStore(), merchant)) { strHTML += "" + tr("Merchant") + ": " + GUIUtil::HtmlEscape(merchant) + "
"; + } } } if (wtx.IsCoinBase()) { quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; strHTML += "
" + 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)) + "
"; } // // Debug view // if (gArgs.GetBoolArg("-debug", false)) { strHTML += "

" + tr("Debug information") + "

"; for (const CTxIn &txin : wtx.tx->vin) { - if (wallet->IsMine(txin)) + if (wallet->IsMine(txin)) { strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit( unit, -1 * wallet->GetDebit(txin, ISMINE_ALL)) + "
"; + } } for (const CTxOut &txout : wtx.tx->vout) { - if (wallet->IsMine(txout)) + if (wallet->IsMine(txout)) { strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit( unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; + } } strHTML += "
" + tr("Transaction") + ":
"; strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true); strHTML += "
" + tr("Inputs") + ":"; strHTML += "
    "; for (const CTxIn &txin : wtx.tx->vin) { COutPoint prevout = txin.prevout; Coin prev; if (pcoinsTip->GetCoin(prevout, prev)) { strHTML += "
  • "; const CTxOut &vout = prev.GetTxOut(); CTxDestination address; if (ExtractDestination(vout.scriptPubKey, address)) { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) { strHTML += GUIUtil::HtmlEscape( wallet->mapAddressBook[address].name) + " "; } strHTML += QString::fromStdString(EncodeDestination(address)); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "
  • "; strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + ""; } } strHTML += "
"; } strHTML += "
"; return strHTML; }