diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 447a6e13e8..0726d506d8 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -312,6 +313,20 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) datacarriersize->setToolTip(tr("While Bitcoin itself does not support attaching arbitrary data to transactions, despite that various methods for disguising it have been devised over the years. Since it is sometimes impractical to detect small spam disguised as ordinary transactions, it is sometimes considered beneficial to tolerate certain kinds of less harmful data attachments.")); CreateOptionUI(verticalLayout_Spamfiltering, datacarriersize, tr("Ignore transactions with additional data larger than %s bytes.")); + datacarriercost = new QDoubleSpinBox(groupBox_Spamfiltering); + datacarriercost->setDecimals(2); + datacarriercost->setStepType(QAbstractSpinBox::DefaultStepType); + datacarriercost->setSingleStep(0.25); + datacarriercost->setMinimum(0.25); + datacarriercost->setMaximum(MAX_BLOCK_SERIALIZED_SIZE); + datacarriercost->setToolTip(tr("As an alternative to, or in addition to, limiting the size of disguised data, you can also configure how it is accounted for in comparison to legitimate transaction data. For example, 1 vbyte per actual byte would count it as equivalent to ordinary transaction data; 0.25 vB/B would allow it to benefit from the so-called \"segwit discount\"; or 2 vB/B would establish a bias toward legitimate transactions.")); + CreateOptionUI(verticalLayout_Spamfiltering, datacarriercost, tr("Weigh embedded data as %s virtual bytes per actual byte.")); + connect(datacarriercost, QOverload::of(&QDoubleSpinBox::valueChanged), [&](double d){ + const double w = d * 4; + const double wf = floor(w); + if (w != wf) datacarriercost->setValue(wf / 4); + }); + 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.")); @@ -612,6 +627,7 @@ void OptionsDialog::setMapper() mapper->addMapping(limitdescendantcount, OptionsModel::limitdescendantcount); mapper->addMapping(limitdescendantsize, OptionsModel::limitdescendantsize); mapper->addMapping(rejectbaremultisig, OptionsModel::rejectbaremultisig); + mapper->addMapping(datacarriercost, OptionsModel::datacarriercost); mapper->addMapping(datacarriersize, OptionsModel::datacarriersize); mapper->addMapping(dustrelayfee, OptionsModel::dustrelayfee); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index cd7051b885..47c7146256 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -17,6 +17,7 @@ QT_BEGIN_NAMESPACE class QBoxLayout; class QCheckBox; class QDataWidgetMapper; +class QDoubleSpinBox; class QSpinBox; class QString; class QValueComboBox; @@ -115,6 +116,7 @@ private: QSpinBox *limitdescendantsize; QCheckBox *rejectbaremultisig; QSpinBox *datacarriersize; + QDoubleSpinBox *datacarriercost; BitcoinAmountField *dustrelayfee; BitcoinAmountField *blockmintxfee; diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 0505264fdf..4219134ede 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include // for DEFAULT_MAX_MEMPOOL_SIZE_MB, DEFAULT_MEMPOOL_EXPIRY_HOURS @@ -75,6 +76,7 @@ static const char* SettingName(OptionsModel::OptionID option) case OptionsModel::maxuploadtarget: return "maxuploadtarget"; case OptionsModel::peerbloomfilters: return "peerbloomfilters"; case OptionsModel::peerblockfilters: return "peerblockfilters"; + case OptionsModel::datacarriercost: return "datacarriercost"; default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option)); } } @@ -673,6 +675,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return qlonglong(node().mempool().m_opts.limits.descendant_size_vbytes / 1'000); case rejectbaremultisig: return !node().mempool().m_opts.permit_bare_multisig; + case datacarriercost: + return double(::g_weight_per_data_byte) / WITNESS_SCALE_FACTOR; case datacarriersize: return qlonglong(node().mempool().m_opts.max_datacarrier_bytes.value_or(0)); case dustrelayfee: @@ -1171,6 +1175,13 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: gArgs.ModifyRWConfigFile("permitbaremultisig", strprintf("%d", fNewValue)); } break; + case datacarriercost: + if (changed()) { + const double nNewSize = value.toDouble(); + update(nNewSize); + ::g_weight_per_data_byte = nNewSize * WITNESS_SCALE_FACTOR; + } + break; case datacarriersize: if (changed()) { const int nNewSize = value.toInt(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 0e6986a5c1..f6f7cc3c3c 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -96,6 +96,7 @@ public: limitdescendantcount, limitdescendantsize, rejectbaremultisig, // bool + datacarriercost, // double datacarriersize, dustrelayfee, blockmintxfee,