0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-14 11:26:09 -05:00
bitcoin-bitcoin-core/src/qt/transactionrecord.cpp
Hennadii Stepanov b000ed5ee5
Merge bitcoin-core/gui#119: Replace send-to-self with dual send+receive entries
099dbe4224 GUI: TransactionRecord: When time/index/etc match, sort send before receive (Luke Dashjr)
2d182f77cd Bugfix: Ignore ischange flag when we're not the sender (Luke Dashjr)
71fbdb7f40 GUI: Remove SendToSelf TransactionRecord type (Luke Dashjr)
f3fbe99fcf GUI: TransactionRecord: Refactor to turn send-to-self into send+receive pairs (Luke Dashjr)
b9765ba1d6 GUI: TransactionRecord: Use "any from me" as the criteria for deciding whether a transaction is a send or receive (Luke Dashjr)

Pull request description:

  Makes the GUI transaction list more like the RPC, and IMO clearer in general.

  As a side effect, this also fixes the GUI entries when a transaction is a net profit to us, but some inputs were also from us.

  Originally https://github.com/bitcoin/bitcoin/pull/15115

  Has Concept ACKs from @*Empact @*jonasschnelli

ACKs for top commit:
  hebasto:
    ACK 099dbe4224.

Tree-SHA512: 7d581add2f59431aa019126d54232a1f15723def5147d7a1b672e9b6d525b6e5a944cc437701aa1bd5bd0fbe557a3d1f4b239337f42bdba4fe1d3960442d0e3b
2023-09-22 18:29:51 +01:00

234 lines
7 KiB
C++

// Copyright (c) 2011-2022 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 <qt/transactionrecord.h>
#include <chain.h>
#include <interfaces/wallet.h>
#include <key_io.h>
#include <wallet/types.h>
#include <stdint.h>
#include <QDateTime>
using wallet::ISMINE_NO;
using wallet::ISMINE_SPENDABLE;
using wallet::ISMINE_WATCH_ONLY;
using wallet::isminetype;
/* Return positive answer if transaction should be shown in list.
*/
bool TransactionRecord::showTransaction()
{
// There are currently no cases where we hide transactions, but
// we may want to use this in the future for things like RBF.
return true;
}
/*
* Decompose CWallet transaction to model transaction records.
*/
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interfaces::WalletTx& wtx)
{
QList<TransactionRecord> parts;
int64_t nTime = wtx.time;
CAmount nCredit = wtx.credit;
CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
uint256 hash = wtx.tx->GetHash();
std::map<std::string, std::string> mapValue = wtx.value_map;
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
bool any_from_me = false;
if (wtx.is_coinbase) {
fAllFromMe = ISMINE_NO;
} else {
for (const isminetype mine : wtx.txin_is_mine)
{
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
if (mine) any_from_me = true;
}
}
if (fAllFromMe || !any_from_me) {
for (const isminetype mine : wtx.txout_is_mine)
{
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
}
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
const CTxOut& txout = wtx.tx->vout[i];
if (fAllFromMe) {
// Change is only really possible if we're the sender
// Otherwise, someone just sent bitcoins to a change address, which should be shown
if (wtx.txout_is_change[i]) {
continue;
}
//
// Debit
//
TransactionRecord sub(hash, nTime);
sub.idx = i;
sub.involvesWatchAddress = involvesWatchAddress;
if (!std::get_if<CNoDestination>(&wtx.txout_address[i]))
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
sub.address = EncodeDestination(wtx.txout_address[i]);
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.type = TransactionRecord::SendToOther;
sub.address = mapValue["to"];
}
CAmount nValue = txout.nValue;
/* Add fee to first output */
if (nTxFee > 0)
{
nValue += nTxFee;
nTxFee = 0;
}
sub.debit = -nValue;
parts.append(sub);
}
isminetype mine = wtx.txout_is_mine[i];
if(mine)
{
//
// Credit
//
TransactionRecord sub(hash, nTime);
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
if (wtx.txout_address_is_mine[i])
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = EncodeDestination(wtx.txout_address[i]);
}
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.is_coinbase)
{
// Generated
sub.type = TransactionRecord::Generated;
}
parts.append(sub);
}
}
} 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 interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time)
{
// Determine transaction status
// Sort order, unrecorded transactions sort to the top
int typesort;
switch (type) {
case SendToAddress: case SendToOther:
typesort = 2; break;
case RecvWithAddress: case RecvFromOther:
typesort = 3; break;
default:
typesort = 9;
}
status.sortKey = strprintf("%010d-%01d-%010u-%03d-%d",
wtx.block_height,
wtx.is_coinbase ? 1 : 0,
wtx.time_received,
idx,
typesort);
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
status.depth = wtx.depth_in_main_chain;
status.m_cur_block_hash = block_hash;
// For generated transactions, determine maturity
if (type == TransactionRecord::Generated) {
if (wtx.blocks_to_maturity > 0)
{
status.status = TransactionStatus::Immature;
if (wtx.is_in_main_chain)
{
status.matures_in = wtx.blocks_to_maturity;
}
else
{
status.status = TransactionStatus::NotAccepted;
}
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
else
{
if (status.depth < 0)
{
status.status = TransactionStatus::Conflicted;
}
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
if (wtx.is_abandoned)
status.status = TransactionStatus::Abandoned;
}
else if (status.depth < RecommendedNumConfirmations)
{
status.status = TransactionStatus::Confirming;
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
status.needsUpdate = false;
}
bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
{
assert(!block_hash.IsNull());
return status.m_cur_block_hash != block_hash || status.needsUpdate;
}
QString TransactionRecord::getTxHash() const
{
return QString::fromStdString(hash.ToString());
}
int TransactionRecord::getOutputIndex() const
{
return idx;
}