From 0b6f8f188949c3e40a9941871fda9adb993ebc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 14 May 2025 12:41:19 +0200 Subject: [PATCH] feat: update base fee via cli (#1183) * feat: update base fee via cli * fix * improve comments --- cmd/geth/main.go | 2 ++ cmd/geth/usage.go | 25 +++++++++++++++++++++ cmd/utils/flags.go | 37 ++++++++++++++++++++++++++++++ consensus/misc/eip1559.go | 41 +++++++++++++++++++++++++--------- consensus/misc/eip1559_test.go | 34 +++++++++++++++++++++++++++- eth/backend.go | 7 ++++++ eth/ethconfig/config.go | 4 ++++ params/config.go | 19 ++++++++++++++-- params/version.go | 2 +- 9 files changed, 157 insertions(+), 14 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index dcb7d12fc1..0377609ceb 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -185,6 +185,8 @@ var ( utils.DARecoverySignBlocksFlag, utils.DARecoveryL2EndBlockFlag, utils.DARecoveryProduceBlocksFlag, + utils.L2BaseFeeScalarFlag, + utils.L2BaseFeeOverheadFlag, } rpcFlags = []cli.Flag{ diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 8686dbc3df..df99d6eed0 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.RPCGlobalEVMTimeoutFlag, utils.RPCGlobalTxFeeCapFlag, utils.AllowUnprotectedTxs, + utils.MaxBlockRangeFlag, utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag, @@ -227,6 +228,30 @@ var AppHelpFlagGroups = []flags.FlagGroup{ Name: "METRICS AND STATS", Flags: metricsFlags, }, + { + Name: "ROLLUP", + Flags: []cli.Flag{ + utils.L1EndpointFlag, + utils.L1ConfirmationsFlag, + utils.L1DeploymentBlockFlag, + utils.L1DisableMessageQueueV2Flag, + utils.RollupVerifyEnabledFlag, + utils.L2BaseFeeScalarFlag, + utils.L2BaseFeeOverheadFlag, + utils.DASyncEnabledFlag, + utils.DABlobScanAPIEndpointFlag, + utils.DABlockNativeAPIEndpointFlag, + utils.DABeaconNodeAPIEndpointFlag, + utils.DARecoveryModeFlag, + utils.DARecoveryInitialL1BlockFlag, + utils.DARecoveryInitialBatchFlag, + utils.DARecoverySignBlocksFlag, + utils.DARecoveryL2EndBlockFlag, + utils.DARecoveryProduceBlocksFlag, + utils.CircuitCapacityCheckEnabledFlag, + utils.CircuitCapacityCheckWorkersFlag, + }, + }, { Name: "ALIASED (deprecated)", Flags: []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 7371a37700..10c8a60e53 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -47,6 +47,7 @@ import ( "github.com/scroll-tech/go-ethereum/consensus" "github.com/scroll-tech/go-ethereum/consensus/clique" "github.com/scroll-tech/go-ethereum/consensus/ethash" + "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/vm" @@ -934,6 +935,18 @@ var ( Name: "da.recovery.produceblocks", Usage: "Produce unsigned blocks after L1 recovery for permissionless batch submission", } + + // L2 base fee settings + L2BaseFeeScalarFlag = BigFlag{ + Name: "basefee.scalar", + Usage: "Scalar used in the l2 base fee formula. Signer nodes will use this for computing the next block's base fee. Follower nodes will use this in RPC.", + Value: misc.DefaultBaseFeeScalar, + } + L2BaseFeeOverheadFlag = BigFlag{ + Name: "basefee.overhead", + Usage: "Overhead used in the l2 base fee formula. Signer nodes will use this for computing the next block's base fee. Follower nodes will use this in RPC.", + Value: misc.DefaultBaseFeeOverhead, + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1709,6 +1722,29 @@ func setDA(ctx *cli.Context, cfg *ethconfig.Config) { } } +func setBaseFee(ctx *cli.Context, cfg *ethconfig.Config) { + cfg.BaseFeeScalar = misc.DefaultBaseFeeScalar + if ctx.GlobalIsSet(L2BaseFeeScalarFlag.Name) { + cfg.BaseFeeScalar = GlobalBig(ctx, L2BaseFeeScalarFlag.Name) + } + cfg.BaseFeeOverhead = misc.DefaultBaseFeeOverhead + if ctx.GlobalIsSet(L2BaseFeeOverheadFlag.Name) { + cfg.BaseFeeOverhead = GlobalBig(ctx, L2BaseFeeOverheadFlag.Name) + } + + log.Info("L2 base fee coefficients", "scalar", cfg.BaseFeeScalar, "overhead", cfg.BaseFeeOverhead) + + var minBaseFee uint64 + if fee := misc.MinBaseFee(cfg.BaseFeeScalar, cfg.BaseFeeOverhead); fee.IsUint64() { + minBaseFee = fee.Uint64() + } + + if cfg.TxPool.PriceLimit < minBaseFee { + log.Warn("Updating txpool price limit to min L2 base fee", "provided", cfg.TxPool.PriceLimit, "updated", minBaseFee) + cfg.TxPool.PriceLimit = minBaseFee + } +} + func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.GlobalIsSet(MaxBlockRangeFlag.Name) { cfg.MaxBlockRange = ctx.GlobalInt64(MaxBlockRangeFlag.Name) @@ -1785,6 +1821,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setCircuitCapacityCheck(ctx, cfg) setEnableRollupVerify(ctx, cfg) setDA(ctx, cfg) + setBaseFee(ctx, cfg) setMaxBlockRange(ctx, cfg) if ctx.GlobalIsSet(ShadowforkPeersFlag.Name) { cfg.ShadowForkPeerIDs = ctx.GlobalStringSlice(ShadowforkPeersFlag.Name) diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559.go index 382ad2ada5..b6443e76b0 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559.go @@ -28,6 +28,16 @@ import ( // We would only go above this if L1 base fee hits 2931 Gwei. const MaximumL2BaseFee = 10000000000 +// L2 base fee formula constants and defaults. +// l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead. +// `scalar` accounts for finalization costs. `overhead` accounts for sequencing and proving costs. +// we use 1e18 for precision to match the contract implementation. +var ( + BaseFeePrecision = new(big.Int).SetUint64(1e18) + DefaultBaseFeeScalar = new(big.Int).SetUint64(34000000000000) + DefaultBaseFeeOverhead = new(big.Int).SetUint64(15680000) +) + // VerifyEip1559Header verifies some header attributes which were changed in EIP-1559, // - gas limit check // - basefee check @@ -54,18 +64,29 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseF if config.Clique != nil && config.Clique.ShadowForkHeight != 0 && parent.Number.Uint64() >= config.Clique.ShadowForkHeight { return big.NewInt(10000000) // 0.01 Gwei } - l2SequencerFee := big.NewInt(1000000) // 0.001 Gwei - provingFee := big.NewInt(14680000) // 0.01468 Gwei - // L1_base_fee * 0.000034 - verificationFee := parentL1BaseFee - verificationFee = new(big.Int).Mul(verificationFee, big.NewInt(34)) - verificationFee = new(big.Int).Div(verificationFee, big.NewInt(1000000)) + scalar := config.Scroll.BaseFeeScalar + if scalar == nil { + scalar = DefaultBaseFeeScalar + } + overhead := config.Scroll.BaseFeeOverhead + if overhead == nil { + overhead = DefaultBaseFeeOverhead + } - baseFee := big.NewInt(0) - baseFee.Add(baseFee, l2SequencerFee) - baseFee.Add(baseFee, provingFee) - baseFee.Add(baseFee, verificationFee) + return calcBaseFee(scalar, overhead, parentL1BaseFee) +} + +// MinBaseFee calculates the minimum L2 base fee based on the configured coefficients. +func MinBaseFee(scalar, overhead *big.Int) *big.Int { + return calcBaseFee(scalar, overhead, big.NewInt(0)) +} + +func calcBaseFee(scalar, overhead, parentL1BaseFee *big.Int) *big.Int { + baseFee := new(big.Int).Set(parentL1BaseFee) + baseFee.Mul(baseFee, scalar) + baseFee.Div(baseFee, BaseFeePrecision) + baseFee.Add(baseFee, overhead) if baseFee.Cmp(big.NewInt(MaximumL2BaseFee)) > 0 { baseFee = big.NewInt(MaximumL2BaseFee) diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index 12aa2b17c2..bab8c4e496 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -111,6 +111,27 @@ func TestCalcBaseFee(t *testing.T) { tests := []struct { parentL1BaseFee int64 expectedL2BaseFee int64 + }{ + {0, 1}, + {1000000000, 1}, + {2000000000, 1}, + {100000000000, 2}, + {111111111111, 2}, + {2164000000000, 22}, + {644149677419355, 6442}, + } + for i, test := range tests { + config := config() + config.Scroll.BaseFeeScalar = big.NewInt(10000000) + config.Scroll.BaseFeeOverhead = big.NewInt(1) + if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { + t.Errorf("test %d: have %d want %d, ", i, have, want) + } + } + + testsWithDefaults := []struct { + parentL1BaseFee int64 + expectedL2BaseFee int64 }{ {0, 15680000}, {1000000000, 15714000}, @@ -120,9 +141,20 @@ func TestCalcBaseFee(t *testing.T) { {2164000000000, 89256000}, {644149677419355, 10000000000}, // cap at max L2 base fee } - for i, test := range tests { + for i, test := range testsWithDefaults { if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } } } + +// TestMinBaseFee assumes all blocks are 1559-blocks +func TestMinBaseFee(t *testing.T) { + if have, want := MinBaseFee(DefaultBaseFeeScalar, DefaultBaseFeeOverhead), big.NewInt(15680000); have.Cmp(want) != 0 { + t.Errorf("have %d want %d, ", have, want) + } + + if have, want := MinBaseFee(big.NewInt(10000000), big.NewInt(1)), big.NewInt(1); have.Cmp(want) != 0 { + t.Errorf("have %d want %d, ", have, want) + } +} diff --git a/eth/backend.go b/eth/backend.go index d6902f089d..99d35828d2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -148,6 +148,13 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } + + // Hacky workaround: + // It's hard to pass these fields to `CalcBaseFee`, etc. + // So pass them as part of the genesis config instead. + chainConfig.Scroll.BaseFeeScalar = config.BaseFeeScalar + chainConfig.Scroll.BaseFeeOverhead = config.BaseFeeOverhead + log.Info("Initialised chain configuration", "config", chainConfig) if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 94fa46c34c..f50109cb3f 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -230,6 +230,10 @@ type Config struct { // DA syncer options DA da_syncer.Config + + // L2 base fee coefficients for the formula: `l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead`. + BaseFeeScalar *big.Int + BaseFeeOverhead *big.Int } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/params/config.go b/params/config.go index d5f1a2166c..28eb863ba6 100644 --- a/params/config.go +++ b/params/config.go @@ -704,6 +704,11 @@ type ScrollConfig struct { // Genesis State Root for MPT clients GenesisStateRoot *common.Hash `json:"genesisStateRoot,omitempty"` + + // L2 base fee coefficients for the formula: `l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead`. + // These fields are populated from configuration flags, and passed to `CalcBaseFee`. + BaseFeeScalar *big.Int `json:"-"` + BaseFeeOverhead *big.Int `json:"-"` } // L1Config contains the l1 parameters needed to sync l1 contract events (e.g., l1 messages, commit/revert/finalize batches) in the sequencer @@ -753,8 +758,18 @@ func (s ScrollConfig) String() string { genesisStateRoot = fmt.Sprintf("%v", *s.GenesisStateRoot) } - return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v}", - s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot) + baseFeeScalar := "" + if s.BaseFeeScalar != nil { + baseFeeScalar = s.BaseFeeScalar.String() + } + + baseFeeOverhead := "" + if s.BaseFeeOverhead != nil { + baseFeeOverhead = s.BaseFeeOverhead.String() + } + + return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v, baseFeeScalar: %v, baseFeeOverhead: %v}", + s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot, baseFeeScalar, baseFeeOverhead) } // IsValidTxCount returns whether the given block's transaction count is below the limit. diff --git a/params/version.go b/params/version.go index 7105fe6f63..07af53cefd 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 44 // Patch version component of the current release + VersionPatch = 45 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string )