Skip to content

Commit 46897ed

Browse files
committed
GUI/Options: Configure dustdynamic using settings
1 parent 057943b commit 46897ed

File tree

4 files changed

+124
-7
lines changed

4 files changed

+124
-7
lines changed

src/qt/optionsdialog.cpp

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
#include <interfaces/node.h>
2222
#include <node/chainstatemanager_args.h>
2323
#include <netbase.h>
24+
#include <node/mempool_args.h> // for ParseDustDynamicOpt
2425
#include <outputtype.h>
2526
#include <primitives/transaction.h> // for WITNESS_SCALE_FACTOR
2627
#include <txdb.h>
2728
#include <txmempool.h> // for maxmempoolMinimum
2829
#include <util/strencodings.h>
2930
#include <chrono>
31+
#include <utility>
3032

3133
#include <QApplication>
3234
#include <QBoxLayout>
@@ -41,6 +43,7 @@
4143
#include <QLabel>
4244
#include <QLocale>
4345
#include <QMessageBox>
46+
#include <QRadioButton>
4447
#include <QSpacerItem>
4548
#include <QString>
4649
#include <QStringList>
@@ -67,13 +70,15 @@ void OptionsDialog::CreateOptionUI(QBoxLayout * const layout, QWidget * const o,
6770

6871
if (!horizontalLayout) horizontalLayout = new QHBoxLayout();
6972

70-
QLabel * const labelBefore = new QLabel(parent);
71-
labelBefore->setText(text_parts[0]);
72-
labelBefore->setTextFormat(Qt::PlainText);
73-
labelBefore->setBuddy(o);
74-
labelBefore->setToolTip(o->toolTip());
73+
if (!text_parts[0].isEmpty()) {
74+
QLabel * const labelBefore = new QLabel(parent);
75+
labelBefore->setText(text_parts[0]);
76+
labelBefore->setTextFormat(Qt::PlainText);
77+
labelBefore->setBuddy(o);
78+
labelBefore->setToolTip(o->toolTip());
79+
horizontalLayout->addWidget(labelBefore);
80+
}
7581

76-
horizontalLayout->addWidget(labelBefore);
7782
horizontalLayout->addWidget(o);
7883

7984
QLabel * const labelAfter = new QLabel(parent);
@@ -340,7 +345,67 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
340345
});
341346

342347
dustrelayfee = new BitcoinAmountField(groupBox_Spamfiltering);
343-
CreateOptionUI(verticalLayout_Spamfiltering, dustrelayfee, tr("Ignore transactions with values that would cost more to spend at a fee rate of %s per kvB."));
348+
CreateOptionUI(verticalLayout_Spamfiltering, dustrelayfee, tr("Ignore transactions with values that would cost more to spend at a fee rate of %s per kvB (\"dust\")."));
349+
350+
351+
auto hlayout = new QHBoxLayout();
352+
dustdynamic_enable = new QCheckBox(groupBox_Spamfiltering);
353+
dustdynamic_enable->setText(tr("Automatically adjust the dust limit upward to"));
354+
hlayout->addWidget(dustdynamic_enable);
355+
dustdynamic_multiplier = new QDoubleSpinBox(groupBox_Spamfiltering);
356+
dustdynamic_multiplier->setDecimals(3);
357+
dustdynamic_multiplier->setStepType(QAbstractSpinBox::DefaultStepType);
358+
dustdynamic_multiplier->setSingleStep(1);
359+
dustdynamic_multiplier->setMinimum(0.001);
360+
dustdynamic_multiplier->setMaximum(65);
361+
dustdynamic_multiplier->setValue(DEFAULT_DUST_RELAY_MULTIPLIER / 1000.0);
362+
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_multiplier, tr("%s times:"), hlayout);
363+
364+
QStyleOptionButton styleoptbtn;
365+
const auto checkbox_indent = dustdynamic_enable->style()->subElementRect(QStyle::SE_CheckBoxIndicator, &styleoptbtn, dustdynamic_enable).width();
366+
367+
hlayout = new QHBoxLayout();
368+
hlayout->addSpacing(checkbox_indent);
369+
dustdynamic_target = new QRadioButton(groupBox_Spamfiltering);
370+
hlayout->addWidget(dustdynamic_target);
371+
dustdynamic_target_blocks = new QSpinBox(groupBox_Spamfiltering);
372+
dustdynamic_target_blocks->setMinimum(2);
373+
dustdynamic_target_blocks->setMaximum(1008); // FIXME: Get this from the fee estimator
374+
dustdynamic_target_blocks->setValue(1008);
375+
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_target_blocks, tr("fee estimate for %s blocks."), hlayout);
376+
// FIXME: Make it possible to click labels to select + focus spinbox
377+
378+
hlayout = new QHBoxLayout();
379+
hlayout->addSpacing(checkbox_indent);
380+
dustdynamic_mempool = new QRadioButton(groupBox_Spamfiltering);
381+
hlayout->addWidget(dustdynamic_mempool);
382+
dustdynamic_mempool_kvB = new QSpinBox(groupBox_Spamfiltering);
383+
dustdynamic_mempool_kvB->setMinimum(1);
384+
dustdynamic_mempool_kvB->setMaximum(std::numeric_limits<int32_t>::max());
385+
dustdynamic_mempool_kvB->setValue(3024000);
386+
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_mempool_kvB, tr("the lowest fee of the best known %s kvB of unconfirmed transactions."), hlayout);
387+
388+
connect(dustdynamic_enable, &QAbstractButton::toggled, [this](const bool state){
389+
dustdynamic_multiplier->setEnabled(state);
390+
dustdynamic_target->setEnabled(state);
391+
dustdynamic_mempool->setEnabled(state);
392+
if (state) {
393+
if (!dustdynamic_mempool->isChecked()) dustdynamic_target->setChecked(true);
394+
dustdynamic_target_blocks->setEnabled(dustdynamic_target->isChecked());
395+
dustdynamic_mempool_kvB->setEnabled(dustdynamic_mempool->isChecked());
396+
} else {
397+
dustdynamic_target_blocks->setEnabled(false);
398+
dustdynamic_mempool_kvB->setEnabled(false);
399+
}
400+
});
401+
dustdynamic_enable->toggled(dustdynamic_enable->isChecked());
402+
connect(dustdynamic_target, &QAbstractButton::toggled, [this](const bool state){
403+
dustdynamic_target_blocks->setEnabled(state);
404+
});
405+
connect(dustdynamic_mempool, &QAbstractButton::toggled, [this](const bool state){
406+
dustdynamic_mempool_kvB->setEnabled(state);
407+
});
408+
344409

345410
verticalLayout_Mempool->addWidget(groupBox_Spamfiltering);
346411

@@ -645,6 +710,24 @@ void OptionsDialog::setMapper()
645710
mapper->addMapping(datacarriersize, OptionsModel::datacarriersize);
646711
mapper->addMapping(dustrelayfee, OptionsModel::dustrelayfee);
647712

713+
QVariant current_dustdynamic = model->data(model->index(OptionsModel::dustdynamic, 0), Qt::EditRole);
714+
const util::Result<std::pair<int32_t, int>> parsed_dustdynamic = ParseDustDynamicOpt(current_dustdynamic.toString().toStdString(), std::numeric_limits<unsigned int>::max());
715+
if (parsed_dustdynamic) {
716+
if (parsed_dustdynamic->first == 0) {
717+
dustdynamic_enable->setChecked(false);
718+
} else {
719+
dustdynamic_multiplier->setValue(parsed_dustdynamic->second / 1000.0);
720+
if (parsed_dustdynamic->first < 0) {
721+
dustdynamic_target->setChecked(true);
722+
dustdynamic_target_blocks->setValue(-parsed_dustdynamic->first);
723+
} else {
724+
dustdynamic_mempool->setChecked(true);
725+
dustdynamic_mempool_kvB->setValue(parsed_dustdynamic->first);
726+
}
727+
dustdynamic_enable->setChecked(true);
728+
}
729+
}
730+
648731
/* Mining tab */
649732

650733
mapper->addMapping(blockmintxfee, OptionsModel::blockmintxfee);
@@ -817,6 +900,16 @@ void OptionsDialog::on_okButton_clicked()
817900

818901
model->setData(model->index(OptionsModel::mempoolreplacement, 0), mempoolreplacement->itemData(mempoolreplacement->currentIndex()));
819902

903+
if (dustdynamic_enable->isChecked()) {
904+
if (dustdynamic_target->isChecked()) {
905+
model->setData(model->index(OptionsModel::dustdynamic, 0), QStringLiteral("%2*target:%1").arg(dustdynamic_target_blocks->value()).arg(dustdynamic_multiplier->value()));
906+
} else if (dustdynamic_mempool->isChecked()) {
907+
model->setData(model->index(OptionsModel::dustdynamic, 0), QStringLiteral("%2*mempool:%1").arg(dustdynamic_mempool_kvB->value()).arg(dustdynamic_multiplier->value()));
908+
}
909+
} else {
910+
model->setData(model->index(OptionsModel::dustdynamic, 0), "off");
911+
}
912+
820913
mapper->submit();
821914
accept();
822915
updateDefaultProxyNets();

src/qt/optionsdialog.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class QCheckBox;
1919
class QDataWidgetMapper;
2020
class QDoubleSpinBox;
2121
class QLayout;
22+
class QRadioButton;
2223
class QSpinBox;
2324
class QString;
2425
class QValueComboBox;
@@ -121,6 +122,12 @@ private Q_SLOTS:
121122
QSpinBox *datacarriersize;
122123
QDoubleSpinBox *datacarriercost;
123124
BitcoinAmountField *dustrelayfee;
125+
QCheckBox *dustdynamic_enable;
126+
QDoubleSpinBox *dustdynamic_multiplier;
127+
QRadioButton *dustdynamic_target;
128+
QSpinBox *dustdynamic_target_blocks;
129+
QRadioButton *dustdynamic_mempool;
130+
QSpinBox *dustdynamic_mempool_kvB;
124131

125132
BitcoinAmountField *blockmintxfee;
126133
QSpinBox *blockmaxsize, *blockprioritysize, *blockmaxweight;

src/qt/optionsmodel.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <netbase.h>
2424
#include <node/chainstatemanager_args.h>
2525
#include <node/context.h>
26+
#include <node/mempool_args.h> // for ParseDustDynamicOpt
2627
#include <outputtype.h>
2728
#include <policy/settings.h>
2829
#include <txdb.h> // for -dbcache defaults
@@ -38,6 +39,7 @@
3839
#include <chrono>
3940
#include <string>
4041
#include <unordered_set>
42+
#include <utility>
4143

4244
#include <QDebug>
4345
#include <QLatin1Char>
@@ -77,6 +79,7 @@ static const char* SettingName(OptionsModel::OptionID option)
7779
case OptionsModel::peerbloomfilters: return "peerbloomfilters";
7880
case OptionsModel::peerblockfilters: return "peerblockfilters";
7981
case OptionsModel::datacarriercost: return "datacarriercost";
82+
case OptionsModel::dustdynamic: return "dustdynamic";
8083
default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option));
8184
}
8285
}
@@ -685,6 +688,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
685688
return qlonglong(node().mempool().m_opts.max_datacarrier_bytes.value_or(0));
686689
case dustrelayfee:
687690
return qlonglong(node().mempool().m_opts.dust_relay_feerate_floor.GetFeePerK());
691+
case dustdynamic:
692+
return QString::fromStdString(SettingToString(setting(), DEFAULT_DUST_DYNAMIC));
688693
case blockmintxfee:
689694
if (gArgs.IsArgSet("-blockmintxfee")) {
690695
return qlonglong(ParseMoney(gArgs.GetArg("-blockmintxfee", "")).value_or(0));
@@ -1230,6 +1235,17 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
12301235
}
12311236
}
12321237
break;
1238+
case dustdynamic:
1239+
if (changed()) {
1240+
const std::string newvalue_str = value.toString().toStdString();
1241+
const util::Result<std::pair<int32_t, int>> parsed = ParseDustDynamicOpt(newvalue_str, 1008 /* FIXME: get from estimator */);
1242+
assert(parsed); // FIXME: what to do if it fails to parse?
1243+
// FIXME: save -prev-<type> for each type
1244+
update(newvalue_str);
1245+
node().mempool().m_opts.dust_relay_target = parsed->first;
1246+
node().mempool().m_opts.dust_relay_multiplier = parsed->second;
1247+
}
1248+
break;
12331249
case blockmintxfee:
12341250
if (changed()) {
12351251
std::string strNv = FormatMoney(value.toLongLong());

src/qt/optionsmodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class OptionsModel : public QAbstractListModel
101101
datacarriercost, // double
102102
datacarriersize,
103103
dustrelayfee,
104+
dustdynamic, // QString
104105
blockmintxfee,
105106
blockmaxsize,
106107
blockprioritysize,

0 commit comments

Comments
 (0)