0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-03-04 13:55:23 -05:00

Merge #18894: gui: Fix manual coin control with multiple wallets loaded

a8b5f1b133 gui: Fix manual coin control with multiple wallets loaded (João Barbosa)

Pull request description:

  This PR ensures each loaded wallet has a dedicated coin control in the send view which is manipulated by the coin control dialog.

  This is an alternative to #17457. Two main differences are:
   - scope reduced - no unnecessary changes unrelated to the fix;
   - approach taken - coin control instance now belongs to the send view.

  All problems raised in #17457 reviews no longer apply due to the approach taken - https://github.com/bitcoin/bitcoin/pull/17457#pullrequestreview-319297589 and https://github.com/bitcoin/bitcoin/pull/17457#issuecomment-555920829)

  No change in behavior if only one wallet is loaded.

  Closes #15725.

ACKs for top commit:
  jonasschnelli:
    utACK a8b5f1b133
  ryanofsky:
    Code review ACK a8b5f1b133. Code changes are very straightforward, just replacing global CCoinControl object with SendCoinsDialog member. Not sure if this means coin control settings are reset between payments. It would be good to note in the PR description or release notes if single wallet behavior is affected
  hebasto:
    ACK a8b5f1b133
  Sjors:
    tACK a8b5f1b133

Tree-SHA512: 3ad9c51bab6f28ec0e90efbd6f43fa510c81dafb2eff0b8c3724efcee3e030054a10be013e27cefe35763374c5f6d7af8c02658736964f733d7e38b646b5df65
This commit is contained in:
Jonas Schnelli 2020-05-13 10:15:26 +02:00
commit 246e878e78
No known key found for this signature in database
GPG key ID: 1EB776BB03C7922D
4 changed files with 45 additions and 67 deletions

View file

@ -41,10 +41,11 @@ bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
return QTreeWidgetItem::operator<(other);
}
CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _model, const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent),
ui(new Ui::CoinControlDialog),
model(nullptr),
m_coin_control(coin_control),
model(_model),
platformStyle(_platformStyle)
{
ui->setupUi(this);
@ -136,6 +137,13 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt())));
GUIUtil::handleCloseWindowShortcut(this);
if(_model->getOptionsModel() && _model->getAddressTableModel())
{
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(m_coin_control, _model, this);
}
}
CoinControlDialog::~CoinControlDialog()
@ -148,18 +156,6 @@ CoinControlDialog::~CoinControlDialog()
delete ui;
}
void CoinControlDialog::setModel(WalletModel *_model)
{
this->model = _model;
if(_model && _model->getOptionsModel() && _model->getAddressTableModel())
{
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(_model, this);
}
}
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
@ -185,8 +181,8 @@ void CoinControlDialog::buttonSelectAllClicked()
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
ui->treeWidget->setEnabled(true);
if (state == Qt::Unchecked)
coinControl()->UnSelectAll(); // just to be sure
CoinControlDialog::updateLabels(model, this);
m_coin_control.UnSelectAll(); // just to be sure
CoinControlDialog::updateLabels(m_coin_control, model, this);
}
// context menu
@ -371,15 +367,15 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
coinControl()->UnSelect(outpt);
m_coin_control.UnSelect(outpt);
else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
else
coinControl()->Select(outpt);
m_coin_control.Select(outpt);
// selection changed -> update labels
if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
CoinControlDialog::updateLabels(model, this);
CoinControlDialog::updateLabels(m_coin_control, model, this);
}
}
@ -396,7 +392,7 @@ void CoinControlDialog::updateLabelLocked()
else ui->labelLocked->setVisible(false);
}
void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *model, QDialog* dialog)
{
if (!model)
return;
@ -428,7 +424,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
bool fWitness = false;
std::vector<COutPoint> vCoinControl;
coinControl()->ListSelected(vCoinControl);
m_coin_control.ListSelected(vCoinControl);
size_t i = 0;
for (const auto& out : model->wallet().getCoins(vCoinControl)) {
@ -439,7 +435,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
const COutPoint& outpt = vCoinControl[i++];
if (out.is_spent)
{
coinControl()->UnSelect(outpt);
m_coin_control.UnSelect(outpt);
continue;
}
@ -492,7 +488,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
nPayFee = model->wallet().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */);
nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control, nullptr /* returned_target */, nullptr /* reason */);
if (nPayAmount > 0)
{
@ -584,12 +580,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
label->setVisible(nChange < 0);
}
CCoinControl* CoinControlDialog::coinControl()
{
static CCoinControl coin_control;
return &coin_control;
}
void CoinControlDialog::updateView()
{
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
@ -689,13 +679,13 @@ void CoinControlDialog::updateView()
// disable locked coins
if (model->wallet().isLockedCoin(output))
{
coinControl()->UnSelect(output); // just to be sure
m_coin_control.UnSelect(output); // just to be sure
itemOutput->setDisabled(true);
itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
if (coinControl()->IsSelected(output))
if (m_coin_control.IsSelected(output))
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}

View file

@ -42,20 +42,18 @@ class CoinControlDialog : public QDialog
Q_OBJECT
public:
explicit CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent = nullptr);
explicit CoinControlDialog(CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr);
~CoinControlDialog();
void setModel(WalletModel *model);
// static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*);
static void updateLabels(CCoinControl& m_coin_control, WalletModel*, QDialog*);
static QList<CAmount> payAmounts;
static CCoinControl *coinControl();
static bool fSubtractFeeFromAmount;
private:
Ui::CoinControlDialog *ui;
CCoinControl& m_coin_control;
WalletModel *model;
int sortColumn;
Qt::SortOrder sortOrder;

View file

@ -57,6 +57,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui(new Ui::SendCoinsDialog),
clientModel(nullptr),
model(nullptr),
m_coin_control(new CCoinControl),
fNewRecipientAllowed(true),
fFeeMinimized(true),
platformStyle(_platformStyle)
@ -259,14 +260,9 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
m_current_transaction = MakeUnique<WalletModelTransaction>(recipients);
WalletModel::SendCoinsReturn prepareStatus;
// Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
CCoinControl ctrl;
if (model->getOptionsModel()->getCoinControlFeatures())
ctrl = *CoinControlDialog::coinControl();
updateCoinControlState(*m_coin_control);
updateCoinControlState(ctrl);
prepareStatus = model->prepareTransaction(*m_current_transaction, ctrl);
prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
@ -454,7 +450,7 @@ void SendCoinsDialog::on_sendButton_clicked()
}
if (!send_failure) {
accept();
CoinControlDialog::coinControl()->UnSelectAll();
m_coin_control->UnSelectAll();
coinControlUpdateLabels();
}
fNewRecipientAllowed = true;
@ -466,7 +462,7 @@ void SendCoinsDialog::clear()
m_current_transaction.reset();
// Clear coin control settings
CoinControlDialog::coinControl()->UnSelectAll();
m_coin_control->UnSelectAll();
ui->checkBoxCoinControlChange->setChecked(false);
ui->lineEditCoinControlChange->clear();
coinControlUpdateLabels();
@ -689,17 +685,11 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
{
// Get CCoinControl instance if CoinControl is enabled or create a new one.
CCoinControl coin_control;
if (model->getOptionsModel()->getCoinControlFeatures()) {
coin_control = *CoinControlDialog::coinControl();
}
// Include watch-only for wallets without private key
coin_control.fAllowWatchOnly = model->wallet().privateKeysDisabled();
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
// Calculate available amount to send.
CAmount amount = model->wallet().getAvailableBalance(coin_control);
CAmount amount = model->wallet().getAvailableBalance(*m_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) {
@ -758,12 +748,11 @@ void SendCoinsDialog::updateSmartFeeLabel()
{
if(!model || !model->getOptionsModel())
return;
CCoinControl coin_control;
updateCoinControlState(coin_control);
coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
updateCoinControlState(*m_coin_control);
m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
int returned_target;
FeeReason reason;
CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, coin_control, &returned_target, &reason));
CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
@ -834,7 +823,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
ui->frameCoinControl->setVisible(checked);
if (!checked && model) // coin control features disabled
CoinControlDialog::coinControl()->SetNull();
m_coin_control->SetNull();
coinControlUpdateLabels();
}
@ -842,8 +831,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked()
{
CoinControlDialog dlg(platformStyle);
dlg.setModel(model);
CoinControlDialog dlg(*m_coin_control, model, platformStyle);
dlg.exec();
coinControlUpdateLabels();
}
@ -853,7 +841,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state)
{
if (state == Qt::Unchecked)
{
CoinControlDialog::coinControl()->destChange = CNoDestination();
m_coin_control->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->clear();
}
else
@ -869,7 +857,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
if (model && model->getAddressTableModel())
{
// Default to no change address until verified
CoinControlDialog::coinControl()->destChange = CNoDestination();
m_coin_control->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
const CTxDestination dest = DecodeDestination(text.toStdString());
@ -892,7 +880,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
if(btnRetVal == QMessageBox::Yes)
CoinControlDialog::coinControl()->destChange = dest;
m_coin_control->destChange = dest;
else
{
ui->lineEditCoinControlChange->setText("");
@ -911,7 +899,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
else
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
CoinControlDialog::coinControl()->destChange = dest;
m_coin_control->destChange = dest;
}
}
}
@ -923,7 +911,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
if (!model || !model->getOptionsModel())
return;
updateCoinControlState(*CoinControlDialog::coinControl());
updateCoinControlState(*m_coin_control);
// set pay amounts
CoinControlDialog::payAmounts.clear();
@ -941,10 +929,10 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
if (CoinControlDialog::coinControl()->HasSelected())
if (m_coin_control->HasSelected())
{
// actual coin control calculation
CoinControlDialog::updateLabels(model, this);
CoinControlDialog::updateLabels(*m_coin_control, model, this);
// show coin control stats
ui->labelCoinControlAutomaticallySelected->hide();

View file

@ -12,6 +12,7 @@
#include <QString>
#include <QTimer>
class CCoinControl;
class ClientModel;
class PlatformStyle;
class SendCoinsEntry;
@ -60,6 +61,7 @@ private:
Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
WalletModel *model;
std::unique_ptr<CCoinControl> m_coin_control;
std::unique_ptr<WalletModelTransaction> m_current_transaction;
bool fNewRecipientAllowed;
bool fFeeMinimized;