From 75322b1ccbcb403d690332af96bd37a3163758c8 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 15 Mar 2024 02:30:37 +0000 Subject: [PATCH 1/8] doc: Un-hide dustrelayfee option --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 2c29ed49a7..c95430b4d9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -628,7 +628,7 @@ 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 | 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("-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); From 0518887aa480453eb116abb4f2c236c068c94413 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 5 Jun 2024 16:34:41 +0000 Subject: [PATCH 2/8] Pass fee estimator into CTxMempool This partially reverts commit 714523918ba2b853fc69bee6b04a33ba0c828bf5. --- src/init.cpp | 1 + src/kernel/mempool_options.h | 4 ++++ src/txmempool.cpp | 1 + src/txmempool.h | 3 +++ 4 files changed, 9 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index c95430b4d9..2a598d7a10 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1499,6 +1499,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) assert(!node.chainman); CTxMemPool::Options mempool_opts{ + .estimator = node.fee_estimator.get(), .check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0, }; auto result{ApplyArgsManOptions(args, chainparams, mempool_opts)}; diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index e4ca6a65c8..777940c9e5 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -13,6 +13,8 @@ #include #include +class CBlockPolicyEstimator; + enum class RBFPolicy { Never, OptIn, Always }; enum class TRUCPolicy { Reject, Accept, Enforce }; @@ -40,6 +42,8 @@ namespace kernel { * Most of the time, this struct should be referenced as CTxMemPool::Options. */ struct MemPoolOptions { + /* Used to estimate appropriate transaction fees. */ + CBlockPolicyEstimator* estimator{nullptr}; /* The ratio used to determine how often sanity checks will run. */ int check_ratio{0}; int64_t max_size_bytes{DEFAULT_MAX_MEMPOOL_SIZE_MB * 1'000'000}; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index aa2583b2c1..5fc0509fd7 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -405,6 +405,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee, CTxMemPool::CTxMemPool(const Options& opts) : m_check_ratio{opts.check_ratio}, + minerPolicyEstimator{opts.estimator}, m_max_size_bytes{opts.max_size_bytes}, m_expiry{opts.expiry}, m_incremental_relay_feerate{opts.incremental_relay_feerate}, diff --git a/src/txmempool.h b/src/txmempool.h index 0f06758a42..4992a05465 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -205,6 +205,8 @@ struct entry_time {}; struct ancestor_score {}; struct index_by_wtxid {}; +class CBlockPolicyEstimator; + /** * Information about a mempool transaction. */ @@ -304,6 +306,7 @@ class CTxMemPool protected: const int m_check_ratio; //!< Value n means that 1 times in n we check. std::atomic nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation + CBlockPolicyEstimator* const minerPolicyEstimator; uint64_t totalTxSize GUARDED_BY(cs){0}; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. CAmount m_total_fee GUARDED_BY(cs){0}; //!< sum of all mempool tx's fees (NOT modified fee) From f8f255c8076fc042bc2c45f29c04bac91a7c7213 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 15 Mar 2024 18:56:15 +0000 Subject: [PATCH 3/8] Pass scheduler onto CTxMempool --- src/init.cpp | 1 + src/kernel/mempool_options.h | 2 ++ src/txmempool.cpp | 1 + src/txmempool.h | 1 + 4 files changed, 5 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 2a598d7a10..4ef57ac63e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1500,6 +1500,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) CTxMemPool::Options mempool_opts{ .estimator = node.fee_estimator.get(), + .scheduler = &*node.scheduler, .check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0, }; auto result{ApplyArgsManOptions(args, chainparams, mempool_opts)}; diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index 777940c9e5..5dd1b06af1 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -14,6 +14,7 @@ #include class CBlockPolicyEstimator; +class CScheduler; enum class RBFPolicy { Never, OptIn, Always }; enum class TRUCPolicy { Reject, Accept, Enforce }; @@ -44,6 +45,7 @@ namespace kernel { struct MemPoolOptions { /* Used to estimate appropriate transaction fees. */ CBlockPolicyEstimator* estimator{nullptr}; + CScheduler* scheduler; /* The ratio used to determine how often sanity checks will run. */ int check_ratio{0}; int64_t max_size_bytes{DEFAULT_MAX_MEMPOOL_SIZE_MB * 1'000'000}; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 5fc0509fd7..f2a72bee71 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -406,6 +406,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee, CTxMemPool::CTxMemPool(const Options& opts) : m_check_ratio{opts.check_ratio}, minerPolicyEstimator{opts.estimator}, + scheduler{opts.scheduler}, m_max_size_bytes{opts.max_size_bytes}, m_expiry{opts.expiry}, m_incremental_relay_feerate{opts.incremental_relay_feerate}, diff --git a/src/txmempool.h b/src/txmempool.h index 4992a05465..ac82a842a5 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -307,6 +307,7 @@ protected: const int m_check_ratio; //!< Value n means that 1 times in n we check. std::atomic nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* const minerPolicyEstimator; + CScheduler* scheduler; uint64_t totalTxSize GUARDED_BY(cs){0}; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. CAmount m_total_fee GUARDED_BY(cs){0}; //!< sum of all mempool tx's fees (NOT modified fee) From b18da0796f4b4e4169345850366d4ad2e2e1563c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 15 Mar 2024 21:21:44 +0000 Subject: [PATCH 4/8] txmempool: Add dustdynamic option supporting fee estimator or kvB into mempool Currently targets 80% success threshold and updates every 15 minutes --- src/init.cpp | 3 +++ src/kernel/mempool_options.h | 2 ++ src/node/mempool_args.cpp | 44 ++++++++++++++++++++++++++++++++++++ src/node/mempool_args.h | 4 ++++ src/policy/policy.h | 1 + src/txmempool.cpp | 38 +++++++++++++++++++++++++++++++ src/txmempool.h | 8 ++++++- 7 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 4ef57ac63e..878155502a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -629,6 +629,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 5dd1b06af1..5f4597723e 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -54,6 +54,8 @@ 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}; + /** Negative for a target number of blocks, positive for target kB into current mempool. */ + int32_t dust_relay_target; /** * 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 ca0213b85c..d88f8258ce 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -13,10 +13,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -38,6 +40,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); @@ -78,6 +113,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 9d64587085..b8d6a08d7c 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -56,6 +56,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 f2a72bee71..6a6abe24db 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -13,10 +13,12 @@ #include #include #include +#include #include #include #include #include +#include #include