GUI/Options: Configure dustdynamic using settings

This commit is contained in:
Luke Dashjr 2025-02-04 03:03:22 +00:00
parent 057943b59b
commit 46897eda0e
4 changed files with 124 additions and 7 deletions

View File

@ -21,12 +21,14 @@
#include <interfaces/node.h> #include <interfaces/node.h>
#include <node/chainstatemanager_args.h> #include <node/chainstatemanager_args.h>
#include <netbase.h> #include <netbase.h>
#include <node/mempool_args.h> // for ParseDustDynamicOpt
#include <outputtype.h> #include <outputtype.h>
#include <primitives/transaction.h> // for WITNESS_SCALE_FACTOR #include <primitives/transaction.h> // for WITNESS_SCALE_FACTOR
#include <txdb.h> #include <txdb.h>
#include <txmempool.h> // for maxmempoolMinimum #include <txmempool.h> // for maxmempoolMinimum
#include <util/strencodings.h> #include <util/strencodings.h>
#include <chrono> #include <chrono>
#include <utility>
#include <QApplication> #include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
@ -41,6 +43,7 @@
#include <QLabel> #include <QLabel>
#include <QLocale> #include <QLocale>
#include <QMessageBox> #include <QMessageBox>
#include <QRadioButton>
#include <QSpacerItem> #include <QSpacerItem>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -67,13 +70,15 @@ void OptionsDialog::CreateOptionUI(QBoxLayout * const layout, QWidget * const o,
if (!horizontalLayout) horizontalLayout = new QHBoxLayout(); if (!horizontalLayout) horizontalLayout = new QHBoxLayout();
QLabel * const labelBefore = new QLabel(parent); if (!text_parts[0].isEmpty()) {
labelBefore->setText(text_parts[0]); QLabel * const labelBefore = new QLabel(parent);
labelBefore->setTextFormat(Qt::PlainText); labelBefore->setText(text_parts[0]);
labelBefore->setBuddy(o); labelBefore->setTextFormat(Qt::PlainText);
labelBefore->setToolTip(o->toolTip()); labelBefore->setBuddy(o);
labelBefore->setToolTip(o->toolTip());
horizontalLayout->addWidget(labelBefore);
}
horizontalLayout->addWidget(labelBefore);
horizontalLayout->addWidget(o); horizontalLayout->addWidget(o);
QLabel * const labelAfter = new QLabel(parent); QLabel * const labelAfter = new QLabel(parent);
@ -340,7 +345,67 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
}); });
dustrelayfee = new BitcoinAmountField(groupBox_Spamfiltering); dustrelayfee = new BitcoinAmountField(groupBox_Spamfiltering);
CreateOptionUI(verticalLayout_Spamfiltering, dustrelayfee, tr("Ignore transactions with values that would cost more to spend at a fee rate of %s per kvB.")); CreateOptionUI(verticalLayout_Spamfiltering, dustrelayfee, tr("Ignore transactions with values that would cost more to spend at a fee rate of %s per kvB (\"dust\")."));
auto hlayout = new QHBoxLayout();
dustdynamic_enable = new QCheckBox(groupBox_Spamfiltering);
dustdynamic_enable->setText(tr("Automatically adjust the dust limit upward to"));
hlayout->addWidget(dustdynamic_enable);
dustdynamic_multiplier = new QDoubleSpinBox(groupBox_Spamfiltering);
dustdynamic_multiplier->setDecimals(3);
dustdynamic_multiplier->setStepType(QAbstractSpinBox::DefaultStepType);
dustdynamic_multiplier->setSingleStep(1);
dustdynamic_multiplier->setMinimum(0.001);
dustdynamic_multiplier->setMaximum(65);
dustdynamic_multiplier->setValue(DEFAULT_DUST_RELAY_MULTIPLIER / 1000.0);
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_multiplier, tr("%s times:"), hlayout);
QStyleOptionButton styleoptbtn;
const auto checkbox_indent = dustdynamic_enable->style()->subElementRect(QStyle::SE_CheckBoxIndicator, &styleoptbtn, dustdynamic_enable).width();
hlayout = new QHBoxLayout();
hlayout->addSpacing(checkbox_indent);
dustdynamic_target = new QRadioButton(groupBox_Spamfiltering);
hlayout->addWidget(dustdynamic_target);
dustdynamic_target_blocks = new QSpinBox(groupBox_Spamfiltering);
dustdynamic_target_blocks->setMinimum(2);
dustdynamic_target_blocks->setMaximum(1008); // FIXME: Get this from the fee estimator
dustdynamic_target_blocks->setValue(1008);
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_target_blocks, tr("fee estimate for %s blocks."), hlayout);
// FIXME: Make it possible to click labels to select + focus spinbox
hlayout = new QHBoxLayout();
hlayout->addSpacing(checkbox_indent);
dustdynamic_mempool = new QRadioButton(groupBox_Spamfiltering);
hlayout->addWidget(dustdynamic_mempool);
dustdynamic_mempool_kvB = new QSpinBox(groupBox_Spamfiltering);
dustdynamic_mempool_kvB->setMinimum(1);
dustdynamic_mempool_kvB->setMaximum(std::numeric_limits<int32_t>::max());
dustdynamic_mempool_kvB->setValue(3024000);
CreateOptionUI(verticalLayout_Spamfiltering, dustdynamic_mempool_kvB, tr("the lowest fee of the best known %s kvB of unconfirmed transactions."), hlayout);
connect(dustdynamic_enable, &QAbstractButton::toggled, [this](const bool state){
dustdynamic_multiplier->setEnabled(state);
dustdynamic_target->setEnabled(state);
dustdynamic_mempool->setEnabled(state);
if (state) {
if (!dustdynamic_mempool->isChecked()) dustdynamic_target->setChecked(true);
dustdynamic_target_blocks->setEnabled(dustdynamic_target->isChecked());
dustdynamic_mempool_kvB->setEnabled(dustdynamic_mempool->isChecked());
} else {
dustdynamic_target_blocks->setEnabled(false);
dustdynamic_mempool_kvB->setEnabled(false);
}
});
dustdynamic_enable->toggled(dustdynamic_enable->isChecked());
connect(dustdynamic_target, &QAbstractButton::toggled, [this](const bool state){
dustdynamic_target_blocks->setEnabled(state);
});
connect(dustdynamic_mempool, &QAbstractButton::toggled, [this](const bool state){
dustdynamic_mempool_kvB->setEnabled(state);
});
verticalLayout_Mempool->addWidget(groupBox_Spamfiltering); verticalLayout_Mempool->addWidget(groupBox_Spamfiltering);
@ -645,6 +710,24 @@ void OptionsDialog::setMapper()
mapper->addMapping(datacarriersize, OptionsModel::datacarriersize); mapper->addMapping(datacarriersize, OptionsModel::datacarriersize);
mapper->addMapping(dustrelayfee, OptionsModel::dustrelayfee); mapper->addMapping(dustrelayfee, OptionsModel::dustrelayfee);
QVariant current_dustdynamic = model->data(model->index(OptionsModel::dustdynamic, 0), Qt::EditRole);
const util::Result<std::pair<int32_t, int>> parsed_dustdynamic = ParseDustDynamicOpt(current_dustdynamic.toString().toStdString(), std::numeric_limits<unsigned int>::max());
if (parsed_dustdynamic) {
if (parsed_dustdynamic->first == 0) {
dustdynamic_enable->setChecked(false);
} else {
dustdynamic_multiplier->setValue(parsed_dustdynamic->second / 1000.0);
if (parsed_dustdynamic->first < 0) {
dustdynamic_target->setChecked(true);
dustdynamic_target_blocks->setValue(-parsed_dustdynamic->first);
} else {
dustdynamic_mempool->setChecked(true);
dustdynamic_mempool_kvB->setValue(parsed_dustdynamic->first);
}
dustdynamic_enable->setChecked(true);
}
}
/* Mining tab */ /* Mining tab */
mapper->addMapping(blockmintxfee, OptionsModel::blockmintxfee); mapper->addMapping(blockmintxfee, OptionsModel::blockmintxfee);
@ -817,6 +900,16 @@ void OptionsDialog::on_okButton_clicked()
model->setData(model->index(OptionsModel::mempoolreplacement, 0), mempoolreplacement->itemData(mempoolreplacement->currentIndex())); model->setData(model->index(OptionsModel::mempoolreplacement, 0), mempoolreplacement->itemData(mempoolreplacement->currentIndex()));
if (dustdynamic_enable->isChecked()) {
if (dustdynamic_target->isChecked()) {
model->setData(model->index(OptionsModel::dustdynamic, 0), QStringLiteral("%2*target:%1").arg(dustdynamic_target_blocks->value()).arg(dustdynamic_multiplier->value()));
} else if (dustdynamic_mempool->isChecked()) {
model->setData(model->index(OptionsModel::dustdynamic, 0), QStringLiteral("%2*mempool:%1").arg(dustdynamic_mempool_kvB->value()).arg(dustdynamic_multiplier->value()));
}
} else {
model->setData(model->index(OptionsModel::dustdynamic, 0), "off");
}
mapper->submit(); mapper->submit();
accept(); accept();
updateDefaultProxyNets(); updateDefaultProxyNets();

View File

@ -19,6 +19,7 @@ class QCheckBox;
class QDataWidgetMapper; class QDataWidgetMapper;
class QDoubleSpinBox; class QDoubleSpinBox;
class QLayout; class QLayout;
class QRadioButton;
class QSpinBox; class QSpinBox;
class QString; class QString;
class QValueComboBox; class QValueComboBox;
@ -121,6 +122,12 @@ private:
QSpinBox *datacarriersize; QSpinBox *datacarriersize;
QDoubleSpinBox *datacarriercost; QDoubleSpinBox *datacarriercost;
BitcoinAmountField *dustrelayfee; BitcoinAmountField *dustrelayfee;
QCheckBox *dustdynamic_enable;
QDoubleSpinBox *dustdynamic_multiplier;
QRadioButton *dustdynamic_target;
QSpinBox *dustdynamic_target_blocks;
QRadioButton *dustdynamic_mempool;
QSpinBox *dustdynamic_mempool_kvB;
BitcoinAmountField *blockmintxfee; BitcoinAmountField *blockmintxfee;
QSpinBox *blockmaxsize, *blockprioritysize, *blockmaxweight; QSpinBox *blockmaxsize, *blockprioritysize, *blockmaxweight;

View File

@ -23,6 +23,7 @@
#include <netbase.h> #include <netbase.h>
#include <node/chainstatemanager_args.h> #include <node/chainstatemanager_args.h>
#include <node/context.h> #include <node/context.h>
#include <node/mempool_args.h> // for ParseDustDynamicOpt
#include <outputtype.h> #include <outputtype.h>
#include <policy/settings.h> #include <policy/settings.h>
#include <txdb.h> // for -dbcache defaults #include <txdb.h> // for -dbcache defaults
@ -38,6 +39,7 @@
#include <chrono> #include <chrono>
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include <utility>
#include <QDebug> #include <QDebug>
#include <QLatin1Char> #include <QLatin1Char>
@ -77,6 +79,7 @@ static const char* SettingName(OptionsModel::OptionID option)
case OptionsModel::peerbloomfilters: return "peerbloomfilters"; case OptionsModel::peerbloomfilters: return "peerbloomfilters";
case OptionsModel::peerblockfilters: return "peerblockfilters"; case OptionsModel::peerblockfilters: return "peerblockfilters";
case OptionsModel::datacarriercost: return "datacarriercost"; case OptionsModel::datacarriercost: return "datacarriercost";
case OptionsModel::dustdynamic: return "dustdynamic";
default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option)); default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option));
} }
} }
@ -685,6 +688,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
return qlonglong(node().mempool().m_opts.max_datacarrier_bytes.value_or(0)); return qlonglong(node().mempool().m_opts.max_datacarrier_bytes.value_or(0));
case dustrelayfee: case dustrelayfee:
return qlonglong(node().mempool().m_opts.dust_relay_feerate_floor.GetFeePerK()); return qlonglong(node().mempool().m_opts.dust_relay_feerate_floor.GetFeePerK());
case dustdynamic:
return QString::fromStdString(SettingToString(setting(), DEFAULT_DUST_DYNAMIC));
case blockmintxfee: case blockmintxfee:
if (gArgs.IsArgSet("-blockmintxfee")) { if (gArgs.IsArgSet("-blockmintxfee")) {
return qlonglong(ParseMoney(gArgs.GetArg("-blockmintxfee", "")).value_or(0)); return qlonglong(ParseMoney(gArgs.GetArg("-blockmintxfee", "")).value_or(0));
@ -1230,6 +1235,17 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
} }
} }
break; break;
case dustdynamic:
if (changed()) {
const std::string newvalue_str = value.toString().toStdString();
const util::Result<std::pair<int32_t, int>> parsed = ParseDustDynamicOpt(newvalue_str, 1008 /* FIXME: get from estimator */);
assert(parsed); // FIXME: what to do if it fails to parse?
// FIXME: save -prev-<type> for each type
update(newvalue_str);
node().mempool().m_opts.dust_relay_target = parsed->first;
node().mempool().m_opts.dust_relay_multiplier = parsed->second;
}
break;
case blockmintxfee: case blockmintxfee:
if (changed()) { if (changed()) {
std::string strNv = FormatMoney(value.toLongLong()); std::string strNv = FormatMoney(value.toLongLong());

View File

@ -101,6 +101,7 @@ public:
datacarriercost, // double datacarriercost, // double
datacarriersize, datacarriersize,
dustrelayfee, dustrelayfee,
dustdynamic, // QString
blockmintxfee, blockmintxfee,
blockmaxsize, blockmaxsize,
blockprioritysize, blockprioritysize,