Changeset View
Changeset View
Standalone View
Standalone View
src/qt/transactionrecord.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "transactionrecord.h" | #include "transactionrecord.h" | ||||
#include "chain.h" | #include "chain.h" | ||||
#include "consensus/consensus.h" | #include "consensus/consensus.h" | ||||
#include "dstencode.h" | #include "dstencode.h" | ||||
#include "interface/wallet.h" | |||||
#include "timedata.h" | #include "timedata.h" | ||||
#include "validation.h" | #include "validation.h" | ||||
#include "wallet/finaltx.h" | #include "wallet/finaltx.h" | ||||
#include "wallet/wallet.h" | |||||
#include <cstdint> | #include <cstdint> | ||||
/** | /** | ||||
* Return positive answer if transaction should be shown in list. | * Return positive answer if transaction should be shown in list. | ||||
*/ | */ | ||||
bool TransactionRecord::showTransaction(const CWalletTx &wtx) { | bool TransactionRecord::showTransaction() { | ||||
// There are currently no cases where we hide transactions, but we may want | // There are currently no cases where we hide transactions, but we may want | ||||
// to use this in the future for things like RBF. | // to use this in the future for things like RBF. | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Decompose CWallet transaction to model transaction records. | * Decompose CWallet transaction to model transaction records. | ||||
*/ | */ | ||||
QList<TransactionRecord> | QList<TransactionRecord> | ||||
TransactionRecord::decomposeTransaction(const CWallet *wallet, | TransactionRecord::decomposeTransaction(const interface::WalletTx &wtx) { | ||||
const CWalletTx &wtx) { | |||||
QList<TransactionRecord> parts; | QList<TransactionRecord> parts; | ||||
int64_t nTime = wtx.GetTxTime(); | int64_t nTime = wtx.time; | ||||
Amount nCredit = wtx.GetCredit(ISMINE_ALL); | Amount nCredit = wtx.credit; | ||||
Amount nDebit = wtx.GetDebit(ISMINE_ALL); | Amount nDebit = wtx.debit; | ||||
Amount nNet = nCredit - nDebit; | Amount nNet = nCredit - nDebit; | ||||
const TxId &txid = wtx.GetId(); | const TxId &txid = wtx.tx->GetId(); | ||||
std::map<std::string, std::string> mapValue = wtx.mapValue; | std::map<std::string, std::string> mapValue = wtx.value_map; | ||||
if (nNet > Amount::zero() || wtx.IsCoinBase()) { | if (nNet > Amount::zero() || wtx.is_coinbase) { | ||||
// | // | ||||
// Credit | // Credit | ||||
// | // | ||||
for (size_t i = 0; i < wtx.tx->vout.size(); i++) { | for (size_t i = 0; i < wtx.tx->vout.size(); i++) { | ||||
const CTxOut &txout = wtx.tx->vout[i]; | const CTxOut &txout = wtx.tx->vout[i]; | ||||
isminetype mine = wallet->IsMine(txout); | isminetype mine = wtx.txout_is_mine[i]; | ||||
if (mine) { | if (mine) { | ||||
TransactionRecord sub(txid, nTime); | TransactionRecord sub(txid, nTime); | ||||
CTxDestination address; | CTxDestination address; | ||||
sub.idx = i; // vout index | sub.idx = i; // vout index | ||||
sub.credit = txout.nValue; | sub.credit = txout.nValue; | ||||
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; | sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; | ||||
if (ExtractDestination(txout.scriptPubKey, address) && | if (wtx.txout_address_is_mine[i]) { | ||||
IsMine(*wallet, address)) { | |||||
// Received by Bitcoin Address | // Received by Bitcoin Address | ||||
sub.type = TransactionRecord::RecvWithAddress; | sub.type = TransactionRecord::RecvWithAddress; | ||||
sub.address = EncodeDestination(address); | sub.address = EncodeDestination(wtx.txout_address[i]); | ||||
} else { | } else { | ||||
// Received by IP connection (deprecated features), or a | // Received by IP connection (deprecated features), or a | ||||
// multisignature or other non-simple transaction | // multisignature or other non-simple transaction | ||||
sub.type = TransactionRecord::RecvFromOther; | sub.type = TransactionRecord::RecvFromOther; | ||||
sub.address = mapValue["from"]; | sub.address = mapValue["from"]; | ||||
} | } | ||||
if (wtx.IsCoinBase()) { | if (wtx.is_coinbase) { | ||||
// Generated | // Generated | ||||
sub.type = TransactionRecord::Generated; | sub.type = TransactionRecord::Generated; | ||||
} | } | ||||
parts.append(sub); | parts.append(sub); | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
bool involvesWatchAddress = false; | bool involvesWatchAddress = false; | ||||
isminetype fAllFromMe = ISMINE_SPENDABLE; | isminetype fAllFromMe = ISMINE_SPENDABLE; | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (isminetype mine : wtx.txin_is_mine) { | ||||
isminetype mine = wallet->IsMine(txin); | if (mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; | ||||
if (mine & ISMINE_WATCH_ONLY) { | if (fAllFromMe > mine) fAllFromMe = mine; | ||||
involvesWatchAddress = true; | |||||
} | |||||
if (fAllFromMe > mine) { | |||||
fAllFromMe = mine; | |||||
} | |||||
} | } | ||||
isminetype fAllToMe = ISMINE_SPENDABLE; | isminetype fAllToMe = ISMINE_SPENDABLE; | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (isminetype mine : wtx.txout_is_mine) { | ||||
isminetype mine = wallet->IsMine(txout); | if (mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; | ||||
if (mine & ISMINE_WATCH_ONLY) { | if (fAllToMe > mine) fAllToMe = mine; | ||||
involvesWatchAddress = true; | |||||
} | |||||
if (fAllToMe > mine) { | |||||
fAllToMe = mine; | |||||
} | |||||
} | } | ||||
if (fAllFromMe && fAllToMe) { | if (fAllFromMe && fAllToMe) { | ||||
// Payment to self | // Payment to self | ||||
Amount nChange = wtx.GetChange(); | Amount nChange = wtx.change; | ||||
parts.append(TransactionRecord( | parts.append(TransactionRecord( | ||||
txid, nTime, TransactionRecord::SendToSelf, "", | txid, nTime, TransactionRecord::SendToSelf, "", | ||||
-1 * (nDebit - nChange), (nCredit - nChange))); | -1 * (nDebit - nChange), (nCredit - nChange))); | ||||
// maybe pass to TransactionRecord as constructor argument | // maybe pass to TransactionRecord as constructor argument | ||||
parts.last().involvesWatchAddress = involvesWatchAddress; | parts.last().involvesWatchAddress = involvesWatchAddress; | ||||
} else if (fAllFromMe) { | } else if (fAllFromMe) { | ||||
// | // | ||||
// Debit | // Debit | ||||
// | // | ||||
Amount nTxFee = nDebit - wtx.tx->GetValueOut(); | Amount nTxFee = nDebit - wtx.tx->GetValueOut(); | ||||
for (size_t nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { | for (size_t nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { | ||||
const CTxOut &txout = wtx.tx->vout[nOut]; | const CTxOut &txout = wtx.tx->vout[nOut]; | ||||
TransactionRecord sub(txid, nTime); | TransactionRecord sub(txid, nTime); | ||||
sub.idx = nOut; | sub.idx = nOut; | ||||
sub.involvesWatchAddress = involvesWatchAddress; | sub.involvesWatchAddress = involvesWatchAddress; | ||||
if (wallet->IsMine(txout)) { | if (wtx.txout_is_mine[nOut]) { | ||||
// Ignore parts sent to self, as this is usually the change | // Ignore parts sent to self, as this is usually the change | ||||
// from a transaction sent back to our own address. | // from a transaction sent back to our own address. | ||||
continue; | continue; | ||||
} | } | ||||
CTxDestination address; | if (!boost::get<CNoDestination>(&wtx.txout_address[nOut])) { | ||||
if (ExtractDestination(txout.scriptPubKey, address)) { | |||||
// Sent to Bitcoin Address | // Sent to Bitcoin Address | ||||
sub.type = TransactionRecord::SendToAddress; | sub.type = TransactionRecord::SendToAddress; | ||||
sub.address = EncodeDestination(address); | sub.address = EncodeDestination(wtx.txout_address[nOut]); | ||||
} else { | } else { | ||||
// Sent to IP, or other non-address transaction like OP_EVAL | // Sent to IP, or other non-address transaction like OP_EVAL | ||||
sub.type = TransactionRecord::SendToOther; | sub.type = TransactionRecord::SendToOther; | ||||
sub.address = mapValue["to"]; | sub.address = mapValue["to"]; | ||||
} | } | ||||
Amount nValue = txout.nValue; | Amount nValue = txout.nValue; | ||||
/* Add fee to first output */ | /* Add fee to first output */ | ||||
Show All 14 Lines | if (nNet > Amount::zero() || wtx.is_coinbase) { | ||||
Amount::zero())); | Amount::zero())); | ||||
parts.last().involvesWatchAddress = involvesWatchAddress; | parts.last().involvesWatchAddress = involvesWatchAddress; | ||||
} | } | ||||
} | } | ||||
return parts; | return parts; | ||||
} | } | ||||
void TransactionRecord::updateStatus(const CWalletTx &wtx) { | void TransactionRecord::updateStatus(const interface::WalletTxStatus &wtx, | ||||
AssertLockHeld(cs_main); | int numBlocks, int64_t adjustedTime) { | ||||
// Determine transaction status | // Determine transaction status | ||||
// Find the block the tx is in | |||||
const CBlockIndex *pindex = LookupBlockIndex(wtx.hashBlock); | |||||
// Sort order, unrecorded transactions sort to the top | // Sort order, unrecorded transactions sort to the top | ||||
status.sortKey = | status.sortKey = strprintf("%010d-%01d-%010u-%03d", wtx.block_height, | ||||
strprintf("%010d-%01d-%010u-%03d", | wtx.is_coinbase ? 1 : 0, wtx.time_received, idx); | ||||
(pindex ? pindex->nHeight : std::numeric_limits<int>::max()), | status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0); | ||||
(wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx); | status.depth = wtx.depth_in_main_chain; | ||||
status.countsForBalance = | status.cur_num_blocks = numBlocks; | ||||
wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); | |||||
status.depth = wtx.GetDepthInMainChain(); | |||||
status.cur_num_blocks = chainActive.Height(); | |||||
if (!CheckFinalTx(*wtx.tx)) { | if (!wtx.is_final) { | ||||
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) { | if (wtx.lock_time < LOCKTIME_THRESHOLD) { | ||||
status.status = TransactionStatus::OpenUntilBlock; | status.status = TransactionStatus::OpenUntilBlock; | ||||
status.open_for = wtx.tx->nLockTime - chainActive.Height(); | status.open_for = wtx.lock_time - numBlocks; | ||||
} else { | } else { | ||||
status.status = TransactionStatus::OpenUntilDate; | status.status = TransactionStatus::OpenUntilDate; | ||||
status.open_for = wtx.tx->nLockTime; | status.open_for = wtx.lock_time; | ||||
} | } | ||||
} else if (type == TransactionRecord::Generated) { | } else if (type == TransactionRecord::Generated) { | ||||
// For generated transactions, determine maturity | // For generated transactions, determine maturity | ||||
if (wtx.GetBlocksToMaturity() > 0) { | if (wtx.blocks_to_maturity > 0) { | ||||
status.status = TransactionStatus::Immature; | status.status = TransactionStatus::Immature; | ||||
if (wtx.IsInMainChain()) { | if (wtx.is_in_main_chain) { | ||||
status.matures_in = wtx.GetBlocksToMaturity(); | status.matures_in = wtx.blocks_to_maturity; | ||||
// Check if the block was requested by anyone | // Check if the block was requested by anyone | ||||
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && | if (adjustedTime - wtx.time_received > 2 * 60 && | ||||
wtx.GetRequestCount() == 0) { | wtx.request_count == 0) { | ||||
status.status = TransactionStatus::MaturesWarning; | status.status = TransactionStatus::MaturesWarning; | ||||
} | } | ||||
} else { | } else { | ||||
status.status = TransactionStatus::NotAccepted; | status.status = TransactionStatus::NotAccepted; | ||||
} | } | ||||
} else { | } else { | ||||
status.status = TransactionStatus::Confirmed; | status.status = TransactionStatus::Confirmed; | ||||
} | } | ||||
} else { | } else { | ||||
if (status.depth < 0) { | if (status.depth < 0) { | ||||
status.status = TransactionStatus::Conflicted; | status.status = TransactionStatus::Conflicted; | ||||
} else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && | } else if (adjustedTime - wtx.time_received > 2 * 60 && | ||||
wtx.GetRequestCount() == 0) { | wtx.request_count == 0) { | ||||
status.status = TransactionStatus::Offline; | status.status = TransactionStatus::Offline; | ||||
} else if (status.depth == 0) { | } else if (status.depth == 0) { | ||||
status.status = TransactionStatus::Unconfirmed; | status.status = TransactionStatus::Unconfirmed; | ||||
if (wtx.isAbandoned()) { | if (wtx.is_abandoned) { | ||||
status.status = TransactionStatus::Abandoned; | status.status = TransactionStatus::Abandoned; | ||||
} | } | ||||
} else if (status.depth < RecommendedNumConfirmations) { | } else if (status.depth < RecommendedNumConfirmations) { | ||||
status.status = TransactionStatus::Confirming; | status.status = TransactionStatus::Confirming; | ||||
} else { | } else { | ||||
status.status = TransactionStatus::Confirmed; | status.status = TransactionStatus::Confirmed; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool TransactionRecord::statusUpdateNeeded() const { | bool TransactionRecord::statusUpdateNeeded(int numBlocks) const { | ||||
AssertLockHeld(cs_main); | return status.cur_num_blocks != numBlocks; | ||||
return status.cur_num_blocks != chainActive.Height(); | |||||
} | } | ||||
QString TransactionRecord::getTxID() const { | QString TransactionRecord::getTxID() const { | ||||
return QString::fromStdString(txid.ToString()); | return QString::fromStdString(txid.ToString()); | ||||
} | } | ||||
int TransactionRecord::getOutputIndex() const { | int TransactionRecord::getOutputIndex() const { | ||||
return idx; | return idx; | ||||
} | } |