Skip to content

Commit b000ed5

Browse files
committed
Merge #119: Replace send-to-self with dual send+receive entries
099dbe4 GUI: TransactionRecord: When time/index/etc match, sort send before receive (Luke Dashjr) 2d182f7 Bugfix: Ignore ischange flag when we're not the sender (Luke Dashjr) 71fbdb7 GUI: Remove SendToSelf TransactionRecord type (Luke Dashjr) f3fbe99 GUI: TransactionRecord: Refactor to turn send-to-self into send+receive pairs (Luke Dashjr) b9765ba 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 bitcoin/bitcoin#15115 Has Concept ACKs from @*Empact @*jonasschnelli ACKs for top commit: hebasto: ACK 099dbe4. Tree-SHA512: 7d581add2f59431aa019126d54232a1f15723def5147d7a1b672e9b6d525b6e5a944cc437701aa1bd5bd0fbe557a3d1f4b239337f42bdba4fe1d3960442d0e3b
2 parents bce7b08 + 099dbe4 commit b000ed5

6 files changed

+81
-92
lines changed

src/interfaces/wallet.h

+1
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ struct WalletTx
393393
CTransactionRef tx;
394394
std::vector<wallet::isminetype> txin_is_mine;
395395
std::vector<wallet::isminetype> txout_is_mine;
396+
std::vector<bool> txout_is_change;
396397
std::vector<CTxDestination> txout_address;
397398
std::vector<wallet::isminetype> txout_address_is_mine;
398399
CAmount credit;

src/qt/transactionrecord.cpp

+77-83
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <QDateTime>
1515

16+
using wallet::ISMINE_NO;
1617
using wallet::ISMINE_SPENDABLE;
1718
using wallet::ISMINE_WATCH_ONLY;
1819
using wallet::isminetype;
@@ -39,99 +40,52 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
3940
uint256 hash = wtx.tx->GetHash();
4041
std::map<std::string, std::string> mapValue = wtx.value_map;
4142

42-
if (nNet > 0 || wtx.is_coinbase)
43-
{
44-
//
45-
// Credit
46-
//
47-
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
48-
{
49-
const CTxOut& txout = wtx.tx->vout[i];
50-
isminetype mine = wtx.txout_is_mine[i];
51-
if(mine)
52-
{
53-
TransactionRecord sub(hash, nTime);
54-
sub.idx = i; // vout index
55-
sub.credit = txout.nValue;
56-
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
57-
if (wtx.txout_address_is_mine[i])
58-
{
59-
// Received by Bitcoin Address
60-
sub.type = TransactionRecord::RecvWithAddress;
61-
sub.address = EncodeDestination(wtx.txout_address[i]);
62-
}
63-
else
64-
{
65-
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
66-
sub.type = TransactionRecord::RecvFromOther;
67-
sub.address = mapValue["from"];
68-
}
69-
if (wtx.is_coinbase)
70-
{
71-
// Generated
72-
sub.type = TransactionRecord::Generated;
73-
}
74-
75-
parts.append(sub);
76-
}
77-
}
78-
}
79-
else
80-
{
81-
bool involvesWatchAddress = false;
82-
isminetype fAllFromMe = ISMINE_SPENDABLE;
43+
bool involvesWatchAddress = false;
44+
isminetype fAllFromMe = ISMINE_SPENDABLE;
45+
bool any_from_me = false;
46+
if (wtx.is_coinbase) {
47+
fAllFromMe = ISMINE_NO;
48+
} else {
8349
for (const isminetype mine : wtx.txin_is_mine)
8450
{
8551
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
8652
if(fAllFromMe > mine) fAllFromMe = mine;
53+
if (mine) any_from_me = true;
8754
}
55+
}
8856

89-
isminetype fAllToMe = ISMINE_SPENDABLE;
57+
if (fAllFromMe || !any_from_me) {
9058
for (const isminetype mine : wtx.txout_is_mine)
9159
{
9260
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
93-
if(fAllToMe > mine) fAllToMe = mine;
9461
}
9562

96-
if (fAllFromMe && fAllToMe)
97-
{
98-
// Payment to self
99-
std::string address;
100-
for (auto it = wtx.txout_address.begin(); it != wtx.txout_address.end(); ++it) {
101-
if (it != wtx.txout_address.begin()) address += ", ";
102-
address += EncodeDestination(*it);
103-
}
63+
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
10464

105-
CAmount nChange = wtx.change;
106-
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, address, -(nDebit - nChange), nCredit - nChange));
107-
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
108-
}
109-
else if (fAllFromMe)
65+
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
11066
{
111-
//
112-
// Debit
113-
//
114-
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
115-
116-
for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
117-
{
118-
const CTxOut& txout = wtx.tx->vout[nOut];
119-
TransactionRecord sub(hash, nTime);
120-
sub.idx = nOut;
121-
sub.involvesWatchAddress = involvesWatchAddress;
67+
const CTxOut& txout = wtx.tx->vout[i];
12268

123-
if(wtx.txout_is_mine[nOut])
124-
{
125-
// Ignore parts sent to self, as this is usually the change
126-
// from a transaction sent back to our own address.
69+
if (fAllFromMe) {
70+
// Change is only really possible if we're the sender
71+
// Otherwise, someone just sent bitcoins to a change address, which should be shown
72+
if (wtx.txout_is_change[i]) {
12773
continue;
12874
}
12975

130-
if (!std::get_if<CNoDestination>(&wtx.txout_address[nOut]))
76+
//
77+
// Debit
78+
//
79+
80+
TransactionRecord sub(hash, nTime);
81+
sub.idx = i;
82+
sub.involvesWatchAddress = involvesWatchAddress;
83+
84+
if (!std::get_if<CNoDestination>(&wtx.txout_address[i]))
13185
{
13286
// Sent to Bitcoin Address
13387
sub.type = TransactionRecord::SendToAddress;
134-
sub.address = EncodeDestination(wtx.txout_address[nOut]);
88+
sub.address = EncodeDestination(wtx.txout_address[i]);
13589
}
13690
else
13791
{
@@ -151,15 +105,45 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
151105

152106
parts.append(sub);
153107
}
108+
109+
isminetype mine = wtx.txout_is_mine[i];
110+
if(mine)
111+
{
112+
//
113+
// Credit
114+
//
115+
116+
TransactionRecord sub(hash, nTime);
117+
sub.idx = i; // vout index
118+
sub.credit = txout.nValue;
119+
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
120+
if (wtx.txout_address_is_mine[i])
121+
{
122+
// Received by Bitcoin Address
123+
sub.type = TransactionRecord::RecvWithAddress;
124+
sub.address = EncodeDestination(wtx.txout_address[i]);
125+
}
126+
else
127+
{
128+
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
129+
sub.type = TransactionRecord::RecvFromOther;
130+
sub.address = mapValue["from"];
131+
}
132+
if (wtx.is_coinbase)
133+
{
134+
// Generated
135+
sub.type = TransactionRecord::Generated;
136+
}
137+
138+
parts.append(sub);
139+
}
154140
}
155-
else
156-
{
157-
//
158-
// Mixed debit transaction, can't break down payees
159-
//
160-
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
161-
parts.last().involvesWatchAddress = involvesWatchAddress;
162-
}
141+
} else {
142+
//
143+
// Mixed debit transaction, can't break down payees
144+
//
145+
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
146+
parts.last().involvesWatchAddress = involvesWatchAddress;
163147
}
164148

165149
return parts;
@@ -170,11 +154,21 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
170154
// Determine transaction status
171155

172156
// Sort order, unrecorded transactions sort to the top
173-
status.sortKey = strprintf("%010d-%01d-%010u-%03d",
157+
int typesort;
158+
switch (type) {
159+
case SendToAddress: case SendToOther:
160+
typesort = 2; break;
161+
case RecvWithAddress: case RecvFromOther:
162+
typesort = 3; break;
163+
default:
164+
typesort = 9;
165+
}
166+
status.sortKey = strprintf("%010d-%01d-%010u-%03d-%d",
174167
wtx.block_height,
175168
wtx.is_coinbase ? 1 : 0,
176169
wtx.time_received,
177-
idx);
170+
idx,
171+
typesort);
178172
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
179173
status.depth = wtx.depth_in_main_chain;
180174
status.m_cur_block_hash = block_hash;

src/qt/transactionrecord.h

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ class TransactionRecord
6969
SendToOther,
7070
RecvWithAddress,
7171
RecvFromOther,
72-
SendToSelf
7372
};
7473

7574
/** Number of confirmation recommended for accepting a transaction */

src/qt/transactiontablemodel.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <core_io.h>
1919
#include <interfaces/handler.h>
20+
#include <tinyformat.h>
2021
#include <uint256.h>
2122

2223
#include <algorithm>
@@ -377,8 +378,6 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
377378
case TransactionRecord::SendToAddress:
378379
case TransactionRecord::SendToOther:
379380
return tr("Sent to");
380-
case TransactionRecord::SendToSelf:
381-
return tr("Payment to yourself");
382381
case TransactionRecord::Generated:
383382
return tr("Mined");
384383
default:
@@ -421,8 +420,6 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b
421420
return lookupAddress(wtx->address, tooltip) + watchAddress;
422421
case TransactionRecord::SendToOther:
423422
return QString::fromStdString(wtx->address) + watchAddress;
424-
case TransactionRecord::SendToSelf:
425-
return lookupAddress(wtx->address, tooltip) + watchAddress;
426423
default:
427424
return tr("(n/a)") + watchAddress;
428425
}
@@ -441,8 +438,6 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
441438
if(label.isEmpty())
442439
return COLOR_BAREADDRESS;
443440
} break;
444-
case TransactionRecord::SendToSelf:
445-
return COLOR_BAREADDRESS;
446441
default:
447442
break;
448443
}
@@ -560,7 +555,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
560555
case Status:
561556
return QString::fromStdString(rec->status.sortKey);
562557
case Date:
563-
return rec->time;
558+
return QString::fromStdString(strprintf("%020s-%s", rec->time, rec->status.sortKey));
564559
case Type:
565560
return formatTxType(rec);
566561
case Watchonly:

src/qt/transactionview.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
9191
TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
9292
typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
9393
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
94-
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
9594
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
9695
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
9796

src/wallet/interfaces.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
6767
result.txout_address_is_mine.reserve(wtx.tx->vout.size());
6868
for (const auto& txout : wtx.tx->vout) {
6969
result.txout_is_mine.emplace_back(wallet.IsMine(txout));
70+
result.txout_is_change.push_back(OutputIsChange(wallet, txout));
7071
result.txout_address.emplace_back();
7172
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
7273
wallet.IsMine(result.txout_address.back()) :

0 commit comments

Comments
 (0)