aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bench/wallet_create_tx.cpp4
-rw-r--r--src/qt/sendcoinsdialog.cpp9
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/test/wallettests.cpp258
-rw-r--r--src/qt/walletmodel.cpp14
-rw-r--r--src/wallet/interfaces.cpp20
-rw-r--r--src/wallet/spend.cpp6
-rw-r--r--src/wallet/spend.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp2
9 files changed, 248 insertions, 70 deletions
diff --git a/src/bench/wallet_create_tx.cpp b/src/bench/wallet_create_tx.cpp
index 80d23d1e51..cb31421598 100644
--- a/src/bench/wallet_create_tx.cpp
+++ b/src/bench/wallet_create_tx.cpp
@@ -102,7 +102,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
}
// Check available balance
- auto bal = wallet::GetAvailableBalance(wallet); // Cache
+ auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
wallet::CCoinControl coin_control;
@@ -161,7 +161,7 @@ static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType
}
// Check available balance
- auto bal = wallet::GetAvailableBalance(wallet); // Cache
+ auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
bench.epochIterations(2).run([&] {
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index e3fa4058a6..48f7fb6ad1 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -403,7 +403,7 @@ void SendCoinsDialog::presentPSBT(PartiallySignedTransaction& psbtx)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- QMessageBox msgBox;
+ QMessageBox msgBox(this);
//: Caption of "PSBT has been copied" messagebox
msgBox.setText(tr("Unsigned Transaction", "PSBT copied"));
msgBox.setInformativeText(tr("The PSBT has been copied to the clipboard. You can also save it."));
@@ -791,8 +791,13 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
// Include watch-only for wallets without private key
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
+ // Same behavior as send: if we have selected coins, only obtain their available balance.
+ // Copy to avoid modifying the member's data.
+ CCoinControl coin_control = *m_coin_control;
+ coin_control.m_allow_other_inputs = !coin_control.HasSelected();
+
// Calculate available amount to send.
- CAmount amount = model->getAvailableBalance(m_coin_control.get());
+ CAmount amount = model->getAvailableBalance(&coin_control);
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if (e && !e->isHidden() && e != entry) {
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 2fcdf5b32a..ac05cd98e5 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -49,6 +49,9 @@ public:
void pasteEntry(const SendCoinsRecipient &rv);
bool handlePaymentRequest(const SendCoinsRecipient &recipient);
+ // Only used for testing-purposes
+ wallet::CCoinControl* getCoinControl() { return m_coin_control.get(); }
+
public Q_SLOTS:
void clear();
void reject() override;
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index be5bcbbd54..eb7bf33a32 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -5,6 +5,7 @@
#include <qt/test/wallettests.h>
#include <qt/test/util.h>
+#include <wallet/coincontrol.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <key_io.h>
@@ -34,6 +35,8 @@
#include <QAction>
#include <QApplication>
#include <QCheckBox>
+#include <QClipboard>
+#include <QObject>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
@@ -46,6 +49,7 @@ using wallet::CWallet;
using wallet::CreateMockWalletDatabase;
using wallet::RemoveWallet;
using wallet::WALLET_FLAG_DESCRIPTORS;
+using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;
using wallet::WalletContext;
using wallet::WalletDescriptor;
using wallet::WalletRescanReserver;
@@ -53,14 +57,14 @@ using wallet::WalletRescanReserver;
namespace
{
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
-void ConfirmSend(QString* text = nullptr, bool cancel = false)
+void ConfirmSend(QString* text = nullptr, QMessageBox::StandardButton confirm_type = QMessageBox::Yes)
{
- QTimer::singleShot(0, [text, cancel]() {
+ QTimer::singleShot(0, [text, confirm_type]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("SendConfirmationDialog")) {
SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
if (text) *text = dialog->text();
- QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
+ QAbstractButton* button = dialog->button(confirm_type);
button->setEnabled(true);
button->click();
}
@@ -69,7 +73,8 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
}
//! Send coins to address and return txid.
-uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf)
+uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf,
+ QMessageBox::StandardButton confirm_type = QMessageBox::Yes)
{
QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
@@ -83,7 +88,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](const uint256& hash, ChangeType status) {
if (status == CT_NEW) txid = hash;
}));
- ConfirmSend();
+ ConfirmSend(/*text=*/nullptr, confirm_type);
bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "sendButtonClicked", Q_ARG(bool, false));
assert(invoked);
return txid;
@@ -121,7 +126,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
action->setEnabled(true);
QString text;
if (expectError.empty()) {
- ConfirmSend(&text, cancel);
+ ConfirmSend(&text, cancel ? QMessageBox::Cancel : QMessageBox::Yes);
} else {
ConfirmMessage(&text, 0ms);
}
@@ -136,6 +141,119 @@ void CompareBalance(WalletModel& walletModel, CAmount expected_balance, QLabel*
QCOMPARE(balance_label_to_check->text().trimmed(), balanceComparison);
}
+// Verify the 'useAvailableBalance' functionality. With and without manually selected coins.
+// Case 1: No coin control selected coins.
+// 'useAvailableBalance' should fill the amount edit box with the total available balance
+// Case 2: With coin control selected coins.
+// 'useAvailableBalance' should fill the amount edit box with the sum of the selected coins values.
+void VerifyUseAvailableBalance(SendCoinsDialog& sendCoinsDialog, const WalletModel& walletModel)
+{
+ // Verify first entry amount and "useAvailableBalance" button
+ QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
+ QVERIFY(entries->count() == 1); // only one entry
+ SendCoinsEntry* send_entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
+ QVERIFY(send_entry->getValue().amount == 0);
+ // Now click "useAvailableBalance", check updated balance (the entire wallet balance should be set)
+ Q_EMIT send_entry->useAvailableBalance(send_entry);
+ QVERIFY(send_entry->getValue().amount == walletModel.getCachedBalance().balance);
+
+ // Now manually select two coins and click on "useAvailableBalance". Then check updated balance
+ // (only the sum of the selected coins should be set).
+ int COINS_TO_SELECT = 2;
+ auto coins = walletModel.wallet().listCoins();
+ CAmount sum_selected_coins = 0;
+ int selected = 0;
+ QVERIFY(coins.size() == 1); // context check, coins received only on one destination
+ for (const auto& [outpoint, tx_out] : coins.begin()->second) {
+ sendCoinsDialog.getCoinControl()->Select(outpoint);
+ sum_selected_coins += tx_out.txout.nValue;
+ if (++selected == COINS_TO_SELECT) break;
+ }
+ QVERIFY(selected == COINS_TO_SELECT);
+
+ // Now that we have 2 coins selected, "useAvailableBalance" should update the balance label only with
+ // the sum of them.
+ Q_EMIT send_entry->useAvailableBalance(send_entry);
+ QVERIFY(send_entry->getValue().amount == sum_selected_coins);
+}
+
+void SyncUpWallet(const std::shared_ptr<CWallet>& wallet, interfaces::Node& node)
+{
+ WalletRescanReserver reserver(*wallet);
+ reserver.reserve();
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/false);
+ QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
+ QCOMPARE(result.last_scanned_block, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
+ QVERIFY(result.last_failed_block.IsNull());
+}
+
+std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, TestChain100Setup& test)
+{
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ {
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ wallet->SetupLegacyScriptPubKeyMan();
+ // Add watched key
+ CPubKey pubKey = test.coinbaseKey.GetPubKey();
+ bool import_keys = wallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , /*key_origins=*/{}, /*add_keypool=*/false, /*internal=*/false, /*timestamp=*/1);
+ assert(import_keys);
+ wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
+ }
+ SyncUpWallet(wallet, node);
+ return wallet;
+}
+
+std::shared_ptr<CWallet> SetupDescriptorsWallet(interfaces::Node& node, TestChain100Setup& test)
+{
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ // Add the coinbase key
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
+ CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type);
+ wallet->SetAddressBook(dest, "", "receive");
+ wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
+ SyncUpWallet(wallet, node);
+ wallet->SetBroadcastTransactions(true);
+ return wallet;
+}
+
+struct MiniGUI {
+public:
+ SendCoinsDialog sendCoinsDialog;
+ TransactionView transactionView;
+ OptionsModel optionsModel;
+ std::unique_ptr<ClientModel> clientModel;
+ std::unique_ptr<WalletModel> walletModel;
+
+ MiniGUI(interfaces::Node& node, const PlatformStyle* platformStyle) : sendCoinsDialog(platformStyle), transactionView(platformStyle), optionsModel(node) {
+ bilingual_str error;
+ QVERIFY(optionsModel.Init(error));
+ clientModel = std::make_unique<ClientModel>(node, &optionsModel);
+ }
+
+ void initModelForWallet(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet, const PlatformStyle* platformStyle)
+ {
+ WalletContext& context = *node.walletLoader().context();
+ AddWallet(context, wallet);
+ walletModel = std::make_unique<WalletModel>(interfaces::MakeWallet(context, wallet), *clientModel, platformStyle);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
+ sendCoinsDialog.setModel(walletModel.get());
+ transactionView.setModel(walletModel.get());
+ }
+
+};
+
//! Simple qt wallet tests.
//
// Test widgets can be debugged interactively calling show() on them and
@@ -149,64 +267,24 @@ void CompareBalance(WalletModel& walletModel, CAmount expected_balance, QLabel*
// QT_QPA_PLATFORM=xcb src/qt/test/test_bitcoin-qt # Linux
// QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt # Windows
// QT_QPA_PLATFORM=cocoa src/qt/test/test_bitcoin-qt # macOS
-void TestGUI(interfaces::Node& node)
+void TestGUI(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet)
{
- // Set up wallet and chain with 105 blocks (5 mature blocks for spending).
- TestChain100Setup test;
- for (int i = 0; i < 5; ++i) {
- test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
- }
- auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
- test.m_node.wallet_loader = wallet_loader.get();
- node.setContext(&test.m_node);
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
- wallet->LoadWallet();
- wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
- {
- LOCK(wallet->cs_wallet);
- wallet->SetupDescriptorScriptPubKeyMans();
-
- // Add the coinbase key
- FlatSigningProvider provider;
- std::string error;
- std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false);
- assert(desc);
- WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
- if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
- CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type);
- wallet->SetAddressBook(dest, "", "receive");
- wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
- }
- {
- WalletRescanReserver reserver(*wallet);
- reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/false);
- QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
- QVERIFY(result.last_failed_block.IsNull());
- }
- wallet->SetBroadcastTransactions(true);
-
// Create widgets for sending coins and listing transactions.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- SendCoinsDialog sendCoinsDialog(platformStyle.get());
- TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel(node);
- bilingual_str error;
- QVERIFY(optionsModel.Init(error));
- ClientModel clientModel(node, &optionsModel);
- WalletContext& context = *node.walletLoader().context();
- AddWallet(context, wallet);
- WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
- RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
- sendCoinsDialog.setModel(&walletModel);
- transactionView.setModel(&walletModel);
+ MiniGUI mini_gui(node, platformStyle.get());
+ mini_gui.initModelForWallet(node, wallet, platformStyle.get());
+ WalletModel& walletModel = *mini_gui.walletModel;
+ SendCoinsDialog& sendCoinsDialog = mini_gui.sendCoinsDialog;
+ TransactionView& transactionView = mini_gui.transactionView;
// Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel.
walletModel.pollBalanceChanged();
// Check balance in send dialog
CompareBalance(walletModel, walletModel.wallet().getBalance(), sendCoinsDialog.findChild<QLabel*>("labelBalance"));
+ // Check 'UseAvailableBalance' functionality
+ VerifyUseAvailableBalance(sendCoinsDialog, walletModel);
+
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
@@ -313,6 +391,76 @@ void TestGUI(interfaces::Node& node)
QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(), size_t{0});
}
+void TestGUIWatchOnly(interfaces::Node& node, TestChain100Setup& test)
+{
+ const std::shared_ptr<CWallet>& wallet = SetupLegacyWatchOnlyWallet(node, test);
+
+ // Create widgets and init models
+ std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
+ MiniGUI mini_gui(node, platformStyle.get());
+ mini_gui.initModelForWallet(node, wallet, platformStyle.get());
+ WalletModel& walletModel = *mini_gui.walletModel;
+ SendCoinsDialog& sendCoinsDialog = mini_gui.sendCoinsDialog;
+
+ // Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel.
+ walletModel.pollBalanceChanged();
+ // Check balance in send dialog
+ CompareBalance(walletModel, walletModel.wallet().getBalances().watch_only_balance,
+ sendCoinsDialog.findChild<QLabel*>("labelBalance"));
+
+ // Set change address
+ sendCoinsDialog.getCoinControl()->destChange = GetDestinationForKey(test.coinbaseKey.GetPubKey(), OutputType::LEGACY);
+
+ // Time to reject "save" PSBT dialog ('SendCoins' locks the main thread until the dialog receives the event).
+ QTimer timer;
+ timer.setInterval(500);
+ QObject::connect(&timer, &QTimer::timeout, [&](){
+ for (QWidget* widget : QApplication::topLevelWidgets()) {
+ if (widget->inherits("QMessageBox")) {
+ QMessageBox* dialog = qobject_cast<QMessageBox*>(widget);
+ QAbstractButton* button = dialog->button(QMessageBox::Discard);
+ button->setEnabled(true);
+ button->click();
+ timer.stop();
+ break;
+ }
+ }
+ });
+ timer.start(500);
+
+ // Send tx and verify PSBT copied to the clipboard.
+ SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, /*rbf=*/false, QMessageBox::Save);
+ const std::string& psbt_string = QApplication::clipboard()->text().toStdString();
+ QVERIFY(!psbt_string.empty());
+
+ // Decode psbt
+ std::optional<std::vector<unsigned char>> decoded_psbt = DecodeBase64(psbt_string);
+ QVERIFY(decoded_psbt);
+ PartiallySignedTransaction psbt;
+ std::string err;
+ QVERIFY(DecodeRawPSBT(psbt, MakeByteSpan(*decoded_psbt), err));
+}
+
+void TestGUI(interfaces::Node& node)
+{
+ // Set up wallet and chain with 105 blocks (5 mature blocks for spending).
+ TestChain100Setup test;
+ for (int i = 0; i < 5; ++i) {
+ test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
+ }
+ auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
+ test.m_node.wallet_loader = wallet_loader.get();
+ node.setContext(&test.m_node);
+
+ // "Full" GUI tests, use descriptor wallet
+ const std::shared_ptr<CWallet>& desc_wallet = SetupDescriptorsWallet(node, test);
+ TestGUI(node, desc_wallet);
+
+ // Legacy watch-only wallet test
+ // Verify PSBT creation.
+ TestGUIWatchOnly(node, test);
+}
+
} // namespace
void WalletTests::walletTests()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 61172d1625..565b732bf0 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -613,5 +613,17 @@ uint256 WalletModel::getLastBlockProcessed() const
CAmount WalletModel::getAvailableBalance(const CCoinControl* control)
{
- return control && control->HasSelected() ? wallet().getAvailableBalance(*control) : getCachedBalance().balance;
+ // No selected coins, return the cached balance
+ if (!control || !control->HasSelected()) {
+ const interfaces::WalletBalances& balances = getCachedBalance();
+ CAmount available_balance = balances.balance;
+ // if wallet private keys are disabled, this is a watch-only wallet
+ // so, let's include the watch-only balance.
+ if (balances.have_watch_only && m_wallet->privateKeysDisabled()) {
+ available_balance += balances.watch_only_balance;
+ }
+ return available_balance;
+ }
+ // Fetch balance from the wallet, taking into account the selected coins
+ return wallet().getAvailableBalance(*control);
}
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 1a76e46c54..df1eb19a33 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -18,6 +18,7 @@
#include <util/system.h>
#include <util/translation.h>
#include <util/ui_change_type.h>
+#include <wallet/coincontrol.h>
#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
@@ -403,7 +404,24 @@ public:
CAmount getBalance() override { return GetBalance(*m_wallet).m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
- return GetAvailableBalance(*m_wallet, &coin_control);
+ LOCK(m_wallet->cs_wallet);
+ CAmount total_amount = 0;
+ // Fetch selected coins total amount
+ if (coin_control.HasSelected()) {
+ FastRandomContext rng{};
+ CoinSelectionParams params(rng);
+ // Note: for now, swallow any error.
+ if (auto res = FetchSelectedInputs(*m_wallet, coin_control, params)) {
+ total_amount += res->total_amount;
+ }
+ }
+
+ // And fetch the wallet available coins
+ if (coin_control.m_allow_other_inputs) {
+ total_amount += AvailableCoins(*m_wallet, &coin_control).GetTotalAmount();
+ }
+
+ return total_amount;
}
isminetype txinIsMine(const CTxIn& txin) override
{
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 4548d5f813..57f3785a3a 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -356,12 +356,6 @@ CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl*
return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, params);
}
-CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
-{
- LOCK(wallet.cs_wallet);
- return AvailableCoins(wallet, coinControl).GetTotalAmount();
-}
-
const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint)
{
AssertLockHeld(wallet.cs_wallet);
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index 78c2c5f22b..cc9ccf3011 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -94,8 +94,6 @@ CoinsResult AvailableCoins(const CWallet& wallet,
*/
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, CoinFilterParams params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
-CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
-
/**
* Find non-change parent output.
*/
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 2e95a14807..edcfaa24e5 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -581,7 +581,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
// Check initial balance from one mature coinbase transaction.
- BOOST_CHECK_EQUAL(50 * COIN, GetAvailableBalance(*wallet));
+ BOOST_CHECK_EQUAL(50 * COIN, WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet).GetTotalAmount()));
// Add a transaction creating a change address, and confirm ListCoins still
// returns the coin associated with the change address underneath the