Skip to content

Commit cdd77c1

Browse files
authored
feat: update base fee via contract (#1189)
* Revert "feat: update base fee via cli (#1183)" This reverts commit 0b6f8f1. * feat: update l2 base fee via system contract * nit * bump version * fix tests * update contract abi * init coefficients on startup * nil check * update test * update txpool gas price * init with default value * update scroll sepolia and mainnet contract addresses * nit * update txpool min price during startup * print stack trace for worker panic * fix event parsing * fix log * improve txpool update logic * move initializeL2BaseFeeCoefficients to misc package * bump version * add API to query L2 base fee coefficients via console * fix code review suggestion
1 parent d8f4932 commit cdd77c1

File tree

18 files changed

+338
-104
lines changed

18 files changed

+338
-104
lines changed

cmd/geth/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/scroll-tech/go-ethereum/accounts/keystore"
3030
"github.com/scroll-tech/go-ethereum/cmd/utils"
3131
"github.com/scroll-tech/go-ethereum/common"
32+
"github.com/scroll-tech/go-ethereum/consensus/misc"
3233
"github.com/scroll-tech/go-ethereum/console/prompt"
3334
"github.com/scroll-tech/go-ethereum/eth"
3435
"github.com/scroll-tech/go-ethereum/eth/downloader"
@@ -185,8 +186,6 @@ var (
185186
utils.DARecoverySignBlocksFlag,
186187
utils.DARecoveryL2EndBlockFlag,
187188
utils.DARecoveryProduceBlocksFlag,
188-
utils.L2BaseFeeScalarFlag,
189-
utils.L2BaseFeeOverheadFlag,
190189
}
191190

192191
rpcFlags = []cli.Flag{
@@ -455,8 +454,9 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
455454
utils.Fatalf("Ethereum service not running")
456455
}
457456
// Set the gas price to the limits from the CLI and start mining
458-
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
459-
ethBackend.TxPool().SetGasPrice(gasprice)
457+
// gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
458+
// ethBackend.TxPool().SetGasPrice(gasprice)
459+
ethBackend.TxPool().SetGasPrice(misc.MinBaseFee()) // override configured min gas price
460460
ethBackend.TxPool().SetIsMiner(true)
461461
// start mining
462462
threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)

cmd/geth/usage.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
236236
utils.L1DeploymentBlockFlag,
237237
utils.L1DisableMessageQueueV2Flag,
238238
utils.RollupVerifyEnabledFlag,
239-
utils.L2BaseFeeScalarFlag,
240-
utils.L2BaseFeeOverheadFlag,
241239
utils.DASyncEnabledFlag,
242240
utils.DABlobScanAPIEndpointFlag,
243241
utils.DABlockNativeAPIEndpointFlag,

cmd/utils/flags.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import (
4747
"github.com/scroll-tech/go-ethereum/consensus"
4848
"github.com/scroll-tech/go-ethereum/consensus/clique"
4949
"github.com/scroll-tech/go-ethereum/consensus/ethash"
50-
"github.com/scroll-tech/go-ethereum/consensus/misc"
5150
"github.com/scroll-tech/go-ethereum/core"
5251
"github.com/scroll-tech/go-ethereum/core/rawdb"
5352
"github.com/scroll-tech/go-ethereum/core/vm"
@@ -935,18 +934,6 @@ var (
935934
Name: "da.recovery.produceblocks",
936935
Usage: "Produce unsigned blocks after L1 recovery for permissionless batch submission",
937936
}
938-
939-
// L2 base fee settings
940-
L2BaseFeeScalarFlag = BigFlag{
941-
Name: "basefee.scalar",
942-
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.",
943-
Value: misc.DefaultBaseFeeScalar,
944-
}
945-
L2BaseFeeOverheadFlag = BigFlag{
946-
Name: "basefee.overhead",
947-
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.",
948-
Value: misc.DefaultBaseFeeOverhead,
949-
}
950937
)
951938

952939
// MakeDataDir retrieves the currently requested data directory, terminating
@@ -1722,29 +1709,6 @@ func setDA(ctx *cli.Context, cfg *ethconfig.Config) {
17221709
}
17231710
}
17241711

1725-
func setBaseFee(ctx *cli.Context, cfg *ethconfig.Config) {
1726-
cfg.BaseFeeScalar = misc.DefaultBaseFeeScalar
1727-
if ctx.GlobalIsSet(L2BaseFeeScalarFlag.Name) {
1728-
cfg.BaseFeeScalar = GlobalBig(ctx, L2BaseFeeScalarFlag.Name)
1729-
}
1730-
cfg.BaseFeeOverhead = misc.DefaultBaseFeeOverhead
1731-
if ctx.GlobalIsSet(L2BaseFeeOverheadFlag.Name) {
1732-
cfg.BaseFeeOverhead = GlobalBig(ctx, L2BaseFeeOverheadFlag.Name)
1733-
}
1734-
1735-
log.Info("L2 base fee coefficients", "scalar", cfg.BaseFeeScalar, "overhead", cfg.BaseFeeOverhead)
1736-
1737-
var minBaseFee uint64
1738-
if fee := misc.MinBaseFee(cfg.BaseFeeScalar, cfg.BaseFeeOverhead); fee.IsUint64() {
1739-
minBaseFee = fee.Uint64()
1740-
}
1741-
1742-
if cfg.TxPool.PriceLimit < minBaseFee {
1743-
log.Warn("Updating txpool price limit to min L2 base fee", "provided", cfg.TxPool.PriceLimit, "updated", minBaseFee)
1744-
cfg.TxPool.PriceLimit = minBaseFee
1745-
}
1746-
}
1747-
17481712
func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) {
17491713
if ctx.GlobalIsSet(MaxBlockRangeFlag.Name) {
17501714
cfg.MaxBlockRange = ctx.GlobalInt64(MaxBlockRangeFlag.Name)
@@ -1821,7 +1785,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
18211785
setCircuitCapacityCheck(ctx, cfg)
18221786
setEnableRollupVerify(ctx, cfg)
18231787
setDA(ctx, cfg)
1824-
setBaseFee(ctx, cfg)
18251788
setMaxBlockRange(ctx, cfg)
18261789
if ctx.GlobalIsSet(ShadowforkPeersFlag.Name) {
18271790
cfg.ShadowForkPeerIDs = ctx.GlobalStringSlice(ShadowforkPeersFlag.Name)

consensus/misc/eip1559.go

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,67 @@ package misc
1919
import (
2020
"fmt"
2121
"math/big"
22+
"sync"
2223

24+
"github.com/scroll-tech/go-ethereum/common"
2325
"github.com/scroll-tech/go-ethereum/core/types"
26+
"github.com/scroll-tech/go-ethereum/log"
2427
"github.com/scroll-tech/go-ethereum/params"
28+
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
29+
"github.com/scroll-tech/go-ethereum/rpc"
2530
)
2631

27-
// Protocol-enforced maximum L2 base fee.
28-
// We would only go above this if L1 base fee hits 2931 Gwei.
29-
const MaximumL2BaseFee = 10000000000
32+
const (
33+
// Protocol-enforced maximum L2 base fee.
34+
// We would only go above this if L1 base fee hits 2931 Gwei.
35+
MaximumL2BaseFee = 10000000000
36+
37+
// L2 base fee fallback values, in case the L2 system contract
38+
// is not deployed on not configured yet.
39+
DefaultBaseFeeOverhead = 15680000
40+
DefaultBaseFeeScalar = 34000000000000
41+
)
3042

3143
// L2 base fee formula constants and defaults.
3244
// l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead.
3345
// `scalar` accounts for finalization costs. `overhead` accounts for sequencing and proving costs.
34-
// we use 1e18 for precision to match the contract implementation.
3546
var (
36-
BaseFeePrecision = new(big.Int).SetUint64(1e18)
37-
DefaultBaseFeeScalar = new(big.Int).SetUint64(34000000000000)
38-
DefaultBaseFeeOverhead = new(big.Int).SetUint64(15680000)
47+
// We use 1e18 for precision to match the contract implementation.
48+
BaseFeePrecision = new(big.Int).SetUint64(1e18)
49+
50+
// scalar and overhead are updated automatically in `Blockchain.writeBlockWithState`.
51+
baseFeeScalar = big.NewInt(0)
52+
baseFeeOverhead = big.NewInt(0)
53+
54+
lock sync.RWMutex
3955
)
4056

57+
func ReadL2BaseFeeCoefficients() (scalar *big.Int, overhead *big.Int) {
58+
lock.RLock()
59+
defer lock.RUnlock()
60+
return new(big.Int).Set(baseFeeScalar), new(big.Int).Set(baseFeeOverhead)
61+
}
62+
63+
func UpdateL2BaseFeeOverhead(newOverhead *big.Int) {
64+
if newOverhead == nil {
65+
log.Error("Failed to set L2 base fee overhead, new value is <nil>")
66+
return
67+
}
68+
lock.Lock()
69+
defer lock.Unlock()
70+
baseFeeOverhead.Set(newOverhead)
71+
}
72+
73+
func UpdateL2BaseFeeScalar(newScalar *big.Int) {
74+
if newScalar == nil {
75+
log.Error("Failed to set L2 base fee scalar, new value is <nil>")
76+
return
77+
}
78+
lock.Lock()
79+
defer lock.Unlock()
80+
baseFeeScalar.Set(newScalar)
81+
}
82+
4183
// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
4284
// - gas limit check
4385
// - basefee check
@@ -65,20 +107,13 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseF
65107
return big.NewInt(10000000) // 0.01 Gwei
66108
}
67109

68-
scalar := config.Scroll.BaseFeeScalar
69-
if scalar == nil {
70-
scalar = DefaultBaseFeeScalar
71-
}
72-
overhead := config.Scroll.BaseFeeOverhead
73-
if overhead == nil {
74-
overhead = DefaultBaseFeeOverhead
75-
}
76-
110+
scalar, overhead := ReadL2BaseFeeCoefficients()
77111
return calcBaseFee(scalar, overhead, parentL1BaseFee)
78112
}
79113

80-
// MinBaseFee calculates the minimum L2 base fee based on the configured coefficients.
81-
func MinBaseFee(scalar, overhead *big.Int) *big.Int {
114+
// MinBaseFee calculates the minimum L2 base fee based on the current coefficients.
115+
func MinBaseFee() *big.Int {
116+
scalar, overhead := ReadL2BaseFeeCoefficients()
82117
return calcBaseFee(scalar, overhead, big.NewInt(0))
83118
}
84119

@@ -94,3 +129,60 @@ func calcBaseFee(scalar, overhead, parentL1BaseFee *big.Int) *big.Int {
94129

95130
return baseFee
96131
}
132+
133+
type State interface {
134+
GetState(addr common.Address, hash common.Hash) common.Hash
135+
}
136+
137+
func InitializeL2BaseFeeCoefficients(chainConfig *params.ChainConfig, state State) error {
138+
overhead := common.Big0
139+
scalar := common.Big0
140+
141+
if l2SystemConfig := chainConfig.Scroll.L2SystemConfigAddress(); l2SystemConfig != (common.Address{}) {
142+
overhead = state.GetState(l2SystemConfig, rcfg.L2BaseFeeOverheadSlot).Big()
143+
scalar = state.GetState(l2SystemConfig, rcfg.L2BaseFeeScalarSlot).Big()
144+
} else {
145+
log.Warn("L2SystemConfig address is not configured")
146+
}
147+
148+
// fallback to default if contract is not deployed or configured yet
149+
if overhead.Cmp(common.Big0) == 0 {
150+
overhead = big.NewInt(DefaultBaseFeeOverhead)
151+
}
152+
if scalar.Cmp(common.Big0) == 0 {
153+
scalar = big.NewInt(DefaultBaseFeeScalar)
154+
}
155+
156+
// update local view of coefficients
157+
lock.Lock()
158+
defer lock.Unlock()
159+
baseFeeOverhead.Set(overhead)
160+
baseFeeScalar.Set(scalar)
161+
log.Info("Initialized L2 base fee coefficients", "overhead", overhead, "scalar", scalar)
162+
return nil
163+
}
164+
165+
type API struct{}
166+
167+
type L2BaseFeeConfig struct {
168+
Scalar *big.Int `json:"scalar,omitempty"`
169+
Overhead *big.Int `json:"overhead,omitempty"`
170+
}
171+
172+
func (api *API) GetL2BaseFeeConfig() *L2BaseFeeConfig {
173+
scalar, overhead := ReadL2BaseFeeCoefficients()
174+
175+
return &L2BaseFeeConfig{
176+
Scalar: scalar,
177+
Overhead: overhead,
178+
}
179+
}
180+
181+
func APIs() []rpc.API {
182+
return []rpc.API{{
183+
Namespace: "scroll",
184+
Version: "1.0",
185+
Service: &API{},
186+
Public: false,
187+
}}
188+
}

consensus/misc/eip1559_test.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ func TestCalcBaseFee(t *testing.T) {
122122
}
123123
for i, test := range tests {
124124
config := config()
125-
config.Scroll.BaseFeeScalar = big.NewInt(10000000)
126-
config.Scroll.BaseFeeOverhead = big.NewInt(1)
125+
UpdateL2BaseFeeScalar(big.NewInt(10000000))
126+
UpdateL2BaseFeeOverhead(big.NewInt(1))
127127
if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
128128
t.Errorf("test %d: have %d want %d, ", i, have, want)
129129
}
@@ -142,6 +142,8 @@ func TestCalcBaseFee(t *testing.T) {
142142
{644149677419355, 10000000000}, // cap at max L2 base fee
143143
}
144144
for i, test := range testsWithDefaults {
145+
UpdateL2BaseFeeScalar(big.NewInt(34000000000000))
146+
UpdateL2BaseFeeOverhead(big.NewInt(15680000))
145147
if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
146148
t.Errorf("test %d: have %d want %d, ", i, have, want)
147149
}
@@ -150,11 +152,15 @@ func TestCalcBaseFee(t *testing.T) {
150152

151153
// TestMinBaseFee assumes all blocks are 1559-blocks
152154
func TestMinBaseFee(t *testing.T) {
153-
if have, want := MinBaseFee(DefaultBaseFeeScalar, DefaultBaseFeeOverhead), big.NewInt(15680000); have.Cmp(want) != 0 {
155+
UpdateL2BaseFeeScalar(big.NewInt(34000000000000))
156+
UpdateL2BaseFeeOverhead(big.NewInt(15680000))
157+
if have, want := MinBaseFee(), big.NewInt(15680000); have.Cmp(want) != 0 {
154158
t.Errorf("have %d want %d, ", have, want)
155159
}
156160

157-
if have, want := MinBaseFee(big.NewInt(10000000), big.NewInt(1)), big.NewInt(1); have.Cmp(want) != 0 {
161+
UpdateL2BaseFeeScalar(big.NewInt(10000000))
162+
UpdateL2BaseFeeOverhead(big.NewInt(1))
163+
if have, want := MinBaseFee(), big.NewInt(1); have.Cmp(want) != 0 {
158164
t.Errorf("have %d want %d, ", have, want)
159165
}
160166
}

core/blockchain.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/scroll-tech/go-ethereum/common/mclock"
3535
"github.com/scroll-tech/go-ethereum/common/prque"
3636
"github.com/scroll-tech/go-ethereum/consensus"
37+
"github.com/scroll-tech/go-ethereum/consensus/misc"
3738
"github.com/scroll-tech/go-ethereum/core/rawdb"
3839
"github.com/scroll-tech/go-ethereum/core/state"
3940
"github.com/scroll-tech/go-ethereum/core/state/snapshot"
@@ -45,6 +46,7 @@ import (
4546
"github.com/scroll-tech/go-ethereum/log"
4647
"github.com/scroll-tech/go-ethereum/metrics"
4748
"github.com/scroll-tech/go-ethereum/params"
49+
"github.com/scroll-tech/go-ethereum/rollup/l2_system_config"
4850
"github.com/scroll-tech/go-ethereum/trie"
4951
)
5052

@@ -55,7 +57,8 @@ var (
5557
headTimeGapGauge = metrics.NewRegisteredGauge("chain/head/timegap", nil)
5658
headL1MessageGauge = metrics.NewRegisteredGauge("chain/head/l1msg", nil)
5759

58-
l2BaseFeeGauge = metrics.NewRegisteredGauge("chain/fees/l2basefee", nil)
60+
l2BaseFeeGauge = metrics.NewRegisteredGauge("chain/fees/l2basefee", nil)
61+
l2BaseFeeUpdateTimer = metrics.NewRegisteredTimer("chain/fees/updates", nil)
5962

6063
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
6164
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
@@ -161,6 +164,39 @@ func updateHeadL1msgGauge(block *types.Block) {
161164
}
162165
}
163166

167+
// updateL2BaseFeeCoefficients updates the global L2 base fee coefficients.
168+
// Coefficient updates are written into L2 state and emit an event.
169+
// We could use either here; we read from the event to avoid state reads.
170+
// In the future, if the base fee setting becomes part of block validation,
171+
// reading from state will be more appropriate.
172+
func updateL2BaseFeeCoefficients(l2SystemConfigAddress common.Address, logs []*types.Log) {
173+
defer func(start time.Time) { l2BaseFeeUpdateTimer.Update(time.Since(start)) }(time.Now())
174+
175+
for _, l := range logs {
176+
if l.Address != l2SystemConfigAddress {
177+
continue
178+
}
179+
switch l.Topics[0] {
180+
case l2_system_config.BaseFeeOverheadUpdatedTopic:
181+
event, err := l2_system_config.UnpackBaseFeeOverheadUpdatedEvent(*l)
182+
if err != nil {
183+
log.Error("failed to unpack base fee overhead updated event log", "err", err, "log", *l)
184+
break // break from switch, continue loop
185+
}
186+
misc.UpdateL2BaseFeeOverhead(event.NewBaseFeeOverhead)
187+
log.Info("Updated L2 base fee overhead", "blockNumber", l.BlockNumber, "blockHash", l.BlockHash.Hex(), "old", event.OldBaseFeeOverhead, "new", event.NewBaseFeeOverhead)
188+
case l2_system_config.BaseFeeScalarUpdatedTopic:
189+
event, err := l2_system_config.UnpackBaseFeeScalarUpdatedEvent(*l)
190+
if err != nil {
191+
log.Error("failed to unpack base fee scalar updated event log", "err", err, "log", *l)
192+
break // break from switch, continue loop
193+
}
194+
misc.UpdateL2BaseFeeScalar(event.NewBaseFeeScalar)
195+
log.Info("Updated L2 base fee scalar", "blockNumber", l.BlockNumber, "blockHash", l.BlockHash.Hex(), "old", event.OldBaseFeeScalar, "new", event.NewBaseFeeScalar)
196+
}
197+
}
198+
}
199+
164200
// BlockChain represents the canonical chain given a database with a genesis
165201
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
166202
//
@@ -1272,6 +1308,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
12721308
// Note the latest relayed L1 message queue index (if any)
12731309
updateHeadL1msgGauge(block)
12741310

1311+
// Execute L2 base fee coefficient updates (if any)
1312+
updateL2BaseFeeCoefficients(bc.Config().Scroll.L2SystemConfigAddress(), logs)
1313+
12751314
parent := bc.GetHeaderByHash(block.ParentHash())
12761315
// block.Time is guaranteed to be larger than parent.Time,
12771316
// and the time gap should fit into int64.
@@ -1302,7 +1341,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
13021341
if queueIndex == nil {
13031342
// We expect that we only insert contiguous chain segments,
13041343
// so the parent will always be inserted first.
1305-
log.Crit("Queue index in DB is nil", "parent", block.ParentHash(), "hash", block.Hash())
1344+
log.Crit("Queue index in DB is nil", "parent", block.ParentHash().Hex(), "hash", block.Hash().Hex())
13061345
}
13071346
numProcessed := uint64(block.NumL1MessagesProcessed(*queueIndex))
13081347
// do not overwrite the index written by the miner worker

0 commit comments

Comments
 (0)