Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions qml/models/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,11 @@ QList<QSharedPointer<Transaction>> Transaction::fromWalletTx(const interfaces::W
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;
//}
// Only hide change when this wallet is the sender. If someone sends to
// one of our change addresses, it is still an incoming payment.
if (wtx.txout_is_change[i]) {
continue;
}

//
// Debit
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_executable(bitcoinqml_unit_tests
test_walletqmlmodel.cpp
test_bumptransactionmodel.cpp
test_walletqmlcontroller.cpp
test_transaction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../bitcoin/src/util/chaintype.cpp
)

Expand Down
125 changes: 125 additions & 0 deletions test/test_transaction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) 2026 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 <qml/models/transaction.h>

#include <chainparams.h>
#include <interfaces/wallet.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <uint256.h>
#include <wallet/types.h>

#include <QtTest/QtTest>

using wallet::ISMINE_NO;
using wallet::ISMINE_SPENDABLE;

namespace {
constexpr CAmount COIN_VALUE{100'000'000};

CTxDestination Destination(unsigned char value)
{
uint160 id;
id.begin()[0] = value;
return PKHash{id};
}

interfaces::WalletTx MakeWalletTx(
const std::vector<wallet::isminetype>& txin_is_mine,
const std::vector<CAmount>& output_values,
const std::vector<wallet::isminetype>& txout_is_mine,
const std::vector<bool>& txout_is_change,
CAmount debit,
CAmount credit = 0)
{
CMutableTransaction mtx;
mtx.vin.emplace_back(COutPoint{Txid::FromUint256(uint256{1}), 0});

std::vector<CTxDestination> addresses;
std::vector<wallet::isminetype> address_is_mine;
for (size_t i = 0; i < output_values.size(); ++i) {
mtx.vout.emplace_back(output_values[i], CScript{});
addresses.push_back(Destination(static_cast<unsigned char>(i + 1)));
address_is_mine.push_back(txout_is_mine[i]);
}

interfaces::WalletTx wtx;
wtx.tx = MakeTransactionRef(std::move(mtx));
wtx.txin_is_mine = txin_is_mine;
wtx.txout_is_mine = txout_is_mine;
wtx.txout_is_change = txout_is_change;
wtx.txout_address = addresses;
wtx.txout_address_is_mine = address_is_mine;
wtx.credit = credit;
wtx.debit = debit;
wtx.change = 0;
wtx.time = 0;
wtx.is_coinbase = false;
return wtx;
}
} // namespace

class TransactionTests : public QObject
{
Q_OBJECT

private Q_SLOTS:
void initTestCase();
void fromWalletTx_hidesSenderChangeOutput();
void fromWalletTx_showsIncomingPaymentToChangeAddress();
};

void TransactionTests::initTestCase()
{
SelectParams(ChainType::REGTEST);
}

void TransactionTests::fromWalletTx_hidesSenderChangeOutput()
{
const interfaces::WalletTx wtx = MakeWalletTx(
/*txin_is_mine=*/{ISMINE_SPENDABLE},
/*output_values=*/{70 * COIN_VALUE, 29 * COIN_VALUE},
/*txout_is_mine=*/{ISMINE_NO, ISMINE_SPENDABLE},
/*txout_is_change=*/{false, true},
/*debit=*/100 * COIN_VALUE);

const auto parts = Transaction::fromWalletTx(wtx);

QCOMPARE(parts.size(), 1);
QCOMPARE(parts.at(0)->type, Transaction::SendToAddress);
QCOMPARE(parts.at(0)->idx, 0);
QCOMPARE(parts.at(0)->debit, -71 * COIN_VALUE);
QCOMPARE(parts.at(0)->credit, 0);
}

void TransactionTests::fromWalletTx_showsIncomingPaymentToChangeAddress()
{
const interfaces::WalletTx wtx = MakeWalletTx(
/*txin_is_mine=*/{ISMINE_NO},
/*output_values=*/{5 * COIN_VALUE},
/*txout_is_mine=*/{ISMINE_SPENDABLE},
/*txout_is_change=*/{true},
/*debit=*/0,
/*credit=*/5 * COIN_VALUE);

const auto parts = Transaction::fromWalletTx(wtx);

QCOMPARE(parts.size(), 1);
QCOMPARE(parts.at(0)->type, Transaction::RecvWithAddress);
QCOMPARE(parts.at(0)->idx, 0);
QCOMPARE(parts.at(0)->debit, 0);
QCOMPARE(parts.at(0)->credit, 5 * COIN_VALUE);
}

int RunTransactionTests(int argc, char* argv[])
{
TransactionTests tests;
return QTest::qExec(&tests, argc, argv);
}

#ifndef BITCOINQML_NO_TEST_MAIN
QTEST_MAIN(TransactionTests)
#endif
#include "test_transaction.moc"
3 changes: 3 additions & 0 deletions test/test_unit_tests_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

const TranslateFn G_TRANSLATION_FUN{nullptr};

int RunTransactionTests(int argc, char* argv[]);

int main(int argc, char* argv[])
{
testing::InitGoogleMock(&argc, argv);
Expand All @@ -22,6 +24,7 @@ int main(int argc, char* argv[])
for (const auto& test : qttestregistry::SortedEntries()) {
status |= test.run(argc, argv);
}
status |= RunTransactionTests(argc, argv);

return status;
}
Loading