diff --git a/src/init.cpp b/src/init.cpp index 504f2ea410..72fe36fb04 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -640,6 +640,9 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (test networks only; default: %u)", DEFAULT_ACCEPT_NON_STD_TXN), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-incrementalrelayfee=", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and replacement policy. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-dustrelayfee=", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-dustdynamic=off|target:|mempool:", + strprintf("Automatically raise dustrelayfee based on either the expected fee to be mined within blocks, or to be within the best kvB of this node's mempool. (default: %s)", + DEFAULT_DUST_DYNAMIC), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index 1c87fa866c..62e31b1df4 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -55,6 +55,9 @@ struct MemPoolOptions { /** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ CFeeRate min_relay_feerate{DEFAULT_MIN_RELAY_TX_FEE}; CFeeRate dust_relay_feerate{DUST_RELAY_TX_FEE}; + CFeeRate dust_relay_feerate_floor{DUST_RELAY_TX_FEE}; + /** Negative for a target number of blocks, positive for target kB into current mempool. */ + int32_t dust_relay_target{0}; /** * A data carrying output is an unspendable output containing data. The script * type is designated as TxoutType::NULL_DATA. diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index ff0b1a794f..e2c883d53e 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -14,9 +14,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -40,6 +42,39 @@ void ApplyArgsManOptions(const ArgsManager& argsman, MemPoolLimits& mempool_limi } } +util::Result ParseDustDynamicOpt(const std::string& optstr, const unsigned int max_fee_estimate_blocks) +{ + if (optstr == "0" || optstr == "off") { + return 0; + } else if (optstr.rfind("target:", 0) == 0) { + if (!max_fee_estimate_blocks) { + return util::Error{_("\"target\" mode requires fee estimator (disabled)")}; + } + const auto val = ToIntegral(optstr.substr(7)); + if (!val) { + return util::Error{_("failed to parse target block count")}; + } + if (*val < 2) { + return util::Error{_("target must be at least 2 blocks")}; + } + if (*val > max_fee_estimate_blocks) { + return util::Error{strprintf(_("target can only be at most %s blocks"), max_fee_estimate_blocks)}; + } + return -*val; + } else if (optstr.rfind("mempool:", 0) == 0) { + const auto val = ToIntegral(optstr.substr(8)); + if (!val) { + return util::Error{_("failed to parse mempool position")}; + } + if (*val < 1) { + return util::Error{_("mempool position must be at least 1 kB")}; + } + return *val; + } else { + return util::Error{strprintf(_("\"%s\""), optstr)}; + } +} + util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainParams& chainparams, MemPoolOptions& mempool_opts) { mempool_opts.check_ratio = argsman.GetIntArg("-checkmempool", mempool_opts.check_ratio); @@ -80,6 +115,15 @@ util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainP return util::Error{AmountErrMsg("dustrelayfee", argsman.GetArg("-dustrelayfee", ""))}; } } + if (argsman.IsArgSet("-dustdynamic")) { + const auto optstr = argsman.GetArg("-dustdynamic", DEFAULT_DUST_DYNAMIC); + const auto max_fee_estimate_blocks = mempool_opts.estimator ? mempool_opts.estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE) : (unsigned int)0; + const auto parsed = ParseDustDynamicOpt(optstr, max_fee_estimate_blocks); + if (!parsed) { + return util::Error{strprintf(_("Invalid mode for dustdynamic: %s"), util::ErrorString(parsed))}; + } + mempool_opts.dust_relay_target = *parsed; + } mempool_opts.permit_bare_pubkey = argsman.GetBoolArg("-permitbarepubkey", DEFAULT_PERMIT_BAREPUBKEY); diff --git a/src/node/mempool_args.h b/src/node/mempool_args.h index 630fee6421..e6a013a6c7 100644 --- a/src/node/mempool_args.h +++ b/src/node/mempool_args.h @@ -7,6 +7,8 @@ #include +#include + class ArgsManager; class CChainParams; struct bilingual_str; @@ -14,6 +16,8 @@ namespace kernel { struct MemPoolOptions; }; +[[nodiscard]] util::Result ParseDustDynamicOpt(const std::string& optstr, unsigned int max_fee_estimate_blocks); + /** * Overlay the options set in \p argsman on top of corresponding members in \p mempool_opts. * Returns an error if one was encountered. diff --git a/src/policy/policy.h b/src/policy/policy.h index 13dad1208b..7db33a8c3c 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -59,6 +59,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650}; * only increase the dust limit after prior releases were already not creating * outputs below the new threshold */ static constexpr unsigned int DUST_RELAY_TX_FEE{3000}; +static const std::string DEFAULT_DUST_DYNAMIC{"off"}; static const std::string DEFAULT_SPKREUSE{"allow"}; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000}; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 80b2d56615..536f08825b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -13,9 +13,11 @@ #include #include #include +#include #include #include #include +#include #include #include