mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 23:07:59 +01:00
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: ACK099dbe4224
. Tree-SHA512: 7d581add2f59431aa019126d54232a1f15723def5147d7a1b672e9b6d525b6e5a944cc437701aa1bd5bd0fbe557a3d1f4b239337f42bdba4fe1d3960442d0e3b
This commit is contained in:
commit
b000ed5ee5
6 changed files with 90 additions and 101 deletions
|
@ -393,6 +393,7 @@ struct WalletTx
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
std::vector<wallet::isminetype> txin_is_mine;
|
std::vector<wallet::isminetype> txin_is_mine;
|
||||||
std::vector<wallet::isminetype> txout_is_mine;
|
std::vector<wallet::isminetype> txout_is_mine;
|
||||||
|
std::vector<bool> txout_is_change;
|
||||||
std::vector<CTxDestination> txout_address;
|
std::vector<CTxDestination> txout_address;
|
||||||
std::vector<wallet::isminetype> txout_address_is_mine;
|
std::vector<wallet::isminetype> txout_address_is_mine;
|
||||||
CAmount credit;
|
CAmount credit;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
|
using wallet::ISMINE_NO;
|
||||||
using wallet::ISMINE_SPENDABLE;
|
using wallet::ISMINE_SPENDABLE;
|
||||||
using wallet::ISMINE_WATCH_ONLY;
|
using wallet::ISMINE_WATCH_ONLY;
|
||||||
using wallet::isminetype;
|
using wallet::isminetype;
|
||||||
|
@ -39,17 +40,79 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
uint256 hash = wtx.tx->GetHash();
|
uint256 hash = wtx.tx->GetHash();
|
||||||
std::map<std::string, std::string> mapValue = wtx.value_map;
|
std::map<std::string, std::string> mapValue = wtx.value_map;
|
||||||
|
|
||||||
if (nNet > 0 || wtx.is_coinbase)
|
bool involvesWatchAddress = false;
|
||||||
{
|
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
||||||
//
|
bool any_from_me = false;
|
||||||
// Credit
|
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++)
|
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||||
{
|
{
|
||||||
const CTxOut& txout = wtx.tx->vout[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];
|
isminetype mine = wtx.txout_is_mine[i];
|
||||||
if(mine)
|
if(mine)
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// Credit
|
||||||
|
//
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
TransactionRecord sub(hash, nTime);
|
||||||
sub.idx = i; // vout index
|
sub.idx = i; // vout index
|
||||||
sub.credit = txout.nValue;
|
sub.credit = txout.nValue;
|
||||||
|
@ -75,91 +138,12 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
parts.append(sub);
|
parts.append(sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
//
|
||||||
{
|
// Mixed debit transaction, can't break down payees
|
||||||
bool involvesWatchAddress = false;
|
//
|
||||||
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
||||||
for (const isminetype mine : wtx.txin_is_mine)
|
parts.last().involvesWatchAddress = involvesWatchAddress;
|
||||||
{
|
|
||||||
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
|
|
||||||
if(fAllFromMe > mine) fAllFromMe = mine;
|
|
||||||
}
|
|
||||||
|
|
||||||
isminetype fAllToMe = ISMINE_SPENDABLE;
|
|
||||||
for (const isminetype mine : wtx.txout_is_mine)
|
|
||||||
{
|
|
||||||
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
|
|
||||||
if(fAllToMe > mine) fAllToMe = mine;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fAllFromMe && fAllToMe)
|
|
||||||
{
|
|
||||||
// Payment to self
|
|
||||||
std::string address;
|
|
||||||
for (auto it = wtx.txout_address.begin(); it != wtx.txout_address.end(); ++it) {
|
|
||||||
if (it != wtx.txout_address.begin()) address += ", ";
|
|
||||||
address += EncodeDestination(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
CAmount nChange = wtx.change;
|
|
||||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, address, -(nDebit - nChange), nCredit - nChange));
|
|
||||||
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
|
|
||||||
}
|
|
||||||
else if (fAllFromMe)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Debit
|
|
||||||
//
|
|
||||||
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
|
|
||||||
|
|
||||||
for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
|
|
||||||
{
|
|
||||||
const CTxOut& txout = wtx.tx->vout[nOut];
|
|
||||||
TransactionRecord sub(hash, nTime);
|
|
||||||
sub.idx = nOut;
|
|
||||||
sub.involvesWatchAddress = involvesWatchAddress;
|
|
||||||
|
|
||||||
if(wtx.txout_is_mine[nOut])
|
|
||||||
{
|
|
||||||
// Ignore parts sent to self, as this is usually the change
|
|
||||||
// from a transaction sent back to our own address.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::get_if<CNoDestination>(&wtx.txout_address[nOut]))
|
|
||||||
{
|
|
||||||
// Sent to Bitcoin Address
|
|
||||||
sub.type = TransactionRecord::SendToAddress;
|
|
||||||
sub.address = EncodeDestination(wtx.txout_address[nOut]);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Mixed debit transaction, can't break down payees
|
|
||||||
//
|
|
||||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
|
||||||
parts.last().involvesWatchAddress = involvesWatchAddress;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
|
@ -170,11 +154,21 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
|
||||||
// Determine transaction status
|
// Determine transaction status
|
||||||
|
|
||||||
// Sort order, unrecorded transactions sort to the top
|
// Sort order, unrecorded transactions sort to the top
|
||||||
status.sortKey = strprintf("%010d-%01d-%010u-%03d",
|
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.block_height,
|
||||||
wtx.is_coinbase ? 1 : 0,
|
wtx.is_coinbase ? 1 : 0,
|
||||||
wtx.time_received,
|
wtx.time_received,
|
||||||
idx);
|
idx,
|
||||||
|
typesort);
|
||||||
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
|
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
|
||||||
status.depth = wtx.depth_in_main_chain;
|
status.depth = wtx.depth_in_main_chain;
|
||||||
status.m_cur_block_hash = block_hash;
|
status.m_cur_block_hash = block_hash;
|
||||||
|
|
|
@ -69,7 +69,6 @@ public:
|
||||||
SendToOther,
|
SendToOther,
|
||||||
RecvWithAddress,
|
RecvWithAddress,
|
||||||
RecvFromOther,
|
RecvFromOther,
|
||||||
SendToSelf
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Number of confirmation recommended for accepting a transaction */
|
/** Number of confirmation recommended for accepting a transaction */
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <interfaces/handler.h>
|
#include <interfaces/handler.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -377,8 +378,6 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
|
||||||
case TransactionRecord::SendToAddress:
|
case TransactionRecord::SendToAddress:
|
||||||
case TransactionRecord::SendToOther:
|
case TransactionRecord::SendToOther:
|
||||||
return tr("Sent to");
|
return tr("Sent to");
|
||||||
case TransactionRecord::SendToSelf:
|
|
||||||
return tr("Payment to yourself");
|
|
||||||
case TransactionRecord::Generated:
|
case TransactionRecord::Generated:
|
||||||
return tr("Mined");
|
return tr("Mined");
|
||||||
default:
|
default:
|
||||||
|
@ -421,8 +420,6 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b
|
||||||
return lookupAddress(wtx->address, tooltip) + watchAddress;
|
return lookupAddress(wtx->address, tooltip) + watchAddress;
|
||||||
case TransactionRecord::SendToOther:
|
case TransactionRecord::SendToOther:
|
||||||
return QString::fromStdString(wtx->address) + watchAddress;
|
return QString::fromStdString(wtx->address) + watchAddress;
|
||||||
case TransactionRecord::SendToSelf:
|
|
||||||
return lookupAddress(wtx->address, tooltip) + watchAddress;
|
|
||||||
default:
|
default:
|
||||||
return tr("(n/a)") + watchAddress;
|
return tr("(n/a)") + watchAddress;
|
||||||
}
|
}
|
||||||
|
@ -441,8 +438,6 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
|
||||||
if(label.isEmpty())
|
if(label.isEmpty())
|
||||||
return COLOR_BAREADDRESS;
|
return COLOR_BAREADDRESS;
|
||||||
} break;
|
} break;
|
||||||
case TransactionRecord::SendToSelf:
|
|
||||||
return COLOR_BAREADDRESS;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -560,7 +555,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
|
||||||
case Status:
|
case Status:
|
||||||
return QString::fromStdString(rec->status.sortKey);
|
return QString::fromStdString(rec->status.sortKey);
|
||||||
case Date:
|
case Date:
|
||||||
return rec->time;
|
return QString::fromStdString(strprintf("%020s-%s", rec->time, rec->status.sortKey));
|
||||||
case Type:
|
case Type:
|
||||||
return formatTxType(rec);
|
return formatTxType(rec);
|
||||||
case Watchonly:
|
case Watchonly:
|
||||||
|
|
|
@ -91,7 +91,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
|
||||||
TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
|
TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
|
||||||
typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
|
typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
|
||||||
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
|
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
|
||||||
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
|
|
||||||
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
|
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
|
||||||
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
|
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
|
||||||
result.txout_address_is_mine.reserve(wtx.tx->vout.size());
|
result.txout_address_is_mine.reserve(wtx.tx->vout.size());
|
||||||
for (const auto& txout : wtx.tx->vout) {
|
for (const auto& txout : wtx.tx->vout) {
|
||||||
result.txout_is_mine.emplace_back(wallet.IsMine(txout));
|
result.txout_is_mine.emplace_back(wallet.IsMine(txout));
|
||||||
|
result.txout_is_change.push_back(OutputIsChange(wallet, txout));
|
||||||
result.txout_address.emplace_back();
|
result.txout_address.emplace_back();
|
||||||
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
|
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
|
||||||
wallet.IsMine(result.txout_address.back()) :
|
wallet.IsMine(result.txout_address.back()) :
|
||||||
|
|
Loading…
Add table
Reference in a new issue