mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-28 21:12:29 +02:00
txmempool: Add dustdynamic option supporting fee estimator or kvB into mempool
Currently targets 80% success threshold and updates every 15 minutes
This commit is contained in:
parent
7d6aaa128e
commit
690811b90f
@ -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=<amt>", 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=<amt>", 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:<blocks>|mempool:<kvB>",
|
||||
strprintf("Automatically raise dustrelayfee based on either the expected fee to be mined within <blocks> blocks, or to be within the best <kvB> 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<std::chrono::hours>(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);
|
||||
|
@ -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.
|
||||
|
@ -14,9 +14,11 @@
|
||||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
@ -40,6 +42,39 @@ void ApplyArgsManOptions(const ArgsManager& argsman, MemPoolLimits& mempool_limi
|
||||
}
|
||||
}
|
||||
|
||||
util::Result<int32_t> 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<uint16_t>(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<int32_t>(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<void> 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<void> 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);
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include <util/result.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
class ArgsManager;
|
||||
class CChainParams;
|
||||
struct bilingual_str;
|
||||
@ -14,6 +16,8 @@ namespace kernel {
|
||||
struct MemPoolOptions;
|
||||
};
|
||||
|
||||
[[nodiscard]] util::Result<int32_t> 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.
|
||||
|
@ -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};
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include <consensus/validation.h>
|
||||
#include <crypto/ripemd160.h>
|
||||
#include <logging.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <random.h>
|
||||
#include <scheduler.h>
|
||||
#include <tinyformat.h>
|
||||
#include <script/script.h>
|
||||
#include <util/check.h>
|
||||
@ -420,6 +422,13 @@ static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str&
|
||||
CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
|
||||
: m_opts{Flatten(std::move(opts), error)}
|
||||
{
|
||||
Assert(m_opts.scheduler || !m_opts.dust_relay_target);
|
||||
m_opts.dust_relay_feerate_floor = m_opts.dust_relay_feerate;
|
||||
if (m_opts.scheduler) {
|
||||
m_opts.scheduler->scheduleEvery([this]{
|
||||
UpdateDynamicDustFeerate();
|
||||
}, DYNAMIC_DUST_FEERATE_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
bool CTxMemPool::isSpent(const COutPoint& outpoint) const
|
||||
@ -687,6 +696,34 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
||||
blockSinceLastRollingFeeBump = true;
|
||||
}
|
||||
|
||||
void CTxMemPool::UpdateDynamicDustFeerate()
|
||||
{
|
||||
CFeeRate est_feerate{0};
|
||||
if (m_opts.dust_relay_target < 0 && m_opts.estimator) {
|
||||
static constexpr double target_success_threshold{0.8};
|
||||
est_feerate = m_opts.estimator->estimateRawFee(-m_opts.dust_relay_target, target_success_threshold, FeeEstimateHorizon::LONG_HALFLIFE, nullptr);
|
||||
} else if (m_opts.dust_relay_target > 0) {
|
||||
auto bytes_remaining = int64_t{m_opts.dust_relay_target} * 1'000;
|
||||
LOCK(cs);
|
||||
for (auto mi = mapTx.get<ancestor_score>().begin(); mi != mapTx.get<ancestor_score>().end(); ++mi) {
|
||||
bytes_remaining -= mi->GetTxSize();
|
||||
if (bytes_remaining <= 0) {
|
||||
est_feerate = CFeeRate(mi->GetFee(), mi->GetTxSize());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (est_feerate < m_opts.dust_relay_feerate_floor) {
|
||||
est_feerate = m_opts.dust_relay_feerate_floor;
|
||||
}
|
||||
|
||||
if (m_opts.dust_relay_feerate != est_feerate) {
|
||||
LogPrint(BCLog::MEMPOOL, "Updating dust feerate to %s\n", est_feerate.ToString(FeeEstimateMode::SAT_VB));
|
||||
m_opts.dust_relay_feerate = est_feerate;
|
||||
}
|
||||
}
|
||||
|
||||
void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
|
||||
{
|
||||
if (m_opts.check_ratio == 0) return;
|
||||
|
@ -46,6 +46,8 @@ class ValidationSignals;
|
||||
|
||||
struct bilingual_str;
|
||||
|
||||
static constexpr std::chrono::minutes DYNAMIC_DUST_FEERATE_UPDATE_INTERVAL{15};
|
||||
|
||||
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
|
||||
static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF;
|
||||
|
||||
@ -493,6 +495,8 @@ public:
|
||||
*/
|
||||
bool HasNoInputsOf(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
void UpdateDynamicDustFeerate();
|
||||
|
||||
/** Affect CreateNewBlock prioritisation of transactions */
|
||||
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
|
||||
void ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
Loading…
Reference in New Issue
Block a user