From bdcd251208bccff2f539b7fbff3a9b7af9aa4fb4 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 11 Jan 2024 18:36:10 -0600 Subject: [PATCH] Proposal to update the mempool 1559 parameters for v22 (#7285) We have observed the mempool 1559 parameters spiking way higher than they need to, due to a series of issue. - Block's have too low of a gas capacity - Different nodes have very divergent mempools, and don't have Ethereum's incentives for maximizing tx fee - The recheck factor forces the base fee to get at minimum 3x higher than the spam rate. - Wallets use a large multiplier (3x) due to the above issues - Leading to users paying 9x over the spam that needs to get evicted at peaks. The need for the recheck factor was to offer a grace period for txs that become candidates for eviction due to momentary surges, or differences between mempool sizes between validator operators. Its because we are in a FIFO mempool, with no way right now to "preserve" user txs at too low of a fee, that may be valid later. Theres no system for keeping them in the network, and no system for flagging the issue to users. An alternative is to keep a higher recheck factor for just RPC nodes, but not validators, if we see user txs not making it in. (That way they are still "remembered" and kept in the network. Though they may never get re-gossipped) With all nodes ideally getting aligned on one fee market / mempool version in the next release, we can revisit this recheck factor. It was already lowered from its previous `4` to `3` in a prior release that got to a subset of nodes. This PR suggests a further 25% reduction to `2.25`. Maybe it should be a bit higher to remain more conservative though? The issue now is really moreso about supporting user txs from occasional fee spikes, instead of handling inconsitencies between 1559 nodes and non-1559 nodes. This further lowers the reset interval as this has gotten to more nodes. --- CHANGELOG.md | 11 ++++- x/txfees/keeper/mempool-1559/code.go | 65 ++++++++++++++++++++++------ x/txfees/module.go | 1 + 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ff3cb91a1..8576a014553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,8 +40,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - ## v22.0.0 + +### Fee Market Parameter Updates +* [#7285](https://github.com/osmosis-labs/osmosis/pull/7285) The following updates are applied: + * Dynamic recheck factor based on current base fee value. Under 0.01, the recheck factor is 3. + In face of continuous spam, will take ~19 blocks from base fee > spam cost, to mempool eviction. + Above 0.01, the recheck factor is 2.3. In face of continuous spam, will take ~15 blocks from base fee > spam cost, to mempool eviction. + * Reset interval set to 6000 which is approximately 8.5 hours. + * Default base fee is reduced by 2 to 0.005. + * Set target gas to .625 * block_gas_limt = 187.5 million + ### State Breaking ### API diff --git a/x/txfees/keeper/mempool-1559/code.go b/x/txfees/keeper/mempool-1559/code.go index 0d1d8ba4043..d41291ca1a3 100644 --- a/x/txfees/keeper/mempool-1559/code.go +++ b/x/txfees/keeper/mempool-1559/code.go @@ -17,47 +17,72 @@ import ( This logic does two things: - Maintaining data parsed from chain transaction execution and updating eipState accordingly. - - Resetting eipState to default every ResetInterval (3000) block height intervals to maintain consistency. + - Resetting eipState to default every ResetInterval (6000) block height intervals to maintain consistency. Additionally: - Periodically evaluating CheckTx and RecheckTx for compliance with these parameters. - Note: The reset interval is set to 3000 blocks, which is approximately 6 hours. Consider adjusting for a smaller time interval (e.g., 500 blocks = 1 hour) if necessary. + Note: The reset interval is set to 6000 blocks, which is approximately 8.5 hours. Challenges: - Transactions falling under their gas bounds are currently discarded by nodes. This behavior can be modified for CheckTx, rather than RecheckTx. Global variables stored in memory: - - DefaultBaseFee: Default base fee, initialized to 0.01. + - DefaultBaseFee: Default base fee, initialized to 0.005. - MinBaseFee: Minimum base fee, initialized to 0.0025. - MaxBaseFee: Maximum base fee, initialized to 5. - MaxBlockChangeRate: The maximum block change rate, initialized to 1/10. Global constants: - - TargetGas: Gas wanted per block, initialized to 75,000,000. - - ResetInterval: The interval at which eipState is reset, initialized to 3000 blocks. + - TargetGas: Gas wanted per block, initialized to .625 * block_gas_limt = 187.5 million. + - ResetInterval: The interval at which eipState is reset, initialized to 6000 blocks. - BackupFile: File for backup, set to "eip1559state.json". - - RecheckFeeConstant: A constant value for rechecking fees, initialized to 3.3. + - RecheckFeeConstant: A constant value for rechecking fees, initialized to 2.25. */ var ( - DefaultBaseFee = sdk.MustNewDecFromStr("0.01") + // We expect wallet multiplier * DefaultBaseFee < MinBaseFee * RecheckFeeConstant + // conservatively assume a wallet multiplier of at least 7%. + DefaultBaseFee = sdk.MustNewDecFromStr("0.0060") MinBaseFee = sdk.MustNewDecFromStr("0.0025") MaxBaseFee = sdk.MustNewDecFromStr("5") + ResetInterval = int64(6000) // Max increase per block is a factor of 1.06, max decrease is 9/10 - // If recovering at ~30M gas per block, decrease is .94 + // If recovering at ~30M gas per block, decrease is .916 MaxBlockChangeRate = sdk.NewDec(1).Quo(sdk.NewDec(10)) TargetGas = int64(187_500_000) TargetBlockSpacePercent = sdk.MustNewDecFromStr("0.625") + + // N.B. on the reason for having two base fee constants for high and low fees: + // + // At higher base fees, we apply a smaller re-check factor. + // The reason for this is that the recheck factor forces the base fee to get at minimum + // "recheck factor" times higher than the spam rate. This leads to slow recovery + // and a bad UX for user transactions. We aim for spam to start getting evicted from the mempool + // sooner as to avoid more severe UX degradation for user transactions. Therefore, + // we apply a smaller recheck factor at higher base fees. + // + // For low base fees: // In face of continuous spam, will take ~19 blocks from base fee > spam cost, to mempool eviction - // ceil(log_{1.06}(RecheckFeeConstant)) - // So potentially 1.8 minutes of impaired UX from 1559 nodes on top of time to get to base fee > spam. - RecheckFeeConstant = "3.0" - ResetInterval = int64(3000) + // ceil(log_{1.06}(RecheckFeeConstantLowBaseFee)) (assuming base fee not going over threshold) + // So potentially 1.2 minutes of impaired UX from 1559 nodes on top of time to get to base fee > spam. + RecheckFeeConstantLowBaseFee = "3" + // + // For high base fees: + // In face of continuous spam, will take ~15 blocks from base fee > spam cost, to mempool eviction + // ceil(log_{1.06}(RecheckFeeConstantHighBaseFee)) (assuming base fee surpasses threshold) + RecheckFeeConstantHighBaseFee = "2.3" + // Note, the choice of 0.01 was made by observing base fee metrics on mainnet and selecting + // this value from Grafana dashboards. The observation is that below this threshold, we do not + // observe user UX degradation. Therefore, we keep the original recheck factor. + RecheckFeeBaseFeeThreshold = sdk.MustNewDecFromStr("0.01") ) -var RecheckFeeDec = sdk.MustNewDecFromStr(RecheckFeeConstant) +var ( + RecheckFeeLowBaseFeeDec = sdk.MustNewDecFromStr(RecheckFeeConstantLowBaseFee) + RecheckFeeHighBaseFeeDec = sdk.MustNewDecFromStr(RecheckFeeConstantHighBaseFee) +) const ( BackupFilename = "eip1559state.json" @@ -155,7 +180,19 @@ func (e *EipState) GetCurBaseFee() osmomath.Dec { // GetCurRecheckBaseFee returns a clone of the CurBaseFee / RecheckFeeConstant to account for // rechecked transactions in the feedecorator ante handler func (e *EipState) GetCurRecheckBaseFee() osmomath.Dec { - return e.CurBaseFee.Clone().Quo(RecheckFeeDec) + baseFee := e.CurBaseFee.Clone() + + // At higher base fees, we apply a smaller re-check factor. + // The reason for this is that the recheck factor forces the base fee to get at minimum + // "recheck factor" times higher than the spam rate. This leads to slow recovery + // and a bad UX for user transactions. We aim for spam to start getting evicted from the mempool + // sooner as to avoid more severe UX degradation for user transactions. Therefore, + // we apply a smaller recheck factor at higher base fees. + if baseFee.GT(RecheckFeeBaseFeeThreshold) { + return baseFee.QuoMut(RecheckFeeHighBaseFeeDec) + } + + return baseFee.QuoMut(RecheckFeeLowBaseFeeDec) } var rwMtx = sync.Mutex{} diff --git a/x/txfees/module.go b/x/txfees/module.go index bbc4914de7a..14b0e9d7fbe 100644 --- a/x/txfees/module.go +++ b/x/txfees/module.go @@ -173,6 +173,7 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } // Then, on every block, we check if the current consensus param bytes have changed in comparison to the cached value. // If they have, we unmarshal the current consensus params, update the target gas, and cache the value. // This is done to improve performance by not having to fetch and unmarshal the consensus params on every block. +// TODO: Move this to EIP-1559 code func (am AppModule) CheckAndSetTargetGas(ctx sdk.Context) { // Check if the block gas limit has changed. // If it has, update the target gas for eip1559.