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
9 changes: 8 additions & 1 deletion src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <map>
#include <memory>
#include <psbt.h>
#include <set>
#include <string>
#include <tuple>
#include <type_traits>
Expand Down Expand Up @@ -170,7 +171,13 @@ class Wallet
virtual bool isLockedCoin(const COutPoint& output) = 0;

//! List locked coins.
virtual std::vector<COutPoint> listLockedCoins() = 0;
virtual std::set<COutPoint> listLockedCoins() = 0;

//! Lock the provided coins in a single batch.
virtual bool lockCoins(const std::vector<COutPoint>& outputs) = 0;

//! Unlock the provided coins in a single batch.
virtual bool unlockCoins(const std::vector<COutPoint>& outputs) = 0;

//! List protx coins.
virtual std::vector<COutPoint> listProTxCoins() = 0;
Expand Down
69 changes: 37 additions & 32 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
// (un)select all
connect(ui->pushButtonSelectAll, &QPushButton::clicked, this, &CoinControlDialog::buttonSelectAllClicked);

// Toggle lock state
connect(ui->pushButtonToggleLock, &QPushButton::clicked, this, &CoinControlDialog::buttonToggleLockClicked);
// (un)lock all
connect(ui->pushButtonLockAll, &QPushButton::clicked, this, &CoinControlDialog::buttonLockAllClicked);

ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 94);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
Expand Down Expand Up @@ -186,41 +186,46 @@ void CoinControlDialog::buttonSelectAllClicked()
CoinControlDialog::updateLabels(m_coin_control, model, this);
}

// Toggle lock state
void CoinControlDialog::buttonToggleLockClicked()
// (un)lock all
void CoinControlDialog::buttonLockAllClicked()
{
QTreeWidgetItem *item;
// Works in list-mode only
if(ui->radioListMode->isChecked()){
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++){
item = ui->treeWidget->topLevelItem(i);
COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
// Don't toggle the lock state of partially mixed coins if they are not hidden in CoinJoin mode
if (m_coin_control.IsUsingCoinJoin() && !fHideAdditional && !model->isFullyMixed(outpt)) {
continue;
}
if (model->wallet().isLockedCoin(outpt)) {
model->wallet().unlockCoin(outpt);
item->setDisabled(false);
item->setIcon(COLUMN_CHECKBOX, QIcon());
}
else{
model->wallet().lockCoin(outpt, /*write_to_db=*/true);
item->setDisabled(true);
item->setIcon(COLUMN_CHECKBOX, GUIUtil::getIcon("lock_closed", GUIUtil::ThemedColor::RED));
// Fetch the wallet-wide locked set once (single cs_wallet acquisition)
const std::set<COutPoint> lockedSet{model->wallet().listLockedCoins()};

// Collect all visible UTXOs; track locked ones separately for the unlock path
// (works in both tree and list modes)
std::vector<COutPoint> outputs;
std::vector<COutPoint> lockedOutputs;
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
QTreeWidgetItem* item = ui->treeWidget->topLevelItem(i);
if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) {
// List mode: top-level item is a UTXO
const COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()),
item->data(COLUMN_ADDRESS, VOutRole).toUInt());
outputs.emplace_back(outpt);
if (lockedSet.contains(outpt)) lockedOutputs.emplace_back(outpt);
} else {
// Tree mode: top-level item is an address group; collect children
for (int j = 0; j < item->childCount(); j++) {
QTreeWidgetItem* child = item->child(j);
const COutPoint outpt(uint256S(child->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()),
child->data(COLUMN_ADDRESS, VOutRole).toUInt());
outputs.emplace_back(outpt);
if (lockedSet.contains(outpt)) lockedOutputs.emplace_back(outpt);
}
updateLabelLocked();
}
ui->treeWidget->setEnabled(true);
CoinControlDialog::updateLabels(m_coin_control, model, this);
}
else{
QMessageBox msgBox(this);
msgBox.setObjectName("lockMessageBox");
msgBox.setText(tr("Please switch to \"List mode\" to use this function."));
msgBox.exec();
bool should_lock = lockedOutputs.empty();
bool success = should_lock ? model->wallet().lockCoins(outputs)
: model->wallet().unlockCoins(lockedOutputs);
if (!success) {
QMessageBox::warning(this, tr("Wallet error"),
should_lock ? tr("Failed to lock some coins.")
: tr("Failed to unlock some coins."));
}
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(m_coin_control, model, this);
}

// context menu
Expand Down
2 changes: 1 addition & 1 deletion src/qt/coincontroldialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private Q_SLOTS:
void headerSectionClicked(int);
void buttonBoxClicked(QAbstractButton*);
void buttonSelectAllClicked();
void buttonToggleLockClicked();
void buttonLockAllClicked();
void updateLabelLocked();
void on_hideButton_clicked();
};
Expand Down
4 changes: 2 additions & 2 deletions src/qt/forms/coincontroldialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,15 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonToggleLock">
<widget class="QPushButton" name="pushButtonLockAll">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>toggle lock state</string>
<string>(un)lock all</string>
</property>
<property name="autoDefault">
<bool>false</bool>
Expand Down
20 changes: 19 additions & 1 deletion src/wallet/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,29 @@ class WalletImpl : public Wallet
LOCK(m_wallet->cs_wallet);
return m_wallet->IsLockedCoin(output);
}
std::vector<COutPoint> listLockedCoins() override
std::set<COutPoint> listLockedCoins() override
{
LOCK(m_wallet->cs_wallet);
return m_wallet->ListLockedCoins();
}
bool lockCoins(const std::vector<COutPoint>& outputs) override
{
LOCK(m_wallet->cs_wallet);
WalletBatch batch(m_wallet->GetDatabase());
for (const auto& output : outputs) {
if (!m_wallet->LockCoin(output, &batch)) return false;
}
return true;
}
bool unlockCoins(const std::vector<COutPoint>& outputs) override
{
LOCK(m_wallet->cs_wallet);
WalletBatch batch(m_wallet->GetDatabase());
for (const auto& output : outputs) {
if (!m_wallet->UnlockCoin(output, &batch)) return false;
}
return true;
}
std::vector<COutPoint> listProTxCoins() override
{
LOCK(m_wallet->cs_wallet);
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2742,10 +2742,10 @@ bool CWallet::IsLockedCoin(const COutPoint& output) const
return setLockedCoins.count(output) > 0;
}

std::vector<COutPoint> CWallet::ListLockedCoins() const
const std::set<COutPoint>& CWallet::ListLockedCoins() const
{
AssertLockHeld(cs_wallet);
return std::vector<COutPoint>(setLockedCoins.begin(), setLockedCoins.end());
return setLockedCoins;
}

std::vector<COutPoint> CWallet::ListProTxCoins() const { return ListProTxCoins(setWalletUTXO); }
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
bool LockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool UnlockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<COutPoint> ListLockedCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const std::set<COutPoint>& ListLockedCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<COutPoint> ListProTxCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<COutPoint> ListProTxCoins(const std::set<COutPoint>& utxos) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockProTxCoins(const std::set<COutPoint>& utxos, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
Expand Down
Loading