diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 0377609ceba6..c1fbb08746a5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -29,6 +29,7 @@ import ( "github.com/scroll-tech/go-ethereum/accounts/keystore" "github.com/scroll-tech/go-ethereum/cmd/utils" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/console/prompt" "github.com/scroll-tech/go-ethereum/eth" "github.com/scroll-tech/go-ethereum/eth/downloader" @@ -185,8 +186,6 @@ var ( utils.DARecoverySignBlocksFlag, utils.DARecoveryL2EndBlockFlag, utils.DARecoveryProduceBlocksFlag, - utils.L2BaseFeeScalarFlag, - utils.L2BaseFeeOverheadFlag, } rpcFlags = []cli.Flag{ @@ -455,8 +454,9 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { utils.Fatalf("Ethereum service not running") } // Set the gas price to the limits from the CLI and start mining - gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) - ethBackend.TxPool().SetGasPrice(gasprice) + // gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) + // ethBackend.TxPool().SetGasPrice(gasprice) + ethBackend.TxPool().SetGasPrice(misc.MinBaseFee()) // override configured min gas price ethBackend.TxPool().SetIsMiner(true) // start mining threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index df99d6eed020..1d299d339fe8 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -236,8 +236,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.L1DeploymentBlockFlag, utils.L1DisableMessageQueueV2Flag, utils.RollupVerifyEnabledFlag, - utils.L2BaseFeeScalarFlag, - utils.L2BaseFeeOverheadFlag, utils.DASyncEnabledFlag, utils.DABlobScanAPIEndpointFlag, utils.DABlockNativeAPIEndpointFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 10c8a60e5307..7371a3770074 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -47,7 +47,6 @@ 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" @@ -935,18 +934,6 @@ 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 @@ -1722,29 +1709,6 @@ 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) @@ -1821,7 +1785,6 @@ 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 b6443e76b0ab..9990df04a7d1 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559.go @@ -19,25 +19,67 @@ package misc import ( "fmt" "math/big" + "sync" + "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/rcfg" + "github.com/scroll-tech/go-ethereum/rpc" ) -// Protocol-enforced maximum L2 base fee. -// We would only go above this if L1 base fee hits 2931 Gwei. -const MaximumL2BaseFee = 10000000000 +const ( + // Protocol-enforced maximum L2 base fee. + // We would only go above this if L1 base fee hits 2931 Gwei. + MaximumL2BaseFee = 10000000000 + + // L2 base fee fallback values, in case the L2 system contract + // is not deployed on not configured yet. + DefaultBaseFeeOverhead = 15680000 + DefaultBaseFeeScalar = 34000000000000 +) // 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) + // We use 1e18 for precision to match the contract implementation. + BaseFeePrecision = new(big.Int).SetUint64(1e18) + + // scalar and overhead are updated automatically in `Blockchain.writeBlockWithState`. + baseFeeScalar = big.NewInt(0) + baseFeeOverhead = big.NewInt(0) + + lock sync.RWMutex ) +func ReadL2BaseFeeCoefficients() (scalar *big.Int, overhead *big.Int) { + lock.RLock() + defer lock.RUnlock() + return new(big.Int).Set(baseFeeScalar), new(big.Int).Set(baseFeeOverhead) +} + +func UpdateL2BaseFeeOverhead(newOverhead *big.Int) { + if newOverhead == nil { + log.Error("Failed to set L2 base fee overhead, new value is ") + return + } + lock.Lock() + defer lock.Unlock() + baseFeeOverhead.Set(newOverhead) +} + +func UpdateL2BaseFeeScalar(newScalar *big.Int) { + if newScalar == nil { + log.Error("Failed to set L2 base fee scalar, new value is ") + return + } + lock.Lock() + defer lock.Unlock() + baseFeeScalar.Set(newScalar) +} + // VerifyEip1559Header verifies some header attributes which were changed in EIP-1559, // - gas limit check // - basefee check @@ -65,20 +107,13 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseF return big.NewInt(10000000) // 0.01 Gwei } - scalar := config.Scroll.BaseFeeScalar - if scalar == nil { - scalar = DefaultBaseFeeScalar - } - overhead := config.Scroll.BaseFeeOverhead - if overhead == nil { - overhead = DefaultBaseFeeOverhead - } - + scalar, overhead := ReadL2BaseFeeCoefficients() 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 { +// MinBaseFee calculates the minimum L2 base fee based on the current coefficients. +func MinBaseFee() *big.Int { + scalar, overhead := ReadL2BaseFeeCoefficients() return calcBaseFee(scalar, overhead, big.NewInt(0)) } @@ -94,3 +129,60 @@ func calcBaseFee(scalar, overhead, parentL1BaseFee *big.Int) *big.Int { return baseFee } + +type State interface { + GetState(addr common.Address, hash common.Hash) common.Hash +} + +func InitializeL2BaseFeeCoefficients(chainConfig *params.ChainConfig, state State) error { + overhead := common.Big0 + scalar := common.Big0 + + if l2SystemConfig := chainConfig.Scroll.L2SystemConfigAddress(); l2SystemConfig != (common.Address{}) { + overhead = state.GetState(l2SystemConfig, rcfg.L2BaseFeeOverheadSlot).Big() + scalar = state.GetState(l2SystemConfig, rcfg.L2BaseFeeScalarSlot).Big() + } else { + log.Warn("L2SystemConfig address is not configured") + } + + // fallback to default if contract is not deployed or configured yet + if overhead.Cmp(common.Big0) == 0 { + overhead = big.NewInt(DefaultBaseFeeOverhead) + } + if scalar.Cmp(common.Big0) == 0 { + scalar = big.NewInt(DefaultBaseFeeScalar) + } + + // update local view of coefficients + lock.Lock() + defer lock.Unlock() + baseFeeOverhead.Set(overhead) + baseFeeScalar.Set(scalar) + log.Info("Initialized L2 base fee coefficients", "overhead", overhead, "scalar", scalar) + return nil +} + +type API struct{} + +type L2BaseFeeConfig struct { + Scalar *big.Int `json:"scalar,omitempty"` + Overhead *big.Int `json:"overhead,omitempty"` +} + +func (api *API) GetL2BaseFeeConfig() *L2BaseFeeConfig { + scalar, overhead := ReadL2BaseFeeCoefficients() + + return &L2BaseFeeConfig{ + Scalar: scalar, + Overhead: overhead, + } +} + +func APIs() []rpc.API { + return []rpc.API{{ + Namespace: "scroll", + Version: "1.0", + Service: &API{}, + Public: false, + }} +} diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index bab8c4e49681..d41d3e59666f 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -122,8 +122,8 @@ func TestCalcBaseFee(t *testing.T) { } for i, test := range tests { config := config() - config.Scroll.BaseFeeScalar = big.NewInt(10000000) - config.Scroll.BaseFeeOverhead = big.NewInt(1) + UpdateL2BaseFeeScalar(big.NewInt(10000000)) + UpdateL2BaseFeeOverhead(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) } @@ -142,6 +142,8 @@ func TestCalcBaseFee(t *testing.T) { {644149677419355, 10000000000}, // cap at max L2 base fee } for i, test := range testsWithDefaults { + UpdateL2BaseFeeScalar(big.NewInt(34000000000000)) + UpdateL2BaseFeeOverhead(big.NewInt(15680000)) 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) } @@ -150,11 +152,15 @@ func TestCalcBaseFee(t *testing.T) { // 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 { + UpdateL2BaseFeeScalar(big.NewInt(34000000000000)) + UpdateL2BaseFeeOverhead(big.NewInt(15680000)) + if have, want := MinBaseFee(), 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 { + UpdateL2BaseFeeScalar(big.NewInt(10000000)) + UpdateL2BaseFeeOverhead(big.NewInt(1)) + if have, want := MinBaseFee(), big.NewInt(1); have.Cmp(want) != 0 { t.Errorf("have %d want %d, ", have, want) } } diff --git a/core/blockchain.go b/core/blockchain.go index 821564769e49..57a803198fa1 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -34,6 +34,7 @@ import ( "github.com/scroll-tech/go-ethereum/common/mclock" "github.com/scroll-tech/go-ethereum/common/prque" "github.com/scroll-tech/go-ethereum/consensus" + "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/state/snapshot" @@ -45,6 +46,7 @@ import ( "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/metrics" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/l2_system_config" "github.com/scroll-tech/go-ethereum/trie" ) @@ -55,7 +57,8 @@ var ( headTimeGapGauge = metrics.NewRegisteredGauge("chain/head/timegap", nil) headL1MessageGauge = metrics.NewRegisteredGauge("chain/head/l1msg", nil) - l2BaseFeeGauge = metrics.NewRegisteredGauge("chain/fees/l2basefee", nil) + l2BaseFeeGauge = metrics.NewRegisteredGauge("chain/fees/l2basefee", nil) + l2BaseFeeUpdateTimer = metrics.NewRegisteredTimer("chain/fees/updates", nil) accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) @@ -161,6 +164,39 @@ func updateHeadL1msgGauge(block *types.Block) { } } +// updateL2BaseFeeCoefficients updates the global L2 base fee coefficients. +// Coefficient updates are written into L2 state and emit an event. +// We could use either here; we read from the event to avoid state reads. +// In the future, if the base fee setting becomes part of block validation, +// reading from state will be more appropriate. +func updateL2BaseFeeCoefficients(l2SystemConfigAddress common.Address, logs []*types.Log) { + defer func(start time.Time) { l2BaseFeeUpdateTimer.Update(time.Since(start)) }(time.Now()) + + for _, l := range logs { + if l.Address != l2SystemConfigAddress { + continue + } + switch l.Topics[0] { + case l2_system_config.BaseFeeOverheadUpdatedTopic: + event, err := l2_system_config.UnpackBaseFeeOverheadUpdatedEvent(*l) + if err != nil { + log.Error("failed to unpack base fee overhead updated event log", "err", err, "log", *l) + break // break from switch, continue loop + } + misc.UpdateL2BaseFeeOverhead(event.NewBaseFeeOverhead) + log.Info("Updated L2 base fee overhead", "blockNumber", l.BlockNumber, "blockHash", l.BlockHash.Hex(), "old", event.OldBaseFeeOverhead, "new", event.NewBaseFeeOverhead) + case l2_system_config.BaseFeeScalarUpdatedTopic: + event, err := l2_system_config.UnpackBaseFeeScalarUpdatedEvent(*l) + if err != nil { + log.Error("failed to unpack base fee scalar updated event log", "err", err, "log", *l) + break // break from switch, continue loop + } + misc.UpdateL2BaseFeeScalar(event.NewBaseFeeScalar) + log.Info("Updated L2 base fee scalar", "blockNumber", l.BlockNumber, "blockHash", l.BlockHash.Hex(), "old", event.OldBaseFeeScalar, "new", event.NewBaseFeeScalar) + } + } +} + // BlockChain represents the canonical chain given a database with a genesis // block. The Blockchain manages chain imports, reverts, chain reorganisations. // @@ -1272,6 +1308,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // Note the latest relayed L1 message queue index (if any) updateHeadL1msgGauge(block) + // Execute L2 base fee coefficient updates (if any) + updateL2BaseFeeCoefficients(bc.Config().Scroll.L2SystemConfigAddress(), logs) + parent := bc.GetHeaderByHash(block.ParentHash()) // block.Time is guaranteed to be larger than parent.Time, // and the time gap should fit into int64. @@ -1302,7 +1341,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if queueIndex == nil { // We expect that we only insert contiguous chain segments, // so the parent will always be inserted first. - log.Crit("Queue index in DB is nil", "parent", block.ParentHash(), "hash", block.Hash()) + log.Crit("Queue index in DB is nil", "parent", block.ParentHash().Hex(), "hash", block.Hash().Hex()) } numProcessed := uint64(block.NumL1MessagesProcessed(*queueIndex)) // do not overwrite the index written by the miner worker diff --git a/core/state_processor_test.go b/core/state_processor_test.go index ac5babcac3bc..a37e90e93c86 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -389,13 +389,13 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]), }, - want: "could not apply tx 0 [0x9fff9d187a68f9dce9664475ed9a01a5178992f15b44ce88ee7b1129a183e6af]: max initcode size exceeded: code size 49153 limit 49152", + want: "could not apply tx 0 [0x7b33776d375660694a23ef992c090265682f3687607e0099b14503fdb65d73e3]: max initcode size exceeded: code size 49153 limit 49152", }, { // ErrIntrinsicGas: Not enough gas to cover init code txs: []*types.Transaction{ mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), smallInitCode[:]), }, - want: "could not apply tx 0 [0x272eefb0eeb3b973e933ae5dba17e7ecf6bfded5ce358f2a78426153c247f677]: intrinsic gas too low: have 54299, want 54300", + want: "could not apply tx 0 [0x98e54c5ecfa7986a66480d65ba32f2c6a2a6aedc3a67abb91b1e118b0717ed2d]: intrinsic gas too low: have 54299, want 54300", }, } { block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) diff --git a/core/tx_pool.go b/core/tx_pool.go index 9a8c011c2a3f..67f3ee101224 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -424,6 +424,8 @@ func (pool *TxPool) loop() { journal = time.NewTicker(pool.config.Rejournal) // Track the previous head headers for transaction reorgs head = pool.chain.CurrentBlock() + // Track the min L2 base fee + minBaseFee = big.NewInt(0) ) defer report.Stop() defer evict.Stop() @@ -443,6 +445,13 @@ func (pool *TxPool) loop() { } } + newMinBaseFee := misc.MinBaseFee() + if minBaseFee.Cmp(newMinBaseFee) != 0 { + log.Debug("Updating min base fee in txpool", "old", minBaseFee, "new", newMinBaseFee) + minBaseFee = newMinBaseFee + pool.SetGasPrice(minBaseFee) + } + // System shutdown. case <-pool.chainHeadSub.Err(): close(pool.reorgShutdownCh) diff --git a/eth/api.go b/eth/api.go index e8b66b9e4e57..0752a3cb158c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -133,6 +133,8 @@ func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { api.e.gasPrice = (*big.Int)(&gasPrice) api.e.lock.Unlock() + // This will override the min base fee configuration. + // That is fine, it only happens if we explicitly set gas price via the console. api.e.txPool.SetGasPrice((*big.Int)(&gasPrice)) return true } diff --git a/eth/backend.go b/eth/backend.go index 99d35828d225..ad765b835b0a 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,6 +32,7 @@ import ( "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/consensus" "github.com/scroll-tech/go-ethereum/consensus/clique" + "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/consensus/system_contract" "github.com/scroll-tech/go-ethereum/consensus/wrapper" "github.com/scroll-tech/go-ethereum/core" @@ -148,13 +149,6 @@ 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 { @@ -220,6 +214,12 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether eth.blockchain.Validator().WithAsyncValidator(eth.asyncChecker.Check) } + state, err := eth.blockchain.State() + if err != nil { + return nil, err + } + misc.InitializeL2BaseFeeCoefficients(chainConfig, state) + // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) @@ -232,6 +232,7 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain) + eth.txPool.SetGasPrice(misc.MinBaseFee()) // Initialize and start DA syncing pipeline before SyncService as SyncService is blocking until all L1 messages are loaded. // We need SyncService to load the L1 messages for DA syncing, but since both sync from last known L1 state, we can @@ -361,6 +362,9 @@ func (s *Ethereum) APIs() []rpc.API { // Append any APIs exposed explicitly by the consensus engine apis = append(apis, s.engine.APIs(s.BlockChain())...) + // Append L2 base fee APIs. + apis = append(apis, misc.APIs()...) + if !s.config.EnableDASyncing { apis = append(apis, rpc.API{ Namespace: "eth", @@ -527,10 +531,11 @@ func (s *Ethereum) StartMining(threads int) error { // If the miner was not running, initialize it if !s.IsMining() { // Propagate the initial price point to the transaction pool - s.lock.RLock() - price := s.gasPrice - s.lock.RUnlock() - s.txPool.SetGasPrice(price) + // Disabled, we now update min gas price automatically via L2 base fee. + // s.lock.RLock() + // price := s.gasPrice + // s.lock.RUnlock() + // s.txPool.SetGasPrice(price) // Configure the local mining address eb, err := s.Etherbase() diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index f50109cb3ff1..94fa46c34c31 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -230,10 +230,6 @@ 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/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 5b1e768dd366..0fc93fec84f9 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -998,6 +998,10 @@ web3._extend({ name: 'localSigner', getter: 'scroll_getLocalSigner', }), + new web3._extend.Property({ + name: 'l2BaseFeeConfig', + getter: 'scroll_getL2BaseFeeConfig', + }), ] }); ` diff --git a/miner/scroll_worker.go b/miner/scroll_worker.go index b95fb6027aee..e58c6360e7b4 100644 --- a/miner/scroll_worker.go +++ b/miner/scroll_worker.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "math/big" + "runtime/debug" "sync" "sync/atomic" "time" @@ -341,6 +342,7 @@ func (w *worker) mainLoop() { p := recover() if p != nil { log.Error("worker mainLoop panic", "panic", p) + fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) } }() diff --git a/params/config.go b/params/config.go index 28eb863ba6e9..96b77e04a7f4 100644 --- a/params/config.go +++ b/params/config.go @@ -351,6 +351,7 @@ var ( L1MessageQueueV2DeploymentBlock: 7773746, NumL1MessagesPerBlock: 10, ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), + L2SystemConfigAddress: common.HexToAddress("0xF444cF06A3E3724e20B35c2989d3942ea8b59124"), }, GenesisStateRoot: &ScrollSepoliaGenesisState, }, @@ -401,6 +402,7 @@ var ( L1MessageQueueV2DeploymentBlock: 22088276, NumL1MessagesPerBlock: 10, ScrollChainAddress: common.HexToAddress("0xa13BAF47339d63B743e7Da8741db5456DAc1E556"), + L2SystemConfigAddress: common.HexToAddress("0x331A873a2a85219863d80d248F9e2978fE88D0Ea"), }, GenesisStateRoot: &ScrollMainnetGenesisState, }, @@ -704,11 +706,6 @@ 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 @@ -719,6 +716,7 @@ type L1Config struct { L1MessageQueueV2DeploymentBlock uint64 `json:"l1MessageQueueV2DeploymentBlock,omitempty"` NumL1MessagesPerBlock uint64 `json:"numL1MessagesPerBlock,string,omitempty"` ScrollChainAddress common.Address `json:"scrollChainAddress,omitempty"` + L2SystemConfigAddress common.Address `json:"l2SystemConfigAddress,omitempty"` } func (c *L1Config) String() string { @@ -726,8 +724,8 @@ func (c *L1Config) String() string { return "" } - return fmt.Sprintf("{l1ChainId: %v, l1MessageQueueAddress: %v, l1MessageQueueV2Address: %v, l1MessageQueueV2DeploymentBlock: %v, numL1MessagesPerBlock: %v, ScrollChainAddress: %v}", - c.L1ChainId, c.L1MessageQueueAddress.Hex(), c.L1MessageQueueV2Address.Hex(), c.L1MessageQueueV2DeploymentBlock, c.NumL1MessagesPerBlock, c.ScrollChainAddress.Hex()) + return fmt.Sprintf("{l1ChainId: %v, l1MessageQueueAddress: %v, l1MessageQueueV2Address: %v, l1MessageQueueV2DeploymentBlock: %v, numL1MessagesPerBlock: %v, ScrollChainAddress: %v, L2SystemConfigAddress: %v}", + c.L1ChainId, c.L1MessageQueueAddress.Hex(), c.L1MessageQueueV2Address.Hex(), c.L1MessageQueueV2DeploymentBlock, c.NumL1MessagesPerBlock, c.ScrollChainAddress.Hex(), c.L2SystemConfigAddress.Hex()) } func (s ScrollConfig) FeeVaultEnabled() bool { @@ -758,18 +756,8 @@ func (s ScrollConfig) String() string { genesisStateRoot = fmt.Sprintf("%v", *s.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) + 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) } // IsValidTxCount returns whether the given block's transaction count is below the limit. @@ -788,6 +776,14 @@ func (s ScrollConfig) IsValidBlockSizeForMining(size common.StorageSize) bool { return s.IsValidBlockSize(size * (1.0 / 0.95)) } +// L2SystemConfigAddress returns the configured l2 system config address, or the zero address if it is not configured. +func (s ScrollConfig) L2SystemConfigAddress() common.Address { + if s.L1Config == nil { + return common.Address{} // only in tests + } + return s.L1Config.L2SystemConfigAddress +} + // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} diff --git a/params/version.go b/params/version.go index 12c6206f5478..dd5c54baa4e8 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 = 50 // Patch version component of the current release + VersionPatch = 51 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/l2_system_config/abi.go b/rollup/l2_system_config/abi.go new file mode 100644 index 000000000000..b30361f07932 --- /dev/null +++ b/rollup/l2_system_config/abi.go @@ -0,0 +1,54 @@ +package l2_system_config + +import ( + "math/big" + + "github.com/scroll-tech/go-ethereum/accounts/abi" + "github.com/scroll-tech/go-ethereum/accounts/abi/bind" + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/rollup/l1" +) + +var ( + l2SystemConfigABI *abi.ABI + + baseFeeOverheadUpdatedEventName = "BaseFeeOverheadUpdated" + baseFeeScalarUpdatedEventName = "BaseFeeScalarUpdated" + + BaseFeeOverheadUpdatedTopic common.Hash + BaseFeeScalarUpdatedTopic common.Hash +) + +func init() { + l2SystemConfigABI, _ = l2SystemConfigMetaData.GetAbi() + + BaseFeeOverheadUpdatedTopic = l2SystemConfigABI.Events[baseFeeOverheadUpdatedEventName].ID + BaseFeeScalarUpdatedTopic = l2SystemConfigABI.Events[baseFeeScalarUpdatedEventName].ID +} + +var l2SystemConfigMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"event\",\"name\":\"BaseFeeOverheadUpdated\",\"inputs\":[{\"name\":\"oldBaseFeeOverhead\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newBaseFeeOverhead\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BaseFeeScalarUpdated\",\"inputs\":[{\"name\":\"oldBaseFeeScalar\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newBaseFeeScalar\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false}]", +} + +type BaseFeeOverheadUpdatedEventUnpacked struct { + OldBaseFeeOverhead *big.Int + NewBaseFeeOverhead *big.Int +} + +type BaseFeeScalarUpdatedEventUnpacked struct { + OldBaseFeeScalar *big.Int + NewBaseFeeScalar *big.Int +} + +func UnpackBaseFeeOverheadUpdatedEvent(log types.Log) (*BaseFeeOverheadUpdatedEventUnpacked, error) { + event := &BaseFeeOverheadUpdatedEventUnpacked{} + err := l1.UnpackLog(l2SystemConfigABI, event, baseFeeOverheadUpdatedEventName, log) + return event, err +} + +func UnpackBaseFeeScalarUpdatedEvent(log types.Log) (*BaseFeeScalarUpdatedEventUnpacked, error) { + event := &BaseFeeScalarUpdatedEventUnpacked{} + err := l1.UnpackLog(l2SystemConfigABI, event, baseFeeScalarUpdatedEventName, log) + return event, err +} diff --git a/rollup/l2_system_config/abi_test.go b/rollup/l2_system_config/abi_test.go new file mode 100644 index 000000000000..db4059599b5c --- /dev/null +++ b/rollup/l2_system_config/abi_test.go @@ -0,0 +1,51 @@ +package l2_system_config + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/crypto" +) + +func TestEventSignatures(t *testing.T) { + assert.Equal(t, crypto.Keccak256Hash([]byte("BaseFeeOverheadUpdated(uint256,uint256)")), BaseFeeOverheadUpdatedTopic) + assert.Equal(t, crypto.Keccak256Hash([]byte("BaseFeeScalarUpdated(uint256,uint256)")), BaseFeeScalarUpdatedTopic) +} + +func bigToBytesPadded(num *big.Int) []byte { + return common.BigToHash(num).Bytes() +} + +func TestUnpackBaseFeeOverheadUpdatedLog(t *testing.T) { + old := common.Big1 + new := common.Big2 + + log := types.Log{ + Topics: []common.Hash{BaseFeeOverheadUpdatedTopic}, + Data: append(bigToBytesPadded(old), bigToBytesPadded(new)...), + } + + event, err := UnpackBaseFeeOverheadUpdatedEvent(log) + assert.NoError(t, err) + assert.Equal(t, common.Big1, event.OldBaseFeeOverhead) + assert.Equal(t, common.Big2, event.NewBaseFeeOverhead) +} + +func TestUnpackBaseFeeScalarUpdatedLog(t *testing.T) { + old := common.Big32 + new := common.Big256 + + log := types.Log{ + Topics: []common.Hash{BaseFeeScalarUpdatedTopic}, + Data: append(bigToBytesPadded(old), bigToBytesPadded(new)...), + } + + event, err := UnpackBaseFeeScalarUpdatedEvent(log) + assert.NoError(t, err) + assert.Equal(t, common.Big32, event.OldBaseFeeScalar) + assert.Equal(t, common.Big256, event.NewBaseFeeScalar) +} diff --git a/rollup/rcfg/config.go b/rollup/rcfg/config.go index 8f7931b74585..3bac185c8302 100644 --- a/rollup/rcfg/config.go +++ b/rollup/rcfg/config.go @@ -49,3 +49,20 @@ var ( // cat artifacts/src/L1GasPriceOracle.sol/L1GasPriceOracle.json | jq -r .deployedBytecode.object CurieL1GasPriceOracleBytecode = common.Hex2Bytes("608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033") ) + +// L2SystemConfig constants. +var ( + // > forge inspect src/L2/L2SystemConfig.sol:L2SystemConfig storageLayout + // ╭-----------------+-------------+------+--------+-------+------------------------------------------╮ + // | Name | Type | Slot | Offset | Bytes | Contract | + // +==================================================================================================+ + // ... + // |-----------------+-------------+------+--------+-------+------------------------------------------| + // | baseFeeOverhead | uint256 | 101 | 0 | 32 | src/L2/L2SystemConfig.sol:L2SystemConfig | + // |-----------------+-------------+------+--------+-------+------------------------------------------| + // | baseFeeScalar | uint256 | 102 | 0 | 32 | src/L2/L2SystemConfig.sol:L2SystemConfig | + // ╰-----------------+-------------+------+--------+-------+------------------------------------------╯ + + L2BaseFeeOverheadSlot = common.BigToHash(big.NewInt(101)) + L2BaseFeeScalarSlot = common.BigToHash(big.NewInt(102)) +)