Skip to content

Commit

Permalink
Implement GUI version of consolidateunspent
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Apr 23, 2021
1 parent c33982a commit 6e0deab
Show file tree
Hide file tree
Showing 9 changed files with 440 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ QT_TS = \
QT_FORMS_UI = \
qt/forms/aboutdialog.ui \
qt/forms/coincontroldialog.ui \
qt/forms/consolidateunspentdialog.ui \
qt/forms/diagnosticsdialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/rpcconsole.ui \
Expand Down Expand Up @@ -111,6 +112,7 @@ QT_MOC_CPP = \
qt/moc_clientmodel.cpp \
qt/moc_coincontroldialog.cpp \
qt/moc_coincontroltreewidget.cpp \
qt/moc_consolidateunspentdialog.cpp \
qt/moc_csvmodelwriter.cpp \
qt/moc_diagnosticsdialog.cpp \
qt/moc_editaddressdialog.cpp \
Expand Down Expand Up @@ -181,6 +183,7 @@ GRIDCOINRESEARCH_QT_H = \
qt/clientmodel.h \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
qt/consolidateunspentdialog.h \
qt/csvmodelwriter.h \
qt/decoration.h \
qt/diagnosticsdialog.h \
Expand Down Expand Up @@ -243,6 +246,7 @@ GRIDCOINRESEARCH_QT_CPP = \
qt/clientmodel.cpp \
qt/coincontroldialog.cpp \
qt/coincontroltreewidget.cpp \
qt/consolidateunspentdialog.cpp \
qt/csvmodelwriter.cpp \
qt/decoration.cpp \
qt/diagnosticsdialog.cpp \
Expand Down
121 changes: 117 additions & 4 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

#include "init.h"
#include "bitcoinunits.h"
#include "walletmodel.h"
#include "addresstablemodel.h"
#include "optionsmodel.h"
#include "policy/fees.h"
#include "validation.h"
#include "wallet/coincontrol.h"
#include "consolidateunspentdialog.h"

#include <QApplication>
#include <QCheckBox>
Expand Down Expand Up @@ -106,9 +106,12 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) :
// (un)select all
connect(ui->selectAllPushButton, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));

ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
// consolidate
connect(ui->consolidateButton, SIGNAL(clicked()), this, SLOT(buttonConsolidateClicked()));

ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 150);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 170);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 200);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
Expand Down Expand Up @@ -153,6 +156,24 @@ void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
done(QDialog::Accepted); // closes the dialog

if (m_consolidationAddress.second.size())
{
SendCoinsRecipient consolidationRecipient;

qint64 amount = 0;
bool parse_status = false;

consolidationRecipient.label = m_consolidationAddress.first;
consolidationRecipient.address = m_consolidationAddress.second;
parse_status = BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(),
ui->coinControlAfterFeeLabel->text().left(ui->coinControlAfterFeeLabel->text().indexOf(" ")),
&amount);

if (parse_status) consolidationRecipient.amount = amount;

emit selectedConsolidationRecipientSignal(consolidationRecipient);
}
}

// (un)select all
Expand All @@ -175,6 +196,93 @@ void CoinControlDialog::buttonSelectAllClicked()
CoinControlDialog::updateLabels(model, this);
}

void CoinControlDialog::buttonConsolidateClicked()
{
ConsolidateUnspentDialog* consolidateUnspentDialog = new ConsolidateUnspentDialog(this);
consolidateUnspentDialog->SetOutputWarningVisible(false);

CAmount maximum_output_value = ui->maxOutputValue->value();

QTreeWidgetItemIterator iter(ui->treeWidget);

std::multimap<CAmount, std::pair<QTreeWidgetItem*, COutPoint>> input_map;

std::map<QString, QString> addressList;

//QList<std::pair<QString, QString>> addressList;

bool culled_inputs = false;

// This consolidate unspent process has to work a little differently than the rpc one. Here in the first loop we
// are going to uncheck checked inputs that are greater than the maximum input value to be selected for consolidation.
// Checked inputs that are less than or equal to the maximum input value desired for consolidation are left checked
// and added to the input map.
while (*iter)
{
CAmount input_value = (*iter)->text(COLUMN_AMOUNT_INT64).toLongLong();
COutPoint outpoint(uint256S((*iter)->text(COLUMN_TXHASH).toStdString()), (*iter)->text(COLUMN_VOUT_INDEX).toUInt());

if ((*iter)->text(COLUMN_TXHASH).length() == 64)
{
if ((*iter)->checkState(COLUMN_CHECKBOX) == Qt::Checked
&& input_value <= maximum_output_value)
{
input_map.insert(std::make_pair(input_value, std::make_pair(*iter, outpoint)));
}
else
{
(*iter)->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
coinControl->UnSelect(outpoint);
}
}

++iter;
}

// The second loop is to limit the number of selected outputs to 200, because above this risks a failed transaction
// due to the size. Here we use the ordering of inputs of the input_map by increasing value for convenience.
unsigned int input_count = 0;

for (auto& input : input_map)
{
if (input_count >= 200)
{
LogPrint(BCLog::LogFlags::MISC, "INFO: %s: Culled input %u with value %f.",
__func__, input_count, (double) input.first / COIN);

if (coinControl->IsSelected(input.second.second.hash, input.second.second.n))
{
input.second.first->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);

culled_inputs = true;
coinControl->UnSelect(input.second.second);
}

}

++input_count;
}

for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
{
QString label = ui->treeWidget->topLevelItem(i)->text(COLUMN_LABEL);
QString address = ui->treeWidget->topLevelItem(i)->text(COLUMN_ADDRESS);

if (label !="(change)") addressList[address] = label;

}

if (!addressList.empty()) consolidateUnspentDialog->SetAddressList(addressList);


if (culled_inputs) consolidateUnspentDialog->SetOutputWarningVisible(true);

consolidateUnspentDialog->show();

connect(consolidateUnspentDialog, SIGNAL(selectedConsolidationAddressSignal(std::pair<QString, QString>)),
this, SLOT(selectedConsolidationAddressSlot(std::pair<QString, QString>)));
}

// context menu
void CoinControlDialog::showMenu(const QPoint &point)
{
Expand Down Expand Up @@ -740,3 +848,8 @@ void CoinControlDialog::updateView()
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}

void CoinControlDialog::selectedConsolidationAddressSlot(std::pair<QString, QString> address)
{
m_consolidationAddress = address;
}
10 changes: 10 additions & 0 deletions src/qt/coincontroldialog.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef COINCONTROLDIALOG_H
#define COINCONTROLDIALOG_H

#include "walletmodel.h"

#include <QAbstractButton>
#include <QAction>
#include <QDialog>
Expand Down Expand Up @@ -33,6 +35,9 @@ class CoinControlDialog : public QDialog
static QList<qint64> payAmounts;
static CCoinControl *coinControl;

signals:
void selectedConsolidationRecipientSignal(SendCoinsRecipient consolidationRecipient);

private:
Ui::CoinControlDialog *ui;
WalletModel *model;
Expand All @@ -45,6 +50,9 @@ class CoinControlDialog : public QDialog
//QAction *lockAction;
//QAction *unlockAction;

std::pair<QString, QString> m_consolidationAddress;
qint64 m_consolidationAmount = 0;

QString strPad(QString, int, QString);
void sortView(int, Qt::SortOrder);
void updateView();
Expand Down Expand Up @@ -86,6 +94,8 @@ private slots:
void headerSectionClicked(int);
void buttonBoxClicked(QAbstractButton*);
void buttonSelectAllClicked();
void buttonConsolidateClicked();
void selectedConsolidationAddressSlot(std::pair<QString, QString> address);
//void updateLabelLocked();
};

Expand Down
77 changes: 77 additions & 0 deletions src/qt/consolidateunspentdialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "consolidateunspentdialog.h"
#include "ui_consolidateunspentdialog.h"

#include "util.h"

using namespace std;

ConsolidateUnspentDialog::ConsolidateUnspentDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ConsolidateUnspentDialog)
{
ui->setupUi(this);

ui->addressTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);

// ok button
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));

// destination address selection
connect(ui->addressTableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(addressSelectionChanged()));
}

ConsolidateUnspentDialog::~ConsolidateUnspentDialog()
{
delete ui;
}

// --------------------------------------------------------- address - label
void ConsolidateUnspentDialog::SetAddressList(const std::map<QString, QString>& addressList)
{
ui->addressTableWidget->setSortingEnabled(false);

int row = 0;
for (const auto& iter : addressList)
{
ui->addressTableWidget->insertRow(row);

QTableWidgetItem* label = new QTableWidgetItem(iter.second);
QTableWidgetItem* address = new QTableWidgetItem(iter.first);

if (label != nullptr) ui->addressTableWidget->setItem(row, 0, label);
if (address != nullptr) ui->addressTableWidget->setItem(row, 1, address);

++row;
}

ui->addressTableWidget->setCurrentItem(ui->addressTableWidget->item(0, 1));
}

void ConsolidateUnspentDialog::SetOutputWarningVisible(bool status)
{
ui->outputLimitWarningIconLabel->setVisible(status);
ui->outputLimitWarningLabel->setVisible(status);
}

// ok button
void ConsolidateUnspentDialog::buttonBoxClicked(QAbstractButton* button)
{
// closes the dialog
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) done(QDialog::Accepted);

emit selectedConsolidationAddressSignal(m_selectedDestinationAddress);
}

void ConsolidateUnspentDialog::addressSelectionChanged()
{
int currentRow = ui->addressTableWidget->currentRow();

QTableWidgetItem* selectedLabel = ui->addressTableWidget->item(currentRow, 0);
QTableWidgetItem* selectedAddress = ui->addressTableWidget->item(currentRow, 1);

m_selectedDestinationAddress = std::make_pair(selectedLabel->text(), selectedAddress->text());

LogPrint(BCLog::LogFlags::MISC, "INFO: %s: Label %, Address %s selected.", __func__,
m_selectedDestinationAddress.first.toStdString(),
m_selectedDestinationAddress.second.toStdString());
}
38 changes: 38 additions & 0 deletions src/qt/consolidateunspentdialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef CONSOLIDATEUNSPENTDIALOG_H
#define CONSOLIDATEUNSPENTDIALOG_H

#include <QDialogButtonBox>
#include <QDialog>
#include <QList>
#include <QSet>
#include <QString>

namespace Ui {
class ConsolidateUnspentDialog;
}

class ConsolidateUnspentDialog : public QDialog
{
Q_OBJECT

public:
explicit ConsolidateUnspentDialog(QWidget *parent = 0);
~ConsolidateUnspentDialog();

void SetAddressList(const std::map<QString, QString>& addressList);
void SetOutputWarningVisible(bool status);

signals:
void selectedConsolidationAddressSignal(std::pair<QString, QString> address);

private:
Ui::ConsolidateUnspentDialog *ui;

std::pair<QString, QString> m_selectedDestinationAddress;

private slots:
void buttonBoxClicked(QAbstractButton *button);
void addressSelectionChanged();
};

#endif // CONSOLIDATEUNSPENTDIALOG_H
Loading

0 comments on commit 6e0deab

Please sign in to comment.