neobytes/src/qt/transactionrecord.cpp

360 lines
12 KiB
C++
Raw Normal View History

// Copyright (c) 2011-2015 The Bitcoin Core developers
2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2014-12-13 05:09:33 +01:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "transactionrecord.h"
#include "base58.h"
#include "consensus/consensus.h"
#include "validation.h"
#include "timedata.h"
#include "wallet/wallet.h"
2015-02-09 16:03:37 +01:00
#include "instantx.h"
#include "privatesend.h"
#include <stdint.h>
#include <boost/foreach.hpp>
/* Return positive answer if transaction should be shown in list.
*/
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
if (wtx.IsCoinBase())
{
// Ensures we show generated coins / mined transactions at depth 1
if (!wtx.IsInMainChain())
{
return false;
}
}
return true;
}
/*
* Decompose CWallet transaction to model transaction records.
*/
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime();
2014-07-26 21:05:11 +02:00
CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
2014-04-23 00:46:19 +02:00
CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
CAmount nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash();
std::map<std::string, std::string> mapValue = wtx.mapValue;
if (nNet > 0 || wtx.IsCoinBase())
{
//
// Credit
//
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
const CTxOut& txout = wtx.tx->vout[i];
isminetype mine = wallet->IsMine(txout);
if(mine)
{
TransactionRecord sub(hash, nTime);
CTxDestination address;
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
{
2015-03-18 00:06:58 +01:00
// Received by Dash Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"];
}
if (wtx.IsCoinBase())
{
// Generated
sub.type = TransactionRecord::Generated;
}
parts.append(sub);
}
}
}
else
{
bool fAllFromMeDenom = true;
int nFromMe = 0;
bool involvesWatchAddress = false;
2014-07-01 11:00:22 +02:00
isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if(wallet->IsMine(txin)) {
fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin.prevout);
nFromMe++;
}
isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
2014-07-01 11:00:22 +02:00
isminetype fAllToMe = ISMINE_SPENDABLE;
bool fAllToMeDenom = true;
int nToMe = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) {
if(wallet->IsMine(txout)) {
fAllToMeDenom = fAllToMeDenom && CPrivateSend::IsDenominatedAmount(txout.nValue);
nToMe++;
}
isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllToMe > mine) fAllToMe = mine;
}
if(fAllFromMeDenom && fAllToMeDenom && nFromMe * nToMe) {
parts.append(TransactionRecord(hash, nTime, TransactionRecord::PrivateSendDenominate, "", -nDebit, nCredit));
2015-04-05 01:45:17 +02:00
parts.last().involvesWatchAddress = false; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe && fAllToMe)
{
// Payment to self
// TODO: this section still not accurate but covers most cases,
// might need some additional work however
TransactionRecord sub(hash, nTime);
// Payment to self by default
sub.type = TransactionRecord::SendToSelf;
sub.address = "";
if(mapValue["DS"] == "1")
{
sub.type = TransactionRecord::PrivateSend;
CTxDestination address;
if (ExtractDestination(wtx.tx->vout[0].scriptPubKey, address))
{
2015-03-18 00:06:58 +01:00
// Sent to Dash Address
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.address = mapValue["to"];
}
}
else
{
sub.idx = parts.size();
if(wtx.tx->vin.size() == 1 && wtx.tx->vout.size() == 1
&& CPrivateSend::IsCollateralAmount(nDebit)
&& CPrivateSend::IsCollateralAmount(nCredit)
&& CPrivateSend::IsCollateralAmount(-nNet))
{
sub.type = TransactionRecord::PrivateSendCollateralPayment;
} else {
for (const auto& txout : wtx.tx->vout) {
if(txout.nValue == CPrivateSend::GetMaxCollateralAmount()) {
sub.type = TransactionRecord::PrivateSendMakeCollaterals;
break;
}
if(CPrivateSend::IsDenominatedAmount(txout.nValue)) {
sub.type = TransactionRecord::PrivateSendCreateDenominations;
break;
}
}
}
}
2014-04-23 00:46:19 +02:00
CAmount nChange = wtx.GetChange();
sub.debit = -(nDebit - nChange);
sub.credit = nCredit - nChange;
parts.append(sub);
2014-06-19 01:42:39 +02:00
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe)
{
//
// Debit
//
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
bool fDone = false;
if(wtx.tx->vin.size() == 1 && wtx.tx->vout.size() == 1
&& CPrivateSend::IsCollateralAmount(nDebit)
&& nCredit == 0 // OP_RETURN
&& CPrivateSend::IsCollateralAmount(-nNet))
{
TransactionRecord sub(hash, nTime);
sub.idx = 0;
sub.type = TransactionRecord::PrivateSendCollateralPayment;
sub.debit = -nDebit;
parts.append(sub);
fDone = true;
}
for (unsigned int nOut = 0; nOut < wtx.tx->vout.size() && !fDone; nOut++)
{
const CTxOut& txout = wtx.tx->vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = nOut;
2014-06-19 01:42:39 +02:00
sub.involvesWatchAddress = involvesWatchAddress;
if(wallet->IsMine(txout))
{
// Ignore parts sent to self, as this is usually the change
// from a transaction sent back to our own address.
continue;
}
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address))
{
2015-03-18 00:06:58 +01:00
// Sent to Dash Address
sub.type = TransactionRecord::SendToAddress;
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.type = TransactionRecord::SendToOther;
sub.address = mapValue["to"];
}
if(mapValue["DS"] == "1")
{
sub.type = TransactionRecord::PrivateSend;
}
2014-04-23 00:46:19 +02:00
CAmount nValue = txout.nValue;
/* Add fee to first output */
if (nTxFee > 0)
{
nValue += nTxFee;
nTxFee = 0;
}
sub.debit = -nValue;
parts.append(sub);
2011-06-07 18:59:01 +02:00
}
}
else
{
//
// Mixed debit transaction, can't break down payees
//
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
parts.last().involvesWatchAddress = involvesWatchAddress;
}
}
return parts;
}
void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
AssertLockHeld(cs_main);
// Determine transaction status
// Find the block the tx is in
CBlockIndex* pindex = NULL;
BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
if (mi != mapBlockIndex.end())
pindex = (*mi).second;
// Sort order, unrecorded transactions sort to the top
status.sortKey = strprintf("%010d-%01d-%010u-%03d",
(pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
(wtx.IsCoinBase() ? 1 : 0),
wtx.nTimeReceived,
idx);
status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
status.depth = wtx.GetDepthInMainChain();
status.cur_num_blocks = chainActive.Height();
2015-02-09 16:03:37 +01:00
status.cur_num_ix_locks = nCompleteTXLocks;
if (!CheckFinalTx(wtx))
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
status.open_for = wtx.tx->nLockTime - chainActive.Height();
2011-06-07 18:59:01 +02:00
}
else
{
status.status = TransactionStatus::OpenUntilDate;
status.open_for = wtx.tx->nLockTime;
}
}
// For generated transactions, determine maturity
else if(type == TransactionRecord::Generated)
{
if (wtx.GetBlocksToMaturity() > 0)
{
status.status = TransactionStatus::Immature;
if (wtx.IsInMainChain())
{
status.matures_in = wtx.GetBlocksToMaturity();
// Check if the block was requested by anyone
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
status.status = TransactionStatus::MaturesWarning;
2011-06-07 18:59:01 +02:00
}
else
{
status.status = TransactionStatus::NotAccepted;
}
2011-06-07 18:59:01 +02:00
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
else
{
status.lockedByInstantSend = wtx.IsLockedByInstantSend();
if (status.depth < 0)
{
status.status = TransactionStatus::Conflicted;
}
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
{
status.status = TransactionStatus::Offline;
}
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
Backport Bitcoin Qt/Gui changes up to 0.14.x part 2 (#1615) * Merge #7506: Use CCoinControl selection in CWallet::FundTransaction d6cc6a1 Use CCoinControl selection in CWallet::FundTransaction (João Barbosa) * Merge #7732: [Qt] Debug window: replace "Build date" with "Datadir" fc737d1 [Qt] remove unused formatBuildDate method (Jonas Schnelli) 4856f1d [Qt] Debug window: replace "Build date" with "Datadir" (Jonas Schnelli) * Merge #7707: [RPC][QT] UI support for abandoned transactions 8efed3b [Qt] Support for abandoned/abandoning transactions (Jonas Schnelli) * Merge #7688: List solvability in listunspent output and improve help c3932b3 List solvability in listunspent output and improve help (Pieter Wuille) * Merge #8006: Qt: Add option to disable the system tray icon 8b0e497 Qt: Add option to hide the system tray icon (Tyler Hardin) * Merge #8073: qt: askpassphrasedialog: Clear pass fields on accept 02ce2a3 qt: askpassphrasedialog: Clear pass fields on accept (Pavel Vasin) * Merge #8231: [Qt] fix a bug where the SplashScreen will not be hidden during startup b3e1348 [Qt] fix a bug where the SplashScreen will not be hidden during startup (Jonas Schnelli) * Merge #8257: Do not ask a UI question from bitcoind 1acf1db Do not ask a UI question from bitcoind (Pieter Wuille) * Merge #8463: [qt] Remove Priority from coincontrol dialog fa8dd78 [qt] Remove Priority from coincontrol dialog (MarcoFalke) * Merge #8678: [Qt][CoinControl] fix UI bug that could result in paying unexpected fee 0480293 [Qt][CoinControl] fix UI bug that could result in paying unexpected fee (Jonas Schnelli) * Merge #8672: Qt: Show transaction size in transaction details window c015634 qt: Adding transaction size to transaction details window (Hampus Sjöberg) \-- merge fix for s/size/total size/ fdf82fb Adding method GetTotalSize() to CTransaction (Hampus Sjöberg) * Merge #8371: [Qt] Add out-of-sync modal info layer 08827df [Qt] modalinfolayer: removed unused comments, renamed signal, code style overhaul (Jonas Schnelli) d8b062e [Qt] only update "amount of blocks left" when the header chain is in-sync (Jonas Schnelli) e3245b4 [Qt] add out-of-sync modal info layer (Jonas Schnelli) e47052f [Qt] ClientModel add method to get the height of the header chain (Jonas Schnelli) a001f18 [Qt] Always pass the numBlocksChanged signal for headers tip changed (Jonas Schnelli) bd44a04 [Qt] make Out-Of-Sync warning icon clickable (Jonas Schnelli) 0904c3c [Refactor] refactor function that forms human readable text out of a timeoffset (Jonas Schnelli) * Merge #8805: Trivial: Grammar and capitalization c9ce17b Trivial: Grammar and capitalization (Derek Miller) * Merge #8885: gui: fix ban from qt console cb78c60 gui: fix ban from qt console (Cory Fields) * Merge #8821: [qt] sync-overlay: Don't block during reindex fa85e86 [qt] sync-overlay: Don't show estimated number of headers left (MarcoFalke) faa4de2 [qt] sync-overlay: Don't block during reindex (MarcoFalke) * Support themes for new transaction_abandoned icon * Fix constructor call to COutput * Merge #7842: RPC: do not print minping time in getpeerinfo when no ping received yet 62a6486 RPC: do not print ping info in getpeerinfo when no ping received yet, fix help (Pavel Janík) * Merge #8918: Qt: Add "Copy URI" to payment request context menu 21f5a63 Qt: Add "Copy URI" to payment request context menu (Luke Dashjr) * Merge #8925: qt: Display minimum ping in debug window. 1724a40 Display minimum ping in debug window. (R E Broadley) * Merge #8972: [Qt] make warnings label selectable (jonasschnelli) ef0c9ee [Qt] make warnings label selectable (Jonas Schnelli) * Make background of warning icon transparent in modaloverlay * Merge #9088: Reduce ambiguity of warning message 77cbbd9 Make warning message about wallet balance possibly being incorrect less ambiguous. (R E Broadley) * Replace Bitcoin with Dash in modal overlay * Remove clicked signals from labelWalletStatus and labelTransactionsStatus As both are really just labels, clicking on those is not possible. This is different in Bitcoin, where these labels are actually buttons. * Pull out modaloverlay show/hide into it's own if/else block and switch to time based check Also don't use masternodeSync.IsBlockchainSynced() for now as it won't report the blockchain being synced before the first block (or other MN data?) arrives. This would otherwise give the impression that sync is being stuck.
2017-09-09 09:04:02 +02:00
if (wtx.isAbandoned())
status.status = TransactionStatus::Abandoned;
}
else if (status.depth < RecommendedNumConfirmations)
{
status.status = TransactionStatus::Confirming;
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
}
bool TransactionRecord::statusUpdateNeeded()
{
AssertLockHeld(cs_main);
2015-02-09 16:03:37 +01:00
return status.cur_num_blocks != chainActive.Height() || status.cur_num_ix_locks != nCompleteTXLocks;
}
QString TransactionRecord::getTxID() const
{
return QString::fromStdString(hash.ToString());
}
int TransactionRecord::getOutputIndex() const
{
return idx;
}