Merge rwconf_policy-28+knots

This commit is contained in:
Luke Dashjr 2025-03-05 03:27:08 +00:00
commit 6fd67aa463
20 changed files with 1179 additions and 27 deletions

View File

@ -140,6 +140,7 @@ using util::Join;
using util::ReplaceAll;
using util::ToString;
static constexpr bool DEFAULT_COREPOLICY{false};
static constexpr bool DEFAULT_PROXYRANDOMIZE{true};
static constexpr bool DEFAULT_REST_ENABLE{false};
static constexpr bool DEFAULT_I2P_ACCEPT_INCOMING{true};
@ -494,6 +495,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-confrw=<file>", strprintf("Specify read/write configuration file. Relative paths will be prefixed by the network-specific datadir location (default: %s)", BITCOIN_RW_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-corepolicy", strprintf("Use Bitcoin Core policy defaults (default: %u)", DEFAULT_COREPOLICY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@ -694,7 +696,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxscriptsize", strprintf("Maximum size of scripts we relay and mine, in bytes (default: %s)", DEFAULT_SCRIPT_SIZE_POLICY_LIMIT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", (DEFAULT_MEMPOOL_RBF_POLICY == RBFPolicy::Always)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-mempoolreplacement", strprintf("Set to 0 to disable RBF entirely, \"fee,optin\" to honour RBF opt-out signal, or \"fee,-optin\" to always RBF aka full RBF (default: %s)", "fee,-optin"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-mempooltruc", strprintf("Behaviour for transactions requesting TRUC limits: \"reject\" the transactions entirely, \"accept\" them just like any other, or \"enforce\" to impose their requested restrictions (default: %s)", "enforce"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-mempooltruc", strprintf("Behaviour for transactions requesting TRUC limits: \"reject\" the transactions entirely, \"accept\" them just like any other, or \"enforce\" to impose their requested restrictions (default: %s)", "accept"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-permitbarepubkey", strprintf("Relay legacy pubkey outputs (default: %u)", DEFAULT_PERMIT_BAREPUBKEY), ArgsManager::ALLOW_ANY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
@ -801,6 +803,23 @@ static bool AppInitServers(NodeContext& node)
// Parameter interaction based on rules
void InitParameterInteraction(ArgsManager& args)
{
if (args.GetBoolArg("-corepolicy", DEFAULT_COREPOLICY)) {
args.SoftSetArg("-acceptnonstddatacarrier", "1");
args.SoftSetArg("-bytespersigopstrict", "0");
args.SoftSetArg("-permitbarepubkey", "1");
args.SoftSetArg("-permitbaremultisig", "1");
args.SoftSetArg("-rejectparasites", "0");
args.SoftSetArg("-datacarriercost", "0.25");
args.SoftSetArg("-datacarrierfullcount", "0");
args.SoftSetArg("-datacarriersize", "83");
args.SoftSetArg("-maxscriptsize", strprintf("%s", std::numeric_limits<unsigned int>::max()));
args.SoftSetArg("-mempooltruc", "enforce");
args.SoftSetArg("-spkreuse", "allow");
args.SoftSetArg("-blockprioritysize", "0");
args.SoftSetArg("-blockmaxsize", "4000000");
args.SoftSetArg("-blockmaxweight", "3996000");
}
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (args.IsArgSet("-bind")) {

View File

@ -26,6 +26,7 @@
class BanMan;
class CFeeRate;
class CNodeStats;
class CTxMemPool;
class Coin;
class RPCTimerInterface;
class UniValue;
@ -157,6 +158,8 @@ public:
//! Get total bytes sent.
virtual int64_t getTotalBytesSent() = 0;
virtual CTxMemPool& mempool() = 0;
//! Get mempool size.
virtual size_t getMempoolSize() = 0;

View File

@ -29,11 +29,11 @@ static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
/** Default for -mempoolreplacement; must update docs in init.cpp manually */
static constexpr RBFPolicy DEFAULT_MEMPOOL_RBF_POLICY{RBFPolicy::Always};
/** Default for -mempooltruc; must update docs in init.cpp manually */
static constexpr TRUCPolicy DEFAULT_MEMPOOL_TRUC_POLICY{TRUCPolicy::Enforce};
static constexpr TRUCPolicy DEFAULT_MEMPOOL_TRUC_POLICY{TRUCPolicy::Accept};
/** Whether to fall back to legacy V1 serialization when writing mempool.dat */
static constexpr bool DEFAULT_PERSIST_V1_DAT{false};
/** Default for -acceptnonstddatacarrier */
static constexpr bool DEFAULT_ACCEPT_NON_STD_DATACARRIER{true};
static constexpr bool DEFAULT_ACCEPT_NON_STD_DATACARRIER{false};
/** Default for -acceptnonstdtxn */
static constexpr bool DEFAULT_ACCEPT_NON_STD_TXN{false};

View File

@ -520,6 +520,7 @@ public:
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
PeerManagerInfo GetInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void LimitOrphanTxSize(unsigned int nMaxOrphans) override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayTransaction(const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SetBestBlock(int height, std::chrono::seconds time) override
@ -1952,6 +1953,13 @@ PeerManagerInfo PeerManagerImpl::GetInfo() const
};
}
void PeerManagerImpl::LimitOrphanTxSize(unsigned int nMaxOrphans)
{
LOCK(g_msgproc_mutex);
LOCK2(cs_main, m_tx_download_mutex);
m_orphanage.LimitOrphans(nMaxOrphans, m_rng);
}
int PeerManagerImpl::GetNumberOfPeersWithValidatedDownloads() const
{
AssertLockHeld(m_chainman.GetMutex());

View File

@ -7,6 +7,7 @@
#define BITCOIN_NET_PROCESSING_H
#include <net.h>
#include <threadsafety.h>
#include <txorphanage.h>
#include <validationinterface.h>
@ -95,6 +96,7 @@ public:
/** Get statistics from node state */
virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0;
virtual void LimitOrphanTxSize(unsigned int nMaxOrphans) = 0;
virtual std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() = 0;

View File

@ -425,6 +425,7 @@ public:
}
ArgsManager& args() { return *Assert(Assert(m_context)->args); }
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
CTxMemPool& mempool() override { return *Assert(m_context->mempool); }
NodeContext* m_context{nullptr};
};

View File

@ -73,14 +73,14 @@ BlockCreateOptions BlockCreateOptions::Clamped() const
constexpr size_t theoretical_min_gentx_sz = 1+4+1+36+1+1+4+1+4;
constexpr size_t theoretical_min_gentx_weight = theoretical_min_gentx_sz * WITNESS_SCALE_FACTOR;
CHECK_NONFATAL(options.coinbase_max_additional_size <= MAX_BLOCK_SERIALIZED_SIZE - 1000);
CHECK_NONFATAL(options.coinbase_max_additional_weight <= DEFAULT_BLOCK_MAX_WEIGHT);
CHECK_NONFATAL(options.coinbase_max_additional_weight <= MAX_BLOCK_WEIGHT - 4000);
CHECK_NONFATAL(options.coinbase_max_additional_weight >= theoretical_min_gentx_weight);
CHECK_NONFATAL(options.coinbase_output_max_additional_sigops <= MAX_BLOCK_SIGOPS_COST);
// Limit size to between coinbase_max_additional_size and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity:
options.nBlockMaxSize = std::clamp<size_t>(options.nBlockMaxSize, options.coinbase_max_additional_size, MAX_BLOCK_SERIALIZED_SIZE - 1000);
// Limit weight to between coinbase_max_additional_weight and DEFAULT_BLOCK_MAX_WEIGHT for sanity:
// Limit weight to between coinbase_max_additional_weight and MAX_BLOCK_WEIGHT for sanity:
// Coinbase (reserved) outputs can safely exceed -blockmaxweight, but the rest of the block template will be empty.
options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, options.coinbase_max_additional_weight, DEFAULT_BLOCK_MAX_WEIGHT);
options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, options.coinbase_max_additional_weight, MAX_BLOCK_WEIGHT);
return options;
}

View File

@ -25,13 +25,13 @@ struct MemPoolOptions;
};
/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = MAX_BLOCK_SERIALIZED_SIZE;
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 300000;
/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0;
static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 100000;
/** Minimum priority for transactions to be accepted into the priority area **/
static const double MINIMUM_TX_PRIORITY = COIN * 144 / 250;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{MAX_BLOCK_WEIGHT - 4000};
static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{DEFAULT_BLOCK_MAX_SIZE * WITNESS_SCALE_FACTOR};
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000};
/** The maximum weight for transactions we're willing to relay/mine */
@ -45,21 +45,21 @@ static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/
/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000};
/** Default for -maxscriptsize */
static constexpr unsigned int DEFAULT_SCRIPT_SIZE_POLICY_LIMIT{std::numeric_limits<unsigned int>::max()};
static constexpr unsigned int DEFAULT_SCRIPT_SIZE_POLICY_LIMIT{1650};
/** Default for -bytespersigop */
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
/** Default for -bytespersigopstrict */
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP_STRICT{20};
/** Default for -datacarriercost (multiplied by WITNESS_SCALE_FACTOR) */
static constexpr unsigned int DEFAULT_WEIGHT_PER_DATA_BYTE{1};
static constexpr unsigned int DEFAULT_WEIGHT_PER_DATA_BYTE{4};
/** Default for -rejecttokens */
static constexpr bool DEFAULT_REJECT_TOKENS{false};
/** Default for -permitbarepubkey */
static constexpr bool DEFAULT_PERMIT_BAREPUBKEY{true};
static constexpr bool DEFAULT_PERMIT_BAREPUBKEY{false};
/** Default for -permitbaremultisig */
static constexpr bool DEFAULT_PERMIT_BAREMULTISIG{true};
static constexpr bool DEFAULT_PERMIT_BAREMULTISIG{false};
/** Default for -rejectparasites */
static constexpr bool DEFAULT_REJECT_PARASITES{false};
static constexpr bool DEFAULT_REJECT_PARASITES{true};
/** The maximum number of witness stack items in a standard P2WSH script */
static constexpr unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS{100};
/** The maximum size in bytes of each witness stack item in a standard P2WSH script */
@ -92,12 +92,12 @@ static constexpr unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT_KVB{101};
/** Default for -datacarrier */
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
/**
* Default setting for -datacarriersize. 80 bytes of data, +1 for OP_RETURN,
* +2 for the pushdata opcodes.
* Default setting for -datacarriersize. 40 bytes of data, +1 for OP_RETURN,
* +1 for the pushdata opcode.
*/
static const unsigned int MAX_OP_RETURN_RELAY = 83;
static constexpr unsigned int MAX_OP_RETURN_RELAY{42};
/** Default for -datacarrierfullcount */
static constexpr bool DEFAULT_DATACARRIER_FULLCOUNT{false};
static constexpr bool DEFAULT_DATACARRIER_FULLCOUNT{true};
/**
* An extra transaction can be added to a package, as long as it only has one
* ancestor and is no larger than this. Not really any reason to make this

View File

@ -7,32 +7,181 @@
#include <qt/optionsdialog.h>
#include <qt/forms/ui_optionsdialog.h>
#include <qt/bitcoinamountfield.h>
#include <qt/bitcoinunits.h>
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <common/args.h>
#include <common/system.h>
#include <consensus/consensus.h> // for MAX_BLOCK_SERIALIZED_SIZE
#include <index/blockfilterindex.h>
#include <interfaces/node.h>
#include <node/chainstatemanager_args.h>
#include <netbase.h>
#include <node/mempool_args.h> // for ParseDustDynamicOpt
#include <outputtype.h>
#include <primitives/transaction.h> // for WITNESS_SCALE_FACTOR
#include <txdb.h>
#include <txmempool.h> // for maxmempoolMinimum
#include <util/check.h>
#include <util/strencodings.h>
#include <chrono>
#include <utility>
#include <QApplication>
#include <QBoxLayout>
#include <QDataWidgetMapper>
#include <QDir>
#include <QDoubleSpinBox>
#include <QFontDialog>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QIntValidator>
#include <QLabel>
#include <QLocale>
#include <QMessageBox>
#include <QRadioButton>
#include <QScrollArea>
#include <QScrollBar>
#include <QSpacerItem>
#include <QString>
#include <QStringList>
#include <QSystemTrayIcon>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>
ModScrollArea::ModScrollArea()
{
setWidgetResizable(true);
setFrameShape(QFrame::NoFrame);
setObjectName(QStringLiteral("scroll"));
setStyleSheet("QScrollArea#scroll, QScrollArea#scroll > QWidget > QWidget { background: transparent; } QScrollArea#scroll > QWidget > QScrollBar { background: palette(base); }");
}
ModScrollArea *ModScrollArea::fromWidget(QWidget * const parent, QWidget * const o)
{
auto * const scroll = new ModScrollArea;
scroll->setWidget(o);
return scroll;
}
QSize ModScrollArea::minimumSizeHint() const
{
auto w = widget()->minimumSizeHint().width();
w += verticalScrollBar()->sizeHint().width();
const auto h = fontMetrics().height() * 2;
return QSize(w, h);
}
QSize ModScrollArea::sizeHint() const
{
QSize sz = widget()->sizeHint();
sz.rwidth() += verticalScrollBar()->sizeHint().width();
return sz;
}
void OptionsDialog::FixTabOrder(QWidget * const o)
{
BitcoinAmountField * const af = qobject_cast<BitcoinAmountField *>(o);
if (af) {
prevwidget = af->setupTabChain(prevwidget);
} else {
setTabOrder(prevwidget, o);
prevwidget = o;
}
}
struct CreateOptionUIOpts {
QBoxLayout *horizontal_layout{nullptr};
int stretch{1};
int indent{0};
};
void OptionsDialog::CreateOptionUI(QBoxLayout * const layout, const QString& text, const std::vector<QWidget *>& objs, const CreateOptionUIOpts& opts)
{
Assert(!objs.empty());
auto& first_o = objs[0];
QWidget * const parent = first_o->parentWidget();
QBoxLayout * const horizontalLayout = opts.horizontal_layout ? opts.horizontal_layout : (new QHBoxLayout);
if (opts.indent) horizontalLayout->addSpacing(opts.indent);
int processed{0}, index_start{0};
QWidget *last_widget{nullptr};
while (true) {
int pos = text.indexOf('%', index_start);
int idx;
if (pos == -1) {
pos = text.size();
idx = -1;
} else {
const int pos_next{pos + 1};
const auto char_next = text[pos_next];
idx = (char_next == 's') ? 0 : (char_next.digitValue() - 1);
if (pos_next == text.size() || idx < 0 || idx > 8 || (unsigned)idx >= objs.size()) {
index_start = pos_next;
continue;
}
}
if (processed != pos) {
auto label_text = text.mid(processed, pos - processed);
if (auto last_widget_as_qcheckbox = qobject_cast<QCheckBox*>(last_widget)) {
if (label_text[0].isSpace()) label_text = label_text.mid(1);
last_widget_as_qcheckbox->setText(label_text);
} else {
const auto label = new QLabel(parent);
label->setText(label_text);
label->setTextFormat(Qt::PlainText);
label->setBuddy(first_o);
label->setToolTip(first_o->toolTip());
horizontalLayout->addWidget(label);
}
}
if (idx == -1) break;
last_widget = objs[idx];
horizontalLayout->addWidget(last_widget);
index_start = processed = pos + 2;
}
if (opts.stretch) horizontalLayout->addStretch(opts.stretch);
layout->addLayout(horizontalLayout);
for (auto& o : objs) {
o->setProperty("L", QVariant::fromValue((QLayout*)horizontalLayout));
FixTabOrder(o);
}
}
void OptionsDialog::CreateOptionUI(QBoxLayout * const layout, const QString& text, const std::vector<QWidget *>& objs)
{
CreateOptionUI(layout, text, objs, {});
}
void OptionsDialog::CreateOptionUI(QBoxLayout * const layout, QWidget * const o, const QString& text, QBoxLayout *horizontalLayout)
{
CreateOptionUI(layout, text, {o}, { .horizontal_layout = horizontalLayout, });
}
static void setSiblingsEnabled(QWidget * const o, const bool state)
{
auto layout = o->property("L").value<QLayout*>();
Assert(layout);
// NOTE: QLayout::children does not do what we need here
for (int i = layout->count(); i-- > 0; ) {
QLayoutItem * const layoutitem = layout->itemAt(i);
QWidget * const childwidget = layoutitem->widget();
if (!childwidget) continue;
childwidget->setEnabled(state);
}
}
int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc)
{
@ -140,6 +289,273 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
ui->maxuploadtarget->setMaximum(std::numeric_limits<int>::max());
connect(ui->maxuploadtargetCheckbox, SIGNAL(toggled(bool)), ui->maxuploadtarget, SLOT(setEnabled(bool)));
prevwidget = ui->tabWidget;
walletrbf = new QCheckBox(ui->tabWallet);
walletrbf->setText(tr("Request Replace-By-Fee"));
walletrbf->setToolTip(tr("Indicates that the sender may wish to replace this transaction with a new one paying higher fees (prior to being confirmed). Can be overridden per send."));
ui->verticalLayout_Wallet->insertWidget(0, walletrbf);
FixTabOrder(walletrbf);
/* Network tab */
QLayoutItem *spacer = ui->verticalLayout_Network->takeAt(ui->verticalLayout_Network->count() - 1);
prevwidget = dynamic_cast<QWidgetItem*>(ui->verticalLayout_Network->itemAt(ui->verticalLayout_Network->count() - 1))->widget();
blockreconstructionextratxn = new QSpinBox(ui->tabNetwork);
blockreconstructionextratxn->setMinimum(0);
blockreconstructionextratxn->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(ui->verticalLayout_Network, blockreconstructionextratxn, tr("Keep at most %s extra transactions in memory for compact block reconstruction"));
ui->verticalLayout_Network->addItem(spacer);
prevwidget = ui->peerbloomfilters;
/* Mempool tab */
QWidget * const tabMempool = new QWidget();
QVBoxLayout * const verticalLayout_Mempool = new QVBoxLayout(tabMempool);
ui->tabWidget->insertTab(ui->tabWidget->indexOf(ui->tabWindow), tabMempool, tr("Mem&pool"));
mempoolreplacement = new QValueComboBox(tabMempool);
mempoolreplacement->addItem(QString("never"), QVariant("never"));
mempoolreplacement->addItem(QString("with a higher mining fee, and opt-in"), QVariant("fee,optin"));
mempoolreplacement->addItem(QString("with a higher mining fee (no opt-out)"), QVariant("fee,-optin"));
CreateOptionUI(verticalLayout_Mempool, mempoolreplacement, tr("Transaction &replacement: %s"));
incrementalrelayfee = new BitcoinAmountField(tabMempool);
connect(incrementalrelayfee, SIGNAL(valueChanged()), this, SLOT(incrementalrelayfee_changed()));
CreateOptionUI(verticalLayout_Mempool, incrementalrelayfee, tr("Require transaction fees to be at least %s per kvB higher than transactions they are replacing."));
rejectspkreuse = new QCheckBox(tabMempool);
rejectspkreuse->setText(tr("Disallow most address reuse"));
rejectspkreuse->setToolTip(tr("With this option enabled, your memory pool will only allow each unique payment destination to be used once, effectively deprioritising address reuse. Address reuse is not technically supported, and harms the privacy of all Bitcoin users. It also has limited real-world utility, and has been known to be common with spam."));
verticalLayout_Mempool->addWidget(rejectspkreuse);
FixTabOrder(rejectspkreuse);
mempooltruc = new QValueComboBox(tabMempool);
mempooltruc->addItem(QString("do not relay or mine at all"), QVariant("reject"));
mempooltruc->addItem(QString("handle the same as other transactions"), QVariant("accept"));
mempooltruc->addItem(QString("impose stricter limits requested (DRAFT)"), QVariant("enforce"));
mempooltruc->setToolTip(tr("Some transactions signal a request to limit both themselves and other related transactions to more restrictive expectations. Specifically, this would disallow more than 1 unconfirmed predecessor or spending transaction, as well as smaller size limits (see BIP 431 for details), regardless of what policy you have configured."));
CreateOptionUI(verticalLayout_Mempool, mempooltruc, tr("Transactions requesting more restrictive policy limits (TRUC): %s"));
maxorphantx = new QSpinBox(tabMempool);
maxorphantx->setMinimum(0);
maxorphantx->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Mempool, maxorphantx, tr("Keep at most %s unconnected transactions in memory"));
maxmempool = new QSpinBox(tabMempool);
const int64_t nMempoolSizeMinMB = maxmempoolMinimumBytes(gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT_KVB) * 1'000) / 1'000'000;
maxmempool->setMinimum(nMempoolSizeMinMB);
maxmempool->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Mempool, maxmempool, tr("Keep the transaction memory pool below %s MB"));
mempoolexpiry = new QSpinBox(tabMempool);
mempoolexpiry->setMinimum(1);
mempoolexpiry->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Mempool, mempoolexpiry, tr("Do not keep transactions in memory more than %s hours"));
verticalLayout_Mempool->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
/* Filters tab */
QWidget * const tabFilters = new QWidget();
auto& groupBox_Spamfiltering = tabFilters;
ui->tabWidget->insertTab(ui->tabWidget->indexOf(ui->tabWindow), ModScrollArea::fromWidget(this, groupBox_Spamfiltering), tr("Spam &filtering"));
QVBoxLayout * const verticalLayout_Spamfiltering = new QVBoxLayout(groupBox_Spamfiltering);
rejectunknownscripts = new QCheckBox(groupBox_Spamfiltering);
rejectunknownscripts->setText(tr("Ignore unrecognised receiver scripts"));
rejectunknownscripts->setToolTip(tr("With this option enabled, unrecognised receiver (\"pubkey\") scripts will be ignored. Unrecognisable scripts could be used to bypass further spam filters. If your software is outdated, they may also be used to trick you into thinking you were sent bitcoins that will never confirm."));
verticalLayout_Spamfiltering->addWidget(rejectunknownscripts);
FixTabOrder(rejectunknownscripts);
rejectparasites = new QCheckBox(groupBox_Spamfiltering);
rejectparasites->setText(tr("Reject parasite transactions"));
rejectparasites->setToolTip(tr("With this option enabled, transactions related to parasitic overlay protocols will be ignored. Parasites are transactions using Bitcoin as a technical infrastructure to animate other protocols, unrelated to ordinary money transfers."));
verticalLayout_Spamfiltering->addWidget(rejectparasites);
FixTabOrder(rejectparasites);
rejecttokens = new QCheckBox(groupBox_Spamfiltering);
rejecttokens->setText(tr("Ignore transactions involving non-bitcoin token/asset overlay protocols"));
rejecttokens->setToolTip(tr("With this option enabled, transactions involving non-bitcoin tokens/assets will not be relayed or mined by your node. Due to not having value, and some technical design flaws, token mints and transfers are often spammy and can bog down the network."));
verticalLayout_Spamfiltering->addWidget(rejecttokens);
FixTabOrder(rejecttokens);
minrelaytxfee = new BitcoinAmountField(groupBox_Spamfiltering);
CreateOptionUI(verticalLayout_Spamfiltering, minrelaytxfee, tr("Ignore transactions offering miners less than %s per kvB in transaction fees."));
bytespersigop = new QSpinBox(groupBox_Spamfiltering);
bytespersigop->setMinimum(1);
bytespersigop->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, bytespersigop, tr("Treat each consensus-counted sigop as at least %s bytes."));
bytespersigopstrict = new QSpinBox(groupBox_Spamfiltering);
bytespersigopstrict->setMinimum(1);
bytespersigopstrict->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, bytespersigopstrict, tr("Ignore transactions with fewer than %s bytes per potentially-executed sigop."));
limitancestorcount = new QSpinBox(groupBox_Spamfiltering);
limitancestorcount->setMinimum(1);
limitancestorcount->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, limitancestorcount, tr("Ignore transactions with %s or more unconfirmed ancestors."));
limitancestorsize = new QSpinBox(groupBox_Spamfiltering);
limitancestorsize->setMinimum(1);
limitancestorsize->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, limitancestorsize, tr("Ignore transactions whose size with all unconfirmed ancestors exceeds %s kilobytes."));
limitdescendantcount = new QSpinBox(groupBox_Spamfiltering);
limitdescendantcount->setMinimum(1);
limitdescendantcount->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, limitdescendantcount, tr("Ignore transactions if any ancestor would have %s or more unconfirmed descendants."));
limitdescendantsize = new QSpinBox(groupBox_Spamfiltering);
limitdescendantsize->setMinimum(1);
limitdescendantsize->setMaximum(std::numeric_limits<int>::max());
CreateOptionUI(verticalLayout_Spamfiltering, limitdescendantsize, tr("Ignore transactions if any ancestor would have more than %s kilobytes of unconfirmed descendants."));
rejectbarepubkey = new QCheckBox(groupBox_Spamfiltering);
rejectbarepubkey->setText(tr("Ignore bare/exposed public keys (pay-to-IP)"));
rejectbarepubkey->setToolTip(tr("Spam is sometimes disguised to appear as if it is a deprecated pay-to-IP (bare pubkey) transaction, where the \"key\" is actually arbitrary data (not a real key) instead. Support for pay-to-IP was only ever supported by Satoshi's early Bitcoin wallet, which has been abandoned since 2011."));
verticalLayout_Spamfiltering->addWidget(rejectbarepubkey);
FixTabOrder(rejectbarepubkey);
rejectbaremultisig = new QCheckBox(groupBox_Spamfiltering);
rejectbaremultisig->setText(tr("Ignore bare/exposed \"multisig\" scripts"));
rejectbaremultisig->setToolTip(tr("Spam is sometimes disguised to appear as if it is an old-style N-of-M multi-party transaction, where most of the keys are really bogus. At the same time, legitimate multi-party transactions typically have always used P2SH format (which is not filtered by this option), which is more secure."));
verticalLayout_Spamfiltering->addWidget(rejectbaremultisig);
FixTabOrder(rejectbaremultisig);
maxscriptsize = new QSpinBox(groupBox_Spamfiltering);
maxscriptsize->setMinimum(0);
maxscriptsize->setMaximum(std::numeric_limits<int>::max());
maxscriptsize->setToolTip(tr("There may be rare smart contracts that require a large amount of code, but more often a larger code segment is actually just spam finding new ways to try to evade filtering. 1650 bytes is sometimes considered the high end of what might be normal, usually for N-of-20 multisig."));
CreateOptionUI(verticalLayout_Spamfiltering, maxscriptsize, tr("Ignore transactions with smart contract code larger than %s bytes."));
datacarriersize = new QSpinBox(groupBox_Spamfiltering);
datacarriersize->setMinimum(0);
datacarriersize->setMaximum(std::numeric_limits<int>::max());
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<double>::of(&QDoubleSpinBox::valueChanged), [&](double d){
const double w = d * 4;
const double wf = floor(w);
if (w != wf) datacarriercost->setValue(wf / 4);
});
rejectnonstddatacarrier = new QCheckBox(groupBox_Spamfiltering);
rejectnonstddatacarrier->setText(tr("Ignore data embedded with non-standard formats"));
rejectnonstddatacarrier->setToolTip(tr("Some attempts to spam Bitcoin intentionally use non-standard formats in an attempt to bypass the datacarrier limits. Without this option, %1 will attempt to detect these and enforce the intended limits. By enabling this option, your node will ignore these transactions entirely (when detected) even if they fall within the configured limits otherwise."));
verticalLayout_Spamfiltering->addWidget(rejectnonstddatacarrier);
FixTabOrder(rejectnonstddatacarrier);
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 (\"dust\")."));
dustdynamic_enable = new QCheckBox(groupBox_Spamfiltering);
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, tr("%1 Automatically adjust the dust limit upward to %2 times:"), {dustdynamic_enable, dustdynamic_multiplier});
QStyleOptionButton styleoptbtn;
const auto checkbox_indent = dustdynamic_enable->style()->subElementRect(QStyle::SE_CheckBoxIndicator, &styleoptbtn, dustdynamic_enable).width();
dustdynamic_target = new QRadioButton(groupBox_Spamfiltering);
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, tr("%1 fee estimate for %2 blocks."), {dustdynamic_target, dustdynamic_target_blocks}, { .indent = checkbox_indent, });
// FIXME: Make it possible to click labels to select + focus spinbox
dustdynamic_mempool = new QRadioButton(groupBox_Spamfiltering);
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, tr("%1 the lowest fee of the best known %2 kvB of unconfirmed transactions."), {dustdynamic_mempool, dustdynamic_mempool_kvB}, { .indent = checkbox_indent, });
const auto dustdynamic_enable_toggled = [this](const bool state){
dustdynamic_multiplier->setEnabled(state);
setSiblingsEnabled(dustdynamic_target_blocks, state);
setSiblingsEnabled(dustdynamic_mempool_kvB, 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());
}
};
connect(dustdynamic_enable, &QAbstractButton::toggled, dustdynamic_enable_toggled);
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);
});
connect(rejectunknownscripts, &QAbstractButton::toggled, [this, dustdynamic_enable_toggled](const bool state){
rejectbarepubkey->setEnabled(state);
rejectbaremultisig->setEnabled(state);
rejectparasites->setEnabled(state);
rejecttokens->setEnabled(state);
setSiblingsEnabled(dustrelayfee, state);
setSiblingsEnabled(maxscriptsize, state);
setSiblingsEnabled(dustdynamic_multiplier, state);
dustdynamic_enable_toggled(state && dustdynamic_enable->isChecked());
});
verticalLayout_Spamfiltering->addStretch(1);
/* Mining tab */
QWidget * const tabMining = new QWidget();
QVBoxLayout * const verticalLayout_Mining = new QVBoxLayout(tabMining);
ui->tabWidget->insertTab(ui->tabWidget->indexOf(ui->tabWindow), tabMining, tr("M&ining"));
verticalLayout_Mining->addWidget(new QLabel(tr("<strong>Note that mining is heavily influenced by the settings on the Mempool and Spam filtering tabs.</strong>")));
blockmintxfee = new BitcoinAmountField(tabMining);
CreateOptionUI(verticalLayout_Mining, blockmintxfee, tr("Only mine transactions paying a fee of at least %s per kvB."));
blockmaxsize = new QSpinBox(tabMining);
blockmaxsize->setMinimum(1);
blockmaxsize->setMaximum((MAX_BLOCK_SERIALIZED_SIZE - 1000) / 1000);
connect(blockmaxsize, SIGNAL(valueChanged(int)), this, SLOT(blockmaxsize_changed(int)));
CreateOptionUI(verticalLayout_Mining, blockmaxsize, tr("Never mine a block larger than %s kB."));
blockprioritysize = new QSpinBox(tabMining);
blockprioritysize->setMinimum(0);
blockprioritysize->setMaximum(blockmaxsize->maximum());
connect(blockprioritysize, SIGNAL(valueChanged(int)), this, SLOT(blockmaxsize_increase(int)));
CreateOptionUI(verticalLayout_Mining, blockprioritysize, tr("Mine first %s kB of transactions sorted by coin-age priority."));
blockmaxweight = new QSpinBox(tabMining);
blockmaxweight->setMinimum(1);
blockmaxweight->setMaximum((MAX_BLOCK_WEIGHT-4000) / 1000);
connect(blockmaxweight, SIGNAL(valueChanged(int)), this, SLOT(blockmaxweight_changed(int)));
CreateOptionUI(verticalLayout_Mining, blockmaxweight, tr("Never mine a block weighing more than %s kWU."));
verticalLayout_Mining->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
/* Window elements init */
#ifdef Q_OS_MACOS
/* remove Window tab on Mac */
@ -233,6 +649,8 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
ui->qrFont_preview->setVisible(false);
#endif
adjustSize();
GUIUtil::handleCloseWindowShortcut(this);
}
@ -296,6 +714,8 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->peerbloomfilters, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->peerblockfilters, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Mempool */
connect(rejectspkreuse, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Display */
connect(ui->lang, qOverload<>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); });
connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
@ -326,6 +746,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->pruneSizeMiB, OptionsModel::PruneSizeMiB);
/* Wallet */
mapper->addMapping(walletrbf, OptionsModel::walletrbf);
mapper->addMapping(ui->addressType, OptionsModel::addresstype);
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
@ -372,6 +793,75 @@ void OptionsDialog::setMapper()
ui->peerblockfilters->setToolTip(ui->peerblockfilters->toolTip() + " " + tr("(only available if enabled at least once before turning on pruning)"));
}
mapper->addMapping(blockreconstructionextratxn, OptionsModel::blockreconstructionextratxn);
/* Mempool tab */
QVariant current_mempoolreplacement = model->data(model->index(OptionsModel::mempoolreplacement, 0), Qt::EditRole);
int current_mempoolreplacement_index = mempoolreplacement->findData(current_mempoolreplacement);
if (current_mempoolreplacement_index == -1) {
mempoolreplacement->addItem(current_mempoolreplacement.toString(), current_mempoolreplacement);
current_mempoolreplacement_index = mempoolreplacement->count() - 1;
}
mempoolreplacement->setCurrentIndex(current_mempoolreplacement_index);
QVariant current_mempooltruc = model->data(model->index(OptionsModel::mempooltruc, 0), Qt::EditRole);
int current_mempooltruc_index = mempooltruc->findData(current_mempooltruc);
if (current_mempooltruc_index == -1) {
mempooltruc->addItem(current_mempooltruc.toString(), current_mempooltruc);
current_mempooltruc_index = mempooltruc->count() - 1;
}
mempooltruc->setCurrentIndex(current_mempooltruc_index);
mapper->addMapping(maxorphantx, OptionsModel::maxorphantx);
mapper->addMapping(maxmempool, OptionsModel::maxmempool);
mapper->addMapping(incrementalrelayfee, OptionsModel::incrementalrelayfee);
mapper->addMapping(mempoolexpiry, OptionsModel::mempoolexpiry);
mapper->addMapping(rejectunknownscripts, OptionsModel::rejectunknownscripts);
mapper->addMapping(rejectparasites, OptionsModel::rejectparasites);
mapper->addMapping(rejecttokens, OptionsModel::rejecttokens);
mapper->addMapping(rejectspkreuse, OptionsModel::rejectspkreuse);
mapper->addMapping(minrelaytxfee, OptionsModel::minrelaytxfee);
mapper->addMapping(bytespersigop, OptionsModel::bytespersigop);
mapper->addMapping(bytespersigopstrict, OptionsModel::bytespersigopstrict);
mapper->addMapping(limitancestorcount, OptionsModel::limitancestorcount);
mapper->addMapping(limitancestorsize, OptionsModel::limitancestorsize);
mapper->addMapping(limitdescendantcount, OptionsModel::limitdescendantcount);
mapper->addMapping(limitdescendantsize, OptionsModel::limitdescendantsize);
mapper->addMapping(rejectbarepubkey, OptionsModel::rejectbarepubkey);
mapper->addMapping(rejectbaremultisig, OptionsModel::rejectbaremultisig);
mapper->addMapping(maxscriptsize, OptionsModel::maxscriptsize);
mapper->addMapping(datacarriercost, OptionsModel::datacarriercost);
mapper->addMapping(datacarriersize, OptionsModel::datacarriersize);
mapper->addMapping(rejectnonstddatacarrier, OptionsModel::rejectnonstddatacarrier);
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 */
mapper->addMapping(blockmintxfee, OptionsModel::blockmintxfee);
mapper->addMapping(blockmaxsize, OptionsModel::blockmaxsize);
mapper->addMapping(blockprioritysize, OptionsModel::blockprioritysize);
mapper->addMapping(blockmaxweight, OptionsModel::blockmaxweight);
/* Window */
#ifndef Q_OS_MACOS
if (QSystemTrayIcon::isSystemTrayAvailable()) {
@ -404,6 +894,42 @@ void OptionsDialog::setOkButtonState(bool fState)
ui->okButton->setEnabled(fState);
}
void OptionsDialog::incrementalrelayfee_changed()
{
if (incrementalrelayfee->value() > minrelaytxfee->value()) {
minrelaytxfee->setValue(incrementalrelayfee->value());
}
}
void OptionsDialog::blockmaxsize_changed(int i)
{
if (blockprioritysize->value() > i) {
blockprioritysize->setValue(i);
}
if (blockmaxweight->value() < i) {
blockmaxweight->setValue(i);
} else if (blockmaxweight->value() > i * WITNESS_SCALE_FACTOR) {
blockmaxweight->setValue(i * WITNESS_SCALE_FACTOR);
}
}
void OptionsDialog::blockmaxsize_increase(int i)
{
if (blockmaxsize->value() < i) {
blockmaxsize->setValue(i);
}
}
void OptionsDialog::blockmaxweight_changed(int i)
{
if (blockmaxsize->value() < i / WITNESS_SCALE_FACTOR) {
blockmaxsize->setValue(i / WITNESS_SCALE_FACTOR);
} else if (blockmaxsize->value() > i) {
blockmaxsize->setValue(i);
}
}
void OptionsDialog::on_resetButton_clicked()
{
if (model) {
@ -419,14 +945,25 @@ void OptionsDialog::on_resetButton_clicked()
with a client shutdown. */
reset_dialog_text.append(tr("Client will be shut down. Do you want to proceed?"));
//: Window title text of pop-up window shown when the user has chosen to reset options.
QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm options reset"),
reset_dialog_text, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
QStringList items;
QString strPrefix = tr("Use policy defaults for %1");
items << strPrefix.arg(tr(PACKAGE_NAME));
items << strPrefix.arg(tr("Bitcoin Core")+" ");
if (btnRetVal == QMessageBox::Cancel)
QInputDialog dialog(this);
dialog.setWindowTitle(tr("Confirm options reset"));
dialog.setLabelText(reset_dialog_text);
dialog.setComboBoxItems(items);
dialog.setTextValue(items[0]);
dialog.setComboBoxEditable(false);
if (!dialog.exec()) {
return;
}
/* reset all options and close GUI */
model->Reset();
model->setData(model->index(OptionsModel::corepolicy, 0), items.indexOf(dialog.textValue()));
close();
Q_EMIT quitOnReset();
}
@ -488,6 +1025,19 @@ void OptionsDialog::on_okButton_clicked()
model->setData(model->index(OptionsModel::maxuploadtarget, 0), 0);
}
model->setData(model->index(OptionsModel::mempoolreplacement, 0), mempoolreplacement->itemData(mempoolreplacement->currentIndex()));
model->setData(model->index(OptionsModel::mempooltruc, 0), mempooltruc->itemData(mempooltruc->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();
accept();
updateDefaultProxyNets();

View File

@ -6,16 +6,42 @@
#define BITCOIN_QT_OPTIONSDIALOG_H
#include <QDialog>
#include <QScrollArea>
#include <QValidator>
class BitcoinAmountField;
class ClientModel;
class OptionsModel;
class QValidatedLineEdit;
QT_BEGIN_NAMESPACE
class QBoxLayout;
class QCheckBox;
class QDataWidgetMapper;
class QDoubleSpinBox;
class QBoxLayout;
class QRadioButton;
class QSpinBox;
class QString;
class QValueComboBox;
class QWidget;
QT_END_NAMESPACE
struct CreateOptionUIOpts;
/** QScrollArea, but returning reasonable size hints.
*/
class ModScrollArea : public QScrollArea {
Q_OBJECT
public:
ModScrollArea();
static ModScrollArea *fromWidget(QWidget *parent, QWidget *o);
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
};
namespace Ui {
class OptionsDialog;
}
@ -69,6 +95,11 @@ private Q_SLOTS:
void updateDefaultProxyNets();
void checkLineEdit();
void incrementalrelayfee_changed();
void blockmaxsize_changed(int);
void blockmaxsize_increase(int);
void blockmaxweight_changed(int);
Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
void quitOnReset();
@ -78,6 +109,50 @@ private:
ClientModel* m_client_model{nullptr};
OptionsModel* model{nullptr};
QDataWidgetMapper* mapper{nullptr};
QWidget *prevwidget{nullptr};
void FixTabOrder(QWidget *);
void CreateOptionUI(QBoxLayout *, const QString& text, const std::vector<QWidget *>&, const CreateOptionUIOpts&);
void CreateOptionUI(QBoxLayout *, const QString& text, const std::vector<QWidget *>&);
void CreateOptionUI(QBoxLayout *, QWidget *, const QString& text, QBoxLayout *horizontalLayout = nullptr);
QCheckBox *walletrbf;
QSpinBox *blockreconstructionextratxn;
QValueComboBox *mempoolreplacement;
QValueComboBox *mempooltruc;
QSpinBox *maxorphantx;
BitcoinAmountField *incrementalrelayfee;
QSpinBox *maxmempool;
QSpinBox *mempoolexpiry;
QCheckBox *rejectunknownscripts;
QCheckBox *rejectparasites;
QCheckBox *rejecttokens;
QCheckBox *rejectspkreuse;
BitcoinAmountField *minrelaytxfee;
QSpinBox *bytespersigop, *bytespersigopstrict;
QSpinBox *limitancestorcount;
QSpinBox *limitancestorsize;
QSpinBox *limitdescendantcount;
QSpinBox *limitdescendantsize;
QCheckBox *rejectbarepubkey;
QCheckBox *rejectbaremultisig;
QSpinBox *maxscriptsize;
QSpinBox *datacarriersize;
QDoubleSpinBox *datacarriercost;
QCheckBox *rejectnonstddatacarrier;
BitcoinAmountField *dustrelayfee;
QCheckBox *dustdynamic_enable;
QDoubleSpinBox *dustdynamic_multiplier;
QRadioButton *dustdynamic_target;
QSpinBox *dustdynamic_target_blocks;
QRadioButton *dustdynamic_mempool;
QSpinBox *dustdynamic_mempool_kvB;
BitcoinAmountField *blockmintxfee;
QSpinBox *blockmaxsize, *blockprioritysize, *blockmaxweight;
};
#endif // BITCOIN_QT_OPTIONSDIALOG_H

View File

@ -12,16 +12,22 @@
#include <chainparams.h>
#include <common/args.h>
#include <consensus/consensus.h>
#include <index/blockfilterindex.h>
#include <interfaces/node.h>
#include <kernel/mempool_options.h> // for DEFAULT_MAX_MEMPOOL_SIZE_MB, DEFAULT_MEMPOOL_EXPIRY_HOURS
#include <mapport.h>
#include <policy/settings.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <node/chainstatemanager_args.h>
#include <node/context.h>
#include <node/mempool_args.h> // for ParseDustDynamicOpt
#include <outputtype.h>
#include <policy/settings.h>
#include <txdb.h> // for -dbcache defaults
#include <util/moneystr.h> // for FormatMoney
#include <util/string.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
@ -30,8 +36,10 @@
#include <interfaces/wallet.h>
#endif
#include <chrono>
#include <string>
#include <unordered_set>
#include <utility>
#include <QDebug>
#include <QLatin1Char>
@ -57,6 +65,7 @@ static const char* SettingName(OptionsModel::OptionID option)
case OptionsModel::MapPortNatpmp: return "natpmp";
case OptionsModel::Listen: return "listen";
case OptionsModel::Server: return "server";
case OptionsModel::walletrbf: return "walletrbf";
case OptionsModel::addresstype: return "addresstype";
case OptionsModel::PruneSizeMiB: return "prune";
case OptionsModel::PruneTristate: return "prune";
@ -70,6 +79,36 @@ static const char* SettingName(OptionsModel::OptionID option)
case OptionsModel::maxuploadtarget: return "maxuploadtarget";
case OptionsModel::peerbloomfilters: return "peerbloomfilters";
case OptionsModel::peerblockfilters: return "peerblockfilters";
case OptionsModel::mempoolreplacement: return "mempoolreplacement";
case OptionsModel::mempooltruc: return "mempooltruc";
case OptionsModel::maxorphantx: return "maxorphantx";
case OptionsModel::maxmempool: return "maxmempool";
case OptionsModel::incrementalrelayfee: return "incrementalrelayfee";
case OptionsModel::mempoolexpiry: return "mempoolexpiry";
case OptionsModel::rejectunknownscripts: return "rejectunknownscripts";
case OptionsModel::rejectparasites: return "rejectparasites";
case OptionsModel::rejecttokens: return "rejecttokens";
case OptionsModel::rejectspkreuse: return "rejectspkreuse";
case OptionsModel::minrelaytxfee: return "minrelaytxfee";
case OptionsModel::bytespersigop: return "bytespersigop";
case OptionsModel::bytespersigopstrict: return "bytespersigopstrict";
case OptionsModel::limitancestorcount: return "limitancestorcount";
case OptionsModel::limitancestorsize: return "limitancestorsize";
case OptionsModel::limitdescendantcount: return "limitdescendantcount";
case OptionsModel::limitdescendantsize: return "limitdescendantsize";
case OptionsModel::rejectbarepubkey: return "rejectbarepubkey";
case OptionsModel::rejectbaremultisig: return "rejectbaremultisig";
case OptionsModel::maxscriptsize: return "maxscriptsize";
case OptionsModel::datacarriercost: return "datacarriercost";
case OptionsModel::datacarriersize: return "datacarriersize";
case OptionsModel::rejectnonstddatacarrier: return "rejectnonstddatacarrier";
case OptionsModel::dustrelayfee: return "dustrelayfee";
case OptionsModel::dustdynamic: return "dustdynamic";
case OptionsModel::blockmintxfee: return "blockmintxfee";
case OptionsModel::blockmaxsize: return "blockmaxsize";
case OptionsModel::blockprioritysize: return "blockprioritysize";
case OptionsModel::blockmaxweight: return "blockmaxweight";
case OptionsModel::blockreconstructionextratxn: return "blockreconstructionextratxn";
default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option));
}
}
@ -203,6 +242,26 @@ OptionsModel::FontChoice OptionsModel::FontChoiceFromString(const QString& s)
}
}
static QString CanonicalMempoolReplacement(const OptionsModel& model)
{
switch (model.node().mempool().m_opts.rbf_policy) {
case RBFPolicy::Never: return "never";
case RBFPolicy::OptIn: return "fee,optin";
case RBFPolicy::Always: return "fee,-optin";
}
assert(0);
}
static QString CanonicalMempoolTRUC(const OptionsModel& model)
{
switch (model.node().mempool().m_opts.truc_policy) {
case TRUCPolicy::Reject: return "reject";
case TRUCPolicy::Accept: return "accept";
case TRUCPolicy::Enforce: return "enforce";
}
assert(0);
}
OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent) :
QAbstractListModel(parent), m_node{node}
{
@ -335,7 +394,9 @@ bool OptionsModel::Init(bilingual_str& error)
addOverriddenOption("-port");
// rwconf settings that require a restart
// Caution: This is before general initialisation occurs!
f_peerbloomfilters = gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS);
f_rejectspkreuse = !(gArgs.GetArg("-spkreuse", DEFAULT_SPKREUSE) == "allow" || gArgs.GetBoolArg("-spkreuse", false));
// Display
if (settings.contains("FontForMoney")) {
@ -596,6 +657,10 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
return QVariant::fromValue(m_font_qrcodes);
case PeersTabAlternatingRowColors:
return m_peers_tab_alternating_row_colors;
#ifdef ENABLE_WALLET
case walletrbf:
return gArgs.GetBoolArg("-walletrbf", wallet::DEFAULT_WALLET_RBF);
#endif
case CoinControlFeatures:
return fCoinControlFeatures;
case EnablePSBTControls:
@ -622,6 +687,70 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
return f_peerbloomfilters;
case peerblockfilters:
return gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS);
case mempoolreplacement:
return CanonicalMempoolReplacement(*this);
case mempooltruc:
return CanonicalMempoolTRUC(*this);
case maxorphantx:
return qlonglong(gArgs.GetIntArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
case maxmempool:
return qlonglong(node().mempool().m_opts.max_size_bytes / 1'000'000);
case incrementalrelayfee:
return qlonglong(node().mempool().m_opts.incremental_relay_feerate.GetFeePerK());
case mempoolexpiry:
return qlonglong(std::chrono::duration_cast<std::chrono::hours>(node().mempool().m_opts.expiry).count());
case rejectunknownscripts:
return node().mempool().m_opts.require_standard;
case rejectparasites:
return node().mempool().m_opts.reject_parasites;
case rejecttokens:
return node().mempool().m_opts.reject_tokens;
case rejectspkreuse:
return f_rejectspkreuse;
case minrelaytxfee:
return qlonglong(node().mempool().m_opts.min_relay_feerate.GetFeePerK());
case bytespersigop:
return nBytesPerSigOp;
case bytespersigopstrict:
return nBytesPerSigOpStrict;
case limitancestorcount:
return qlonglong(node().mempool().m_opts.limits.ancestor_count);
case limitancestorsize:
return qlonglong(node().mempool().m_opts.limits.ancestor_size_vbytes / 1'000);
case limitdescendantcount:
return qlonglong(node().mempool().m_opts.limits.descendant_count);
case limitdescendantsize:
return qlonglong(node().mempool().m_opts.limits.descendant_size_vbytes / 1'000);
case rejectbarepubkey:
return !node().mempool().m_opts.permit_bare_pubkey;
case rejectbaremultisig:
return !node().mempool().m_opts.permit_bare_multisig;
case maxscriptsize:
return ::g_script_size_policy_limit;
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 rejectnonstddatacarrier:
return !node().mempool().m_opts.accept_non_std_datacarrier;
case dustrelayfee:
return qlonglong(node().mempool().m_opts.dust_relay_feerate_floor.GetFeePerK());
case dustdynamic:
return QString::fromStdString(SettingToString(setting(), DEFAULT_DUST_DYNAMIC));
case blockmintxfee:
if (gArgs.IsArgSet("-blockmintxfee")) {
return qlonglong(ParseMoney(gArgs.GetArg("-blockmintxfee", "")).value_or(0));
} else {
return qlonglong(DEFAULT_BLOCK_MIN_TX_FEE);
}
case blockmaxsize:
return qlonglong(gArgs.GetIntArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE) / 1000);
case blockprioritysize:
return qlonglong(gArgs.GetIntArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE) / 1000);
case blockmaxweight:
return qlonglong(gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT) / 1000);
case blockreconstructionextratxn:
return qlonglong(gArgs.GetIntArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
default:
return QVariant();
}
@ -837,6 +966,24 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
settings.setValue("PeersTabAlternatingRowColors", m_peers_tab_alternating_row_colors);
Q_EMIT peersTabAlternatingRowColorsChanged(m_peers_tab_alternating_row_colors);
break;
#ifdef ENABLE_WALLET
case walletrbf:
if (changed()) {
const bool fNewValue = value.toBool();
const std::string newvalue_str = strprintf("%d", fNewValue);
gArgs.ModifyRWConfigFile("walletrbf", newvalue_str);
gArgs.ForceSetArg("-walletrbf", newvalue_str);
for (auto& wallet_interface : m_node.walletLoader().getWallets()) {
wallet::CWallet *wallet;
if (wallet_interface && (wallet = wallet_interface->wallet())) {
wallet->m_signal_rbf = fNewValue;
} else {
setRestartRequired(true);
}
}
}
break;
#endif
case CoinControlFeatures:
fCoinControlFeatures = value.toBool();
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
@ -925,6 +1072,308 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
}
break;
}
case mempoolreplacement:
{
if (changed()) {
QString nv = value.toString();
if (nv == "never") {
node().mempool().m_opts.rbf_policy = RBFPolicy::Never;
node().updateRwSetting("mempoolfullrbf", "0");
} else if (nv == "fee,optin") {
node().mempool().m_opts.rbf_policy = RBFPolicy::OptIn;
node().updateRwSetting("mempoolfullrbf", "0");
} else { // "fee,-optin"
node().mempool().m_opts.rbf_policy = RBFPolicy::Always;
node().updateRwSetting("mempoolfullrbf", "1");
}
gArgs.ModifyRWConfigFile("mempoolreplacement", nv.toStdString());
}
break;
}
case mempooltruc:
{
if (changed()) {
QString nv = value.toString();
if (nv == "reject") {
node().mempool().m_opts.truc_policy = TRUCPolicy::Reject;
} else if (nv == "accept") {
node().mempool().m_opts.truc_policy = TRUCPolicy::Accept;
} else if (nv == "enforce") {
node().mempool().m_opts.truc_policy = TRUCPolicy::Enforce;
}
node().updateRwSetting("mempooltruc", nv.toStdString());
}
break;
}
case maxorphantx:
{
if (changed()) {
unsigned int nMaxOrphanTx = gArgs.GetIntArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS);
unsigned int nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
gArgs.ForceSetArg("-maxorphantx", strNv);
gArgs.ModifyRWConfigFile("maxorphantx", strNv);
if (nNv < nMaxOrphanTx) {
assert(node().context() && node().context()->peerman);
node().context()->peerman->LimitOrphanTxSize(nNv);
}
}
break;
}
case maxmempool:
{
if (changed()) {
long long nOldValue = node().mempool().m_opts.max_size_bytes;
long long nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.max_size_bytes = nNv * 1'000'000;
gArgs.ForceSetArg("-maxmempool", strNv);
gArgs.ModifyRWConfigFile("maxmempool", strNv);
if (nNv < nOldValue) {
LOCK(cs_main);
auto node_ctx = node().context();
assert(node_ctx && node_ctx->mempool && node_ctx->chainman);
auto& active_chainstate = node_ctx->chainman->ActiveChainstate();
LimitMempoolSize(*node_ctx->mempool, active_chainstate.CoinsTip());
}
}
break;
}
case incrementalrelayfee:
if (changed()) {
CAmount nNv = value.toLongLong();
gArgs.ModifyRWConfigFile("incrementalrelayfee", FormatMoney(nNv));
node().mempool().m_opts.incremental_relay_feerate = CFeeRate(nNv);
}
break;
case mempoolexpiry:
{
if (changed()) {
const auto old_value = node().mempool().m_opts.expiry;
const std::chrono::hours new_value{value.toLongLong()};
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.expiry = new_value;
gArgs.ForceSetArg("-mempoolexpiry", strNv);
gArgs.ModifyRWConfigFile("mempoolexpiry", strNv);
if (new_value < old_value) {
LOCK(cs_main);
auto node_ctx = node().context();
assert(node_ctx && node_ctx->mempool && node_ctx->chainman);
auto& active_chainstate = node_ctx->chainman->ActiveChainstate();
LimitMempoolSize(*node_ctx->mempool, active_chainstate.CoinsTip());
}
}
break;
}
case rejectunknownscripts:
{
if (changed()) {
const bool fNewValue = value.toBool();
node().mempool().m_opts.require_standard = fNewValue;
// This option is inverted in the config:
gArgs.ModifyRWConfigFile("acceptnonstdtxn", strprintf("%d", ! fNewValue));
}
break;
}
case rejectparasites:
{
if (changed()) {
const bool nv = value.toBool();
node().mempool().m_opts.reject_parasites = nv;
node().updateRwSetting("rejectparasites", nv);
}
break;
}
case rejecttokens:
{
if (changed()) {
const bool nv = value.toBool();
node().mempool().m_opts.reject_tokens = nv;
node().updateRwSetting("rejecttokens", nv);
}
break;
}
case rejectspkreuse:
if (changed()) {
const bool fNewValue = value.toBool();
gArgs.ModifyRWConfigFile("spkreuse", fNewValue ? "conflict" : "allow");
f_rejectspkreuse = fNewValue;
setRestartRequired(true);
}
break;
case minrelaytxfee:
if (changed()) {
CAmount nNv = value.toLongLong();
gArgs.ModifyRWConfigFile("minrelaytxfee", FormatMoney(nNv));
node().mempool().m_opts.min_relay_feerate = CFeeRate(nNv);
}
break;
case bytespersigop:
if (changed()) {
gArgs.ModifyRWConfigFile("bytespersigop", value.toString().toStdString());
nBytesPerSigOp = value.toLongLong();
}
break;
case bytespersigopstrict:
if (changed()) {
gArgs.ModifyRWConfigFile("bytespersigopstrict", value.toString().toStdString());
nBytesPerSigOpStrict = value.toLongLong();
}
break;
case limitancestorcount:
if (changed()) {
long long nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.limits.ancestor_count = nNv;
gArgs.ForceSetArg("-limitancestorcount", strNv);
gArgs.ModifyRWConfigFile("limitancestorcount", strNv);
}
break;
case limitancestorsize:
if (changed()) {
long long nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.limits.ancestor_size_vbytes = nNv * 1'000;
gArgs.ForceSetArg("-limitancestorsize", strNv);
gArgs.ModifyRWConfigFile("limitancestorsize", strNv);
}
break;
case limitdescendantcount:
if (changed()) {
long long nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.limits.descendant_count = nNv;
gArgs.ForceSetArg("-limitdescendantcount", strNv);
gArgs.ModifyRWConfigFile("limitdescendantcount", strNv);
}
break;
case limitdescendantsize:
if (changed()) {
long long nNv = value.toLongLong();
std::string strNv = value.toString().toStdString();
node().mempool().m_opts.limits.descendant_size_vbytes = nNv * 1'000;
gArgs.ForceSetArg("-limitdescendantsize", strNv);
gArgs.ModifyRWConfigFile("limitdescendantsize", strNv);
}
break;
case rejectbarepubkey:
if (changed()) {
// The config and internal option is inverted
const bool nv = ! value.toBool();
node().mempool().m_opts.permit_bare_pubkey = nv;
node().updateRwSetting("permitbaremultisig", nv);
}
break;
case rejectbaremultisig:
if (changed()) {
// The config and internal option is inverted
const bool fNewValue = ! value.toBool();
node().mempool().m_opts.permit_bare_multisig = fNewValue;
gArgs.ModifyRWConfigFile("permitbaremultisig", strprintf("%d", fNewValue));
}
break;
case maxscriptsize:
if (changed()) {
const auto nv = value.toLongLong();
update(nv);
::g_script_size_policy_limit = nv;
}
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();
const bool fNewEn = (nNewSize > 0);
if (fNewEn) {
if (!node().mempool().m_opts.max_datacarrier_bytes.has_value()) {
gArgs.ModifyRWConfigFile("datacarrier", strprintf("%d", fNewEn));
}
gArgs.ModifyRWConfigFile("datacarriersize", value.toString().toStdString());
node().mempool().m_opts.max_datacarrier_bytes = nNewSize;
} else {
gArgs.ModifyRWConfigFile("datacarrier", "0");
node().mempool().m_opts.max_datacarrier_bytes = std::nullopt;
}
}
break;
case rejectnonstddatacarrier:
if (changed()) {
// This option is inverted
const bool new_value = ! value.toBool();
node().updateRwSetting("acceptnonstddatacarrier" + suffix, new_value);
node().mempool().m_opts.accept_non_std_datacarrier = new_value;
}
break;
case dustrelayfee:
if (changed()) {
CAmount nNv = value.toLongLong();
gArgs.ModifyRWConfigFile("dustrelayfee", FormatMoney(nNv));
CFeeRate feerate{nNv};
node().mempool().m_opts.dust_relay_feerate_floor = feerate;
if (node().mempool().m_opts.dust_relay_feerate < feerate || !node().mempool().m_opts.dust_relay_target) {
node().mempool().m_opts.dust_relay_feerate = feerate;
} else {
node().mempool().UpdateDynamicDustFeerate();
}
}
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:
if (changed()) {
std::string strNv = FormatMoney(value.toLongLong());
gArgs.ForceSetArg("-blockmintxfee", strNv);
gArgs.ModifyRWConfigFile("blockmintxfee", strNv);
}
break;
case blockmaxsize:
case blockprioritysize:
case blockmaxweight:
if (changed()) {
const int nNewValue_kB = value.toInt();
std::string strNv = strprintf("%d000", nNewValue_kB);
std::string strKey;
switch (option) {
case blockmaxsize:
strKey = "blockmaxsize";
break;
case blockprioritysize:
strKey = "blockprioritysize";
break;
case blockmaxweight:
strKey = "blockmaxweight";
break;
default: assert(0);
}
gArgs.ForceSetArg("-" + strKey, strNv);
gArgs.ModifyRWConfigFile(strKey, strNv);
}
break;
case blockreconstructionextratxn:
if (changed()) {
std::string strNv = value.toString().toStdString();
gArgs.ForceSetArg("-blockreconstructionextratxn", strNv);
gArgs.ModifyRWConfigFile("blockreconstructionextratxn", strNv);
}
break;
case corepolicy:
gArgs.ModifyRWConfigFile("corepolicy", value.toString().toStdString());
break;
default:
break;
}

View File

@ -64,6 +64,7 @@ public:
FontForMoney, // FontChoice
FontForQRCodes, // FontChoice
PeersTabAlternatingRowColors, // bool
walletrbf, // bool
CoinControlFeatures, // bool
SubFeeFromAmount, // bool
ThreadsScriptVerif, // int
@ -80,6 +81,37 @@ public:
maxuploadtarget,
peerbloomfilters, // bool
peerblockfilters, // bool
mempoolreplacement,
mempooltruc,
maxorphantx,
maxmempool,
incrementalrelayfee,
mempoolexpiry,
rejectunknownscripts, // bool
rejectparasites, // bool
rejecttokens, // bool
rejectspkreuse, // bool
minrelaytxfee,
bytespersigop,
bytespersigopstrict,
limitancestorcount,
limitancestorsize,
limitdescendantcount,
limitdescendantsize,
rejectbarepubkey, // bool
rejectbaremultisig, // bool
maxscriptsize,
datacarriercost, // double
datacarriersize,
rejectnonstddatacarrier, // bool
dustrelayfee,
dustdynamic, // QString
blockmintxfee,
blockmaxsize,
blockprioritysize,
blockmaxweight,
blockreconstructionextratxn,
corepolicy,
OptionIDRowCount,
};
@ -156,6 +188,7 @@ private:
/* rwconf settings that require a restart */
bool f_peerbloomfilters;
bool f_rejectspkreuse;
// Add option to list of GUI options overridden through command line/config file
void addOverriddenOption(const std::string &option);

View File

@ -30,6 +30,7 @@ void initialize_miner()
for (uint32_t i = 0; i < uint32_t{100}; ++i) {
g_available_coins.emplace_back(Txid::FromUint256(uint256::ZERO), i);
}
g_setup->m_node.args->ForceSetArg("-blockprioritysize", "0");
}
// Test that the MiniMiner can run with various outpoints and feerates.

View File

@ -607,6 +607,8 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
gArgs.ForceSetArg("-blockprioritysize", "0");
// Note that by default, these tests run with size accounting enabled.
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::shared_ptr<CBlockTemplate> pblocktemplate;

View File

@ -22,6 +22,7 @@
static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
{
const kernel::MemPoolOptions opts{
.permit_bare_pubkey = true,
.permit_bare_multisig = permit_bare_multisig,
};
return IsStandardTx(tx, opts, reason);
@ -30,6 +31,7 @@ static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std:
static bool IsStandardTx(const CTransaction& tx, std::string& reason)
{
kernel::MemPoolOptions opts{
.permit_bare_pubkey = true,
.permit_bare_multisig = true,
};
if (!IsStandardTx(tx, opts, reason)) return false;

View File

@ -796,6 +796,8 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
CKey key = GenerateRandomKey();
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
g_mempool_opts.permit_bare_pubkey = true;
constexpr auto CheckIsStandard = [](const auto& t) {
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction{t}, g_mempool_opts, reason));
@ -873,12 +875,15 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
g_mempool_opts.reject_tokens = false;
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
while (t.vout[0].scriptPubKey.size() < MAX_OP_RETURN_RELAY) {
t.vout[0].scriptPubKey << OP_0;
}
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
CheckIsStandard(t);
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
t.vout[0].scriptPubKey << OP_0;
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
CheckIsNotStandard(t, "scriptpubkey");

View File

@ -54,7 +54,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
spends[i].vin[0].prevout.n = 0;
spends[i].vout.resize(1);
spends[i].vout[0].nValue = 11*CENT;
spends[i].vout[0].scriptPubKey = scriptPubKey;
spends[i].vout[0].scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
// Sign:
std::vector<unsigned char> vchSig;

View File

@ -23,6 +23,7 @@ CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node)
// Default to always checking mempool regardless of
// chainparams.DefaultConsistencyChecks for tests
.check_ratio = 1,
.truc_policy = TRUCPolicy::Enforce,
.signals = node.validation_signals.get(),
};
const auto result{ApplyArgsManOptions(*node.args, ::Params(), mempool_opts)};

View File

@ -864,7 +864,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForDestination(PKHash(key.GetPubKey())));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
@ -906,7 +906,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForDestination(PKHash(key.GetPubKey())));
BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
m_node.validation_signals->SyncWithValidationInterfaceQueue();
});

View File

@ -131,6 +131,7 @@ class TestNode():
if self.version is None:
self.args += [
"-corepolicy",
"-softwareexpiry=0",
"-walletimplicitsegwit",
]