From cfe6fd2f45b5f6ddb371e2e2d2647f49163a9ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 19:03:53 +0100 Subject: [PATCH 01/20] fix(sender): log msg on error --- common/version/version.go | 2 +- rollup/internal/controller/sender/estimategas.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/version/version.go b/common/version/version.go index 6418ac17b7..d77ad10a66 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.7.5" +var tag = "v4.7.6" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/internal/controller/sender/estimategas.go b/rollup/internal/controller/sender/estimategas.go index 3d29838655..d9c7ed802e 100644 --- a/rollup/internal/controller/sender/estimategas.go +++ b/rollup/internal/controller/sender/estimategas.go @@ -2,6 +2,7 @@ package sender import ( "errors" + "fmt" "math/big" "github.com/scroll-tech/go-ethereum" @@ -118,7 +119,7 @@ func (s *Sender) estimateGasLimit(to *common.Address, data []byte, sidecar *type gasLimitWithoutAccessList, err := s.client.EstimateGas(s.ctx, msg) if err != nil { - log.Error("estimateGasLimit EstimateGas failure without access list", "error", err) + log.Error("estimateGasLimit EstimateGas failure without access list", "error", err, "msg", fmt.Sprintf("%+v", msg)) return 0, nil, err } From 2880bd500bd4b6cca40eb4c2718cf7a968f9963f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 20:43:02 +0100 Subject: [PATCH 02/20] add more logs --- rollup/internal/controller/sender/estimategas.go | 1 + rollup/internal/controller/sender/sender.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/rollup/internal/controller/sender/estimategas.go b/rollup/internal/controller/sender/estimategas.go index d9c7ed802e..36a9d93656 100644 --- a/rollup/internal/controller/sender/estimategas.go +++ b/rollup/internal/controller/sender/estimategas.go @@ -82,6 +82,7 @@ func (s *Sender) estimateBlobGas(to *common.Address, data []byte, sidecar *types gasFeeCap := getGasFeeCap(new(big.Int).SetUint64(baseFee), gasTipCap) blobGasFeeCap := getBlobGasFeeCap(new(big.Int).SetUint64(blobBaseFee)) + log.Warn("estimateBlobGas", "blobBaseFee", blobBaseFee, "blobGasFeeCap", blobGasFeeCap.String()) gasLimit, accessList, err := s.estimateGasLimit(to, data, sidecar, nil, gasTipCap, gasFeeCap, blobGasFeeCap) if err != nil { log.Error("estimateBlobGas estimateGasLimit failure", diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index cf23436a31..4b3a7a2e96 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -834,14 +834,18 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont return 0, 0, 0, 0, fmt.Errorf("failed to get header by number, err: %w", err) } + log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "number", header.Number.Uint64()) + var baseFee uint64 if header.BaseFee != nil { baseFee = header.BaseFee.Uint64() + log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "baseFee", header.BaseFee.String(), "baseFeeUint64", baseFee) } var blobBaseFee uint64 if excess := header.ExcessBlobGas; excess != nil { blobBaseFee = misc.CalcBlobFee(*excess).Uint64() + log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "blobBaseFee", misc.CalcBlobFee(*excess).String(), "blobBaseFeeUint64", blobBaseFee) } // header.Number.Uint64() returns the pendingBlockNumber, so we minus 1 to get the latestBlockNumber. return header.Number.Uint64() - 1, header.Time, baseFee, blobBaseFee, nil From 93604d944e0ac0347959c7604d193190c86d401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 21:12:04 +0100 Subject: [PATCH 03/20] query from L1 RPC --- rollup/internal/controller/sender/sender.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 4b3a7a2e96..d4ae235972 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -10,10 +10,10 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/holiman/uint256" "github.com/prometheus/client_golang/prometheus" "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/consensus/misc" gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto/kzg4844" "github.com/scroll-tech/go-ethereum/ethclient" @@ -67,7 +67,8 @@ type FeeData struct { // Sender Transaction sender to send transaction to l1/l2 type Sender struct { config *config.SenderConfig - gethClient *gethclient.Client + rpcClient *rpc.Client // Raw RPC Client + gethClient *gethclient.Client // Client to use for CreateAccessList client *ethclient.Client // The client to retrieve on chain data (read-only) writeClients []*ethclient.Client // The clients to send transactions to (write operations) transactionSigner *TransactionSigner @@ -141,6 +142,7 @@ func NewSender(ctx context.Context, config *config.SenderConfig, signerConfig *c sender := &Sender{ ctx: ctx, config: config, + rpcClient: rpcClient, gethClient: gethclient.New(rpcClient), client: client, writeClients: writeClients, @@ -842,11 +844,17 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "baseFee", header.BaseFee.String(), "baseFeeUint64", baseFee) } - var blobBaseFee uint64 - if excess := header.ExcessBlobGas; excess != nil { - blobBaseFee = misc.CalcBlobFee(*excess).Uint64() - log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "blobBaseFee", misc.CalcBlobFee(*excess).String(), "blobBaseFeeUint64", blobBaseFee) + // Leave it up to the L1 node to return the correct blob base fee. + // Previously we would compute it locally using `CalcBlobFee`, but + // that needs to be in sync with the L1 node's configuration. + var hex hexutil.Big + if err := s.rpcClient.CallContext(ctx, &hex, "eth_blobBaseFee"); err != nil { + return 0, 0, 0, 0, fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) } + blobBaseFee := hex.ToInt().Uint64() + + log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "blobBaseFeeUint64", blobBaseFee) + // header.Number.Uint64() returns the pendingBlockNumber, so we minus 1 to get the latestBlockNumber. return header.Number.Uint64() - 1, header.Time, baseFee, blobBaseFee, nil } From 8e9057adc65d76ab764246d1883d625368f3c8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 21:33:38 +0100 Subject: [PATCH 04/20] go mod tidy --- rollup/go.mod | 3 ++- rollup/go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rollup/go.mod b/rollup/go.mod index 6229f23a3f..d14eaa9cf3 100644 --- a/rollup/go.mod +++ b/rollup/go.mod @@ -10,6 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0 github.com/consensys/gnark-crypto v0.16.0 github.com/crate-crypto/go-kzg-4844 v1.1.0 + github.com/ethereum/go-ethereum v1.10.26 github.com/gin-gonic/gin v1.9.1 github.com/go-resty/resty/v2 v2.7.0 github.com/holiman/uint256 v1.3.2 @@ -51,7 +52,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea // indirect + github.com/deckarep/golang-set v1.8.0 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/fjl/memsize v0.0.2 // indirect diff --git a/rollup/go.sum b/rollup/go.sum index b9b5c43c05..f23e89ef86 100644 --- a/rollup/go.sum +++ b/rollup/go.sum @@ -88,13 +88,15 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= +github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= From c178e2c112d31f0ef61177f1c17813d02703a7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 21:34:44 +0100 Subject: [PATCH 05/20] clean up --- rollup/internal/controller/sender/estimategas.go | 1 - rollup/internal/controller/sender/sender.go | 5 ----- 2 files changed, 6 deletions(-) diff --git a/rollup/internal/controller/sender/estimategas.go b/rollup/internal/controller/sender/estimategas.go index 36a9d93656..d9c7ed802e 100644 --- a/rollup/internal/controller/sender/estimategas.go +++ b/rollup/internal/controller/sender/estimategas.go @@ -82,7 +82,6 @@ func (s *Sender) estimateBlobGas(to *common.Address, data []byte, sidecar *types gasFeeCap := getGasFeeCap(new(big.Int).SetUint64(baseFee), gasTipCap) blobGasFeeCap := getBlobGasFeeCap(new(big.Int).SetUint64(blobBaseFee)) - log.Warn("estimateBlobGas", "blobBaseFee", blobBaseFee, "blobGasFeeCap", blobGasFeeCap.String()) gasLimit, accessList, err := s.estimateGasLimit(to, data, sidecar, nil, gasTipCap, gasFeeCap, blobGasFeeCap) if err != nil { log.Error("estimateBlobGas estimateGasLimit failure", diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index d4ae235972..8c754cee6b 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -836,12 +836,9 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont return 0, 0, 0, 0, fmt.Errorf("failed to get header by number, err: %w", err) } - log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "number", header.Number.Uint64()) - var baseFee uint64 if header.BaseFee != nil { baseFee = header.BaseFee.Uint64() - log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "baseFee", header.BaseFee.String(), "baseFeeUint64", baseFee) } // Leave it up to the L1 node to return the correct blob base fee. @@ -853,8 +850,6 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont } blobBaseFee := hex.ToInt().Uint64() - log.Warn("getBlockNumberAndTimestampAndBaseFeeAndBlobFee", "blobBaseFeeUint64", blobBaseFee) - // header.Number.Uint64() returns the pendingBlockNumber, so we minus 1 to get the latestBlockNumber. return header.Number.Uint64() - 1, header.Time, baseFee, blobBaseFee, nil } From 692fd0f282e9ae8b5c0ec6b6ed496a2078dea065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 26 Nov 2025 21:52:24 +0100 Subject: [PATCH 06/20] fix gas-oracle --- common/testcontainers/testcontainers.go | 16 ++++++++----- common/testcontainers/testcontainers_test.go | 4 ++-- rollup/cmd/gas_oracle/app/app.go | 7 +++--- .../controller/relayer/relayer_test.go | 2 +- rollup/internal/controller/sender/sender.go | 4 +++- .../internal/controller/sender/sender_test.go | 2 +- .../internal/controller/watcher/l1_watcher.go | 24 +++++++++++++------ .../controller/watcher/l1_watcher_test.go | 4 ++-- rollup/tests/bridge_test.go | 11 +++++---- rollup/tests/gas_oracle_test.go | 4 ++-- tests/integration-test/contracts_test.go | 4 ++-- tests/integration-test/integration_test.go | 2 +- 12 files changed, 52 insertions(+), 32 deletions(-) diff --git a/common/testcontainers/testcontainers.go b/common/testcontainers/testcontainers.go index fa93b1385c..4ec4040912 100644 --- a/common/testcontainers/testcontainers.go +++ b/common/testcontainers/testcontainers.go @@ -168,12 +168,16 @@ func (t *TestcontainerApps) GetPoSL1EndPoint() (string, error) { } // GetPoSL1Client returns a ethclient by dialing running PoS L1 client -func (t *TestcontainerApps) GetPoSL1Client() (*ethclient.Client, error) { +func (t *TestcontainerApps) GetPoSL1Client() (*rpc.Client, *ethclient.Client, error) { endpoint, err := t.GetPoSL1EndPoint() if err != nil { - return nil, err + return nil, nil, err + } + rpcClient, err := rpc.Dial(endpoint) + if err != nil { + return nil, nil, fmt.Errorf("failed to dial L1 rpc endpoint, endpoint: %s, err: %w", endpoint, err) } - return ethclient.Dial(endpoint) + return rpcClient, ethclient.NewClient(rpcClient), nil } // GetDBEndPoint returns the endpoint of the running postgres container @@ -220,13 +224,13 @@ func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) { } // GetL2GethClient returns a ethclient by dialing running L2Geth -func (t *TestcontainerApps) GetL2GethClient() (*ethclient.Client, error) { +func (t *TestcontainerApps) GetL2GethClient() (*rpc.Client, *ethclient.Client, error) { rpcCli, err := t.GetL2Client() if err != nil { - return nil, err + return nil, nil, err } - return ethclient.NewClient(rpcCli), nil + return rpcCli, ethclient.NewClient(rpcCli), nil } // GetL2GethClient returns a rpc client by dialing running L2Geth diff --git a/common/testcontainers/testcontainers_test.go b/common/testcontainers/testcontainers_test.go index 52271fb1cf..0bdc2181d6 100644 --- a/common/testcontainers/testcontainers_test.go +++ b/common/testcontainers/testcontainers_test.go @@ -32,7 +32,7 @@ func TestNewTestcontainerApps(t *testing.T) { endpoint, err = testApps.GetL2GethEndPoint() assert.NoError(t, err) assert.NotEmpty(t, endpoint) - ethclient, err = testApps.GetL2GethClient() + _, ethclient, err = testApps.GetL2GethClient() assert.NoError(t, err) assert.NotNil(t, ethclient) @@ -40,7 +40,7 @@ func TestNewTestcontainerApps(t *testing.T) { endpoint, err = testApps.GetPoSL1EndPoint() assert.NoError(t, err) assert.NotEmpty(t, endpoint) - ethclient, err = testApps.GetPoSL1Client() + _, ethclient, err = testApps.GetPoSL1Client() assert.NoError(t, err) assert.NotNil(t, ethclient) diff --git a/rollup/cmd/gas_oracle/app/app.go b/rollup/cmd/gas_oracle/app/app.go index ec3a9738ae..3c14826712 100644 --- a/rollup/cmd/gas_oracle/app/app.go +++ b/rollup/cmd/gas_oracle/app/app.go @@ -66,12 +66,13 @@ func action(ctx *cli.Context) error { registry := prometheus.DefaultRegisterer observability.Server(ctx, db) - l1client, err := ethclient.Dial(cfg.L1Config.Endpoint) + l1RpcClient, err := rpc.Dial(cfg.L1Config.Endpoint) if err != nil { - log.Crit("failed to connect l1 geth", "config file", cfgFile, "error", err) + log.Crit("failed to dial raw RPC client to L1 endpoint", "endpoint", cfg.L1Config.Endpoint, "error", err) } + l1client := ethclient.NewClient(l1RpcClient) - l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1client, cfg.L1Config.StartHeight, db, registry) + l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1RpcClient, cfg.L1Config.StartHeight, db, registry) l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig, relayer.ServiceTypeL1GasOracle, registry) if err != nil { diff --git a/rollup/internal/controller/relayer/relayer_test.go b/rollup/internal/controller/relayer/relayer_test.go index 322a259aed..53118c75cc 100644 --- a/rollup/internal/controller/relayer/relayer_test.go +++ b/rollup/internal/controller/relayer/relayer_test.go @@ -74,7 +74,7 @@ func setupEnv(t *testing.T) { cfg.L2Config.RelayerConfig.ChainMonitor.BaseURL = "http://localhost:" + svrPort // Create l2geth client. - l2Cli, err = testApps.GetL2GethClient() + _, l2Cli, err = testApps.GetL2GethClient() assert.NoError(t, err) templateBlockTrace1, err := os.ReadFile("../../../testdata/blockTrace_02.json") diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 8c754cee6b..8582e64e23 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -67,7 +67,7 @@ type FeeData struct { // Sender Transaction sender to send transaction to l1/l2 type Sender struct { config *config.SenderConfig - rpcClient *rpc.Client // Raw RPC Client + rpcClient *rpc.Client // Raw RPC client gethClient *gethclient.Client // Client to use for CreateAccessList client *ethclient.Client // The client to retrieve on chain data (read-only) writeClients []*ethclient.Client // The clients to send transactions to (write operations) @@ -844,6 +844,8 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont // Leave it up to the L1 node to return the correct blob base fee. // Previously we would compute it locally using `CalcBlobFee`, but // that needs to be in sync with the L1 node's configuration. + // Note: The fetched blob base fee might not correspond to the block + // that we fetched in the previous step, but this is acceptable. var hex hexutil.Big if err := s.rpcClient.CallContext(ctx, &hex, "eth_blobBaseFee"); err != nil { return 0, 0, 0, 0, fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) diff --git a/rollup/internal/controller/sender/sender_test.go b/rollup/internal/controller/sender/sender_test.go index 3417d7e410..4e73ab253e 100644 --- a/rollup/internal/controller/sender/sender_test.go +++ b/rollup/internal/controller/sender/sender_test.go @@ -94,7 +94,7 @@ func setupEnv(t *testing.T) { assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) - l1Client, err := testApps.GetPoSL1Client() + _, l1Client, err := testApps.GetPoSL1Client() assert.NoError(t, err) chainID, err := l1Client.ChainID(context.Background()) diff --git a/rollup/internal/controller/watcher/l1_watcher.go b/rollup/internal/controller/watcher/l1_watcher.go index 1b67089a3a..510c362494 100644 --- a/rollup/internal/controller/watcher/l1_watcher.go +++ b/rollup/internal/controller/watcher/l1_watcher.go @@ -3,13 +3,15 @@ package watcher import ( "context" "errors" + "fmt" "math/big" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prometheus/client_golang/prometheus" - "github.com/scroll-tech/go-ethereum/consensus/misc" gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethclient" "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rpc" "gorm.io/gorm" "scroll-tech/common/types" @@ -20,7 +22,8 @@ import ( // L1WatcherClient will listen for smart contract events from Eth L1. type L1WatcherClient struct { ctx context.Context - client *ethclient.Client + rpcClient *rpc.Client // Raw RPC client + client *ethclient.Client // Go SDK RPC client l1BlockOrm *orm.L1Block // The height of the block that the watcher has retrieved header rlp @@ -30,7 +33,7 @@ type L1WatcherClient struct { } // NewL1WatcherClient returns a new instance of L1WatcherClient. -func NewL1WatcherClient(ctx context.Context, client *ethclient.Client, startHeight uint64, db *gorm.DB, reg prometheus.Registerer) *L1WatcherClient { +func NewL1WatcherClient(ctx context.Context, rpcClient *rpc.Client, startHeight uint64, db *gorm.DB, reg prometheus.Registerer) *L1WatcherClient { l1BlockOrm := orm.NewL1Block(db) savedL1BlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight(ctx) if err != nil { @@ -43,7 +46,8 @@ func NewL1WatcherClient(ctx context.Context, client *ethclient.Client, startHeig return &L1WatcherClient{ ctx: ctx, - client: client, + rpcClient: rpcClient, + client: ethclient.NewClient(rpcClient), l1BlockOrm: l1BlockOrm, processedBlockHeight: savedL1BlockHeight, @@ -78,10 +82,16 @@ func (w *L1WatcherClient) FetchBlockHeader(blockHeight uint64) error { baseFee = block.BaseFee.Uint64() } - var blobBaseFee uint64 - if excess := block.ExcessBlobGas; excess != nil { - blobBaseFee = misc.CalcBlobFee(*excess).Uint64() + // Leave it up to the L1 node to return the correct blob base fee. + // Previously we would compute it locally using `CalcBlobFee`, but + // that needs to be in sync with the L1 node's configuration. + // Note: The fetched blob base fee might not correspond to the block + // that we fetched in the previous step, but this is acceptable. + var hex hexutil.Big + if err := w.rpcClient.CallContext(w.ctx, &hex, "eth_blobBaseFee"); err != nil { + return fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) } + blobBaseFee := hex.ToInt().Uint64() l1Block := orm.L1Block{ Number: blockHeight, diff --git a/rollup/internal/controller/watcher/l1_watcher_test.go b/rollup/internal/controller/watcher/l1_watcher_test.go index 3183b4e346..b530692b1f 100644 --- a/rollup/internal/controller/watcher/l1_watcher_test.go +++ b/rollup/internal/controller/watcher/l1_watcher_test.go @@ -21,10 +21,10 @@ import ( func setupL1Watcher(t *testing.T) (*L1WatcherClient, *gorm.DB) { db := setupDB(t) - client, err := testApps.GetPoSL1Client() + rawClient, _, err := testApps.GetPoSL1Client() assert.NoError(t, err) l1Cfg := cfg.L1Config - watcher := NewL1WatcherClient(context.Background(), client, l1Cfg.StartHeight, db, nil) + watcher := NewL1WatcherClient(context.Background(), rawClient, l1Cfg.StartHeight, db, nil) return watcher, db } diff --git a/rollup/tests/bridge_test.go b/rollup/tests/bridge_test.go index 837e1a054d..4bb37de1a7 100644 --- a/rollup/tests/bridge_test.go +++ b/rollup/tests/bridge_test.go @@ -19,6 +19,7 @@ import ( "github.com/scroll-tech/go-ethereum/crypto" "github.com/scroll-tech/go-ethereum/ethclient" "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rpc" "github.com/stretchr/testify/assert" "gorm.io/gorm" @@ -37,8 +38,10 @@ var ( rollupApp *bcmd.MockApp // clients - l1Client *ethclient.Client - l2Client *ethclient.Client + l1RawClient *rpc.Client + l1Client *ethclient.Client + l2RawClient *rpc.Client + l2Client *ethclient.Client l1Auth *bind.TransactOpts l2Auth *bind.TransactOpts @@ -91,9 +94,9 @@ func setupEnv(t *testing.T) { assert.NoError(t, testApps.StartPoSL1Container()) rollupApp = bcmd.NewRollupApp(testApps, "../conf/config.json") - l1Client, err = testApps.GetPoSL1Client() + l1RawClient, l1Client, err = testApps.GetPoSL1Client() assert.NoError(t, err) - l2Client, err = testApps.GetL2GethClient() + l2RawClient, l2Client, err = testApps.GetL2GethClient() assert.NoError(t, err) l1GethChainID, err = l1Client.ChainID(context.Background()) assert.NoError(t, err) diff --git a/rollup/tests/gas_oracle_test.go b/rollup/tests/gas_oracle_test.go index 1cace3bd11..e069b26baf 100644 --- a/rollup/tests/gas_oracle_test.go +++ b/rollup/tests/gas_oracle_test.go @@ -36,7 +36,7 @@ func testImportL1GasPrice(t *testing.T) { // Create L1Watcher startHeight, err := l1Client.BlockNumber(context.Background()) assert.NoError(t, err) - l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, startHeight-1, db, nil) + l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1RawClient, startHeight-1, db, nil) // fetch new blocks number, err := l1Client.BlockNumber(context.Background()) @@ -110,7 +110,7 @@ func testImportDefaultL1GasPriceDueToL1GasPriceSpike(t *testing.T) { // Create L1Watcher startHeight, err := l1Client.BlockNumber(context.Background()) assert.NoError(t, err) - l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, startHeight-2, db, nil) + l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1RawClient, startHeight-2, db, nil) // fetch new blocks number, err := l1Client.BlockNumber(context.Background()) diff --git a/tests/integration-test/contracts_test.go b/tests/integration-test/contracts_test.go index 4483f394bf..01c47e902f 100644 --- a/tests/integration-test/contracts_test.go +++ b/tests/integration-test/contracts_test.go @@ -24,7 +24,7 @@ func testERC20(t *testing.T) { assert.NoError(t, testApps.StartL2GethContainer()) time.Sleep(time.Second * 3) - l2Cli, err := testApps.GetL2GethClient() + _, l2Cli, err := testApps.GetL2GethClient() assert.Nil(t, err) token, err := erc20.NewERC20Mock(erc20Address, l2Cli) @@ -63,7 +63,7 @@ func testERC20(t *testing.T) { func testGreeter(t *testing.T) { assert.NoError(t, testApps.StartL2GethContainer()) - l2Cli, err := testApps.GetL2GethClient() + _, l2Cli, err := testApps.GetL2GethClient() assert.Nil(t, err) chainID, err := l2Cli.ChainID(context.Background()) diff --git a/tests/integration-test/integration_test.go b/tests/integration-test/integration_test.go index 28527e0247..ec8548970d 100644 --- a/tests/integration-test/integration_test.go +++ b/tests/integration-test/integration_test.go @@ -79,7 +79,7 @@ func testCoordinatorProverInteraction(t *testing.T) { l2BlockOrm := orm.NewL2Block(db) // Connect to l2geth client - l2Client, err := testApps.GetL2GethClient() + _, l2Client, err := testApps.GetL2GethClient() if err != nil { log.Fatalf("Failed to connect to the l2geth client: %v", err) } From 5976ba7d0f4eab3bf9d956497324e8ad7e5c2371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 08:37:50 +0100 Subject: [PATCH 07/20] update dependency --- rollup/internal/controller/sender/sender.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 8582e64e23..ab1f5980f8 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -10,10 +10,10 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/holiman/uint256" "github.com/prometheus/client_golang/prometheus" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto/kzg4844" "github.com/scroll-tech/go-ethereum/ethclient" From eab42296cf5f847952e0ab849ee4487016583a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 08:42:18 +0100 Subject: [PATCH 08/20] cleanup --- common/testcontainers/testcontainers.go | 1 - rollup/tests/bridge_test.go | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/testcontainers/testcontainers.go b/common/testcontainers/testcontainers.go index 4ec4040912..23c66cf712 100644 --- a/common/testcontainers/testcontainers.go +++ b/common/testcontainers/testcontainers.go @@ -225,7 +225,6 @@ func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) { // GetL2GethClient returns a ethclient by dialing running L2Geth func (t *TestcontainerApps) GetL2GethClient() (*rpc.Client, *ethclient.Client, error) { - rpcCli, err := t.GetL2Client() if err != nil { return nil, nil, err diff --git a/rollup/tests/bridge_test.go b/rollup/tests/bridge_test.go index 4bb37de1a7..daa87827cf 100644 --- a/rollup/tests/bridge_test.go +++ b/rollup/tests/bridge_test.go @@ -40,7 +40,6 @@ var ( // clients l1RawClient *rpc.Client l1Client *ethclient.Client - l2RawClient *rpc.Client l2Client *ethclient.Client l1Auth *bind.TransactOpts @@ -96,7 +95,7 @@ func setupEnv(t *testing.T) { l1RawClient, l1Client, err = testApps.GetPoSL1Client() assert.NoError(t, err) - l2RawClient, l2Client, err = testApps.GetL2GethClient() + _, l2Client, err = testApps.GetL2GethClient() assert.NoError(t, err) l1GethChainID, err = l1Client.ChainID(context.Background()) assert.NoError(t, err) From 0894618814c93b20865452b2d9d2e7a967c4c23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 16:01:39 +0100 Subject: [PATCH 09/20] comment fixes --- common/testcontainers/testcontainers.go | 4 ++-- rollup/internal/controller/sender/sender.go | 4 ++-- rollup/internal/controller/watcher/l1_watcher.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/testcontainers/testcontainers.go b/common/testcontainers/testcontainers.go index 23c66cf712..e2c224b251 100644 --- a/common/testcontainers/testcontainers.go +++ b/common/testcontainers/testcontainers.go @@ -167,7 +167,7 @@ func (t *TestcontainerApps) GetPoSL1EndPoint() (string, error) { return contrainer.PortEndpoint(context.Background(), "8545/tcp", "http") } -// GetPoSL1Client returns a ethclient by dialing running PoS L1 client +// GetPoSL1Client returns a raw rpc client and an ethclient by dialing the L1 node func (t *TestcontainerApps) GetPoSL1Client() (*rpc.Client, *ethclient.Client, error) { endpoint, err := t.GetPoSL1EndPoint() if err != nil { @@ -223,7 +223,7 @@ func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) { return database.InitDB(dbCfg) } -// GetL2GethClient returns a ethclient by dialing running L2Geth +// GetL2GethClient returns a raw rpc client and an ethclient by dialing the L2 node func (t *TestcontainerApps) GetL2GethClient() (*rpc.Client, *ethclient.Client, error) { rpcCli, err := t.GetL2Client() if err != nil { diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index ab1f5980f8..282c8be582 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -841,9 +841,9 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont baseFee = header.BaseFee.Uint64() } - // Leave it up to the L1 node to return the correct blob base fee. + // Leave it up to the L1 node to compute the correct blob base fee. // Previously we would compute it locally using `CalcBlobFee`, but - // that needs to be in sync with the L1 node's configuration. + // that approach requires syncing any future L1 configuration changes. // Note: The fetched blob base fee might not correspond to the block // that we fetched in the previous step, but this is acceptable. var hex hexutil.Big diff --git a/rollup/internal/controller/watcher/l1_watcher.go b/rollup/internal/controller/watcher/l1_watcher.go index 510c362494..ed2b69e665 100644 --- a/rollup/internal/controller/watcher/l1_watcher.go +++ b/rollup/internal/controller/watcher/l1_watcher.go @@ -6,8 +6,8 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prometheus/client_golang/prometheus" + "github.com/scroll-tech/go-ethereum/common/hexutil" gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethclient" "github.com/scroll-tech/go-ethereum/log" @@ -82,9 +82,9 @@ func (w *L1WatcherClient) FetchBlockHeader(blockHeight uint64) error { baseFee = block.BaseFee.Uint64() } - // Leave it up to the L1 node to return the correct blob base fee. + // Leave it up to the L1 node to compute the correct blob base fee. // Previously we would compute it locally using `CalcBlobFee`, but - // that needs to be in sync with the L1 node's configuration. + // that approach requires syncing any future L1 configuration changes. // Note: The fetched blob base fee might not correspond to the block // that we fetched in the previous step, but this is acceptable. var hex hexutil.Big From a20959c8f0c9b2258a628386706bffdf8f9bbf6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 16:15:10 +0100 Subject: [PATCH 10/20] add limits --- rollup/internal/config/relayer.go | 4 ++++ rollup/internal/controller/relayer/l1_relayer.go | 11 +++++++++++ .../controller/relayer/l1_relayer_metrics.go | 15 ++++++++++----- rollup/internal/controller/sender/sender.go | 7 ++++--- rollup/internal/controller/watcher/l1_watcher.go | 7 ++++--- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/rollup/internal/config/relayer.go b/rollup/internal/config/relayer.go index d82fd861a7..c993b4e3cf 100644 --- a/rollup/internal/config/relayer.go +++ b/rollup/internal/config/relayer.go @@ -109,6 +109,10 @@ type GasOracleConfig struct { L1BaseFeeDefault uint64 `json:"l1_base_fee_default"` L1BlobBaseFeeDefault uint64 `json:"l1_blob_base_fee_default"` + // Limit values for gas oracle updates + L1BaseFeeLimit uint64 `json:"l1_base_fee_limit"` + L1BlobBaseFeeLimit uint64 `json:"l1_blob_base_fee_limit"` + // L1BlobBaseFeeThreshold the threshold of L1 blob base fee to enter the default gas price mode L1BlobBaseFeeThreshold uint64 `json:"l1_blob_base_fee_threshold"` } diff --git a/rollup/internal/controller/relayer/l1_relayer.go b/rollup/internal/controller/relayer/l1_relayer.go index d9f0835d52..da6a19f292 100644 --- a/rollup/internal/controller/relayer/l1_relayer.go +++ b/rollup/internal/controller/relayer/l1_relayer.go @@ -173,6 +173,17 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { } else if err != nil { return } + // set limit + if baseFee > r.cfg.GasOracleConfig.L1BaseFeeLimit { + log.Error("L1 base fee exceed max limit, set to max limit", "baseFee", baseFee, "maxLimit", r.cfg.GasOracleConfig.L1BaseFeeLimit) + r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() + baseFee = r.cfg.GasOracleConfig.L1BaseFeeLimit + } + if blobBaseFee > r.cfg.GasOracleConfig.L1BlobBaseFeeLimit { + log.Error("L1 blob base fee exceed max limit, set to max limit", "blobBaseFee", blobBaseFee, "maxLimit", r.cfg.GasOracleConfig.L1BlobBaseFeeLimit) + r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() + blobBaseFee = r.cfg.GasOracleConfig.L1BlobBaseFeeLimit + } data, err := r.l1GasOracleABI.Pack("setL1BaseFeeAndBlobBaseFee", new(big.Int).SetUint64(baseFee), new(big.Int).SetUint64(blobBaseFee)) if err != nil { log.Error("Failed to pack setL1BaseFeeAndBlobBaseFee", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "err", err) diff --git a/rollup/internal/controller/relayer/l1_relayer_metrics.go b/rollup/internal/controller/relayer/l1_relayer_metrics.go index 433c373964..0efb6264ee 100644 --- a/rollup/internal/controller/relayer/l1_relayer_metrics.go +++ b/rollup/internal/controller/relayer/l1_relayer_metrics.go @@ -8,11 +8,12 @@ import ( ) type l1RelayerMetrics struct { - rollupL1RelayerGasPriceOraclerRunTotal prometheus.Counter - rollupL1RelayerLatestBaseFee prometheus.Gauge - rollupL1RelayerLatestBlobBaseFee prometheus.Gauge - rollupL1UpdateGasOracleConfirmedTotal prometheus.Counter - rollupL1UpdateGasOracleConfirmedFailedTotal prometheus.Counter + rollupL1RelayerGasPriceOraclerRunTotal prometheus.Counter + rollupL1RelayerLatestBaseFee prometheus.Gauge + rollupL1RelayerLatestBlobBaseFee prometheus.Gauge + rollupL1UpdateGasOracleConfirmedTotal prometheus.Counter + rollupL1UpdateGasOracleConfirmedFailedTotal prometheus.Counter + rollupL1RelayerGasPriceOracleFeeOverLimitTotal prometheus.Counter } var ( @@ -43,6 +44,10 @@ func initL1RelayerMetrics(reg prometheus.Registerer) *l1RelayerMetrics { Name: "rollup_layer1_update_gas_oracle_confirmed_failed_total", Help: "The total number of updating layer1 gas oracle confirmed failed", }), + rollupL1RelayerGasPriceOracleFeeOverLimitTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{ + Name: "rollup_layer1_gas_price_oracle_fee_over_limit_total", + Help: "The total number of layer1 gas price oracle fee over limit", + }), } }) return l1RelayerMetric diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 282c8be582..4f6e959254 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -846,11 +846,12 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont // that approach requires syncing any future L1 configuration changes. // Note: The fetched blob base fee might not correspond to the block // that we fetched in the previous step, but this is acceptable. - var hex hexutil.Big - if err := s.rpcClient.CallContext(ctx, &hex, "eth_blobBaseFee"); err != nil { + var blobBaseFeeHex hexutil.Big + if err := s.rpcClient.CallContext(ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { return 0, 0, 0, 0, fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) } - blobBaseFee := hex.ToInt().Uint64() + // A correct L1 node could not return a value that overflows uint64 + blobBaseFee := blobBaseFeeHex.ToInt().Uint64() // header.Number.Uint64() returns the pendingBlockNumber, so we minus 1 to get the latestBlockNumber. return header.Number.Uint64() - 1, header.Time, baseFee, blobBaseFee, nil diff --git a/rollup/internal/controller/watcher/l1_watcher.go b/rollup/internal/controller/watcher/l1_watcher.go index ed2b69e665..7bd7dc8c76 100644 --- a/rollup/internal/controller/watcher/l1_watcher.go +++ b/rollup/internal/controller/watcher/l1_watcher.go @@ -87,11 +87,12 @@ func (w *L1WatcherClient) FetchBlockHeader(blockHeight uint64) error { // that approach requires syncing any future L1 configuration changes. // Note: The fetched blob base fee might not correspond to the block // that we fetched in the previous step, but this is acceptable. - var hex hexutil.Big - if err := w.rpcClient.CallContext(w.ctx, &hex, "eth_blobBaseFee"); err != nil { + var blobBaseFeeHex hexutil.Big + if err := w.rpcClient.CallContext(w.ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { return fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) } - blobBaseFee := hex.ToInt().Uint64() + // A correct L1 node could not return a value that overflows uint64 + blobBaseFee := blobBaseFeeHex.ToInt().Uint64() l1Block := orm.L1Block{ Number: blockHeight, From e4cf623ae1a17ca5828fe6dad034ed3d79f56eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 19:40:38 +0100 Subject: [PATCH 11/20] go mod tidy --- rollup/go.mod | 1 - rollup/go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/rollup/go.mod b/rollup/go.mod index d14eaa9cf3..d20c991f33 100644 --- a/rollup/go.mod +++ b/rollup/go.mod @@ -10,7 +10,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0 github.com/consensys/gnark-crypto v0.16.0 github.com/crate-crypto/go-kzg-4844 v1.1.0 - github.com/ethereum/go-ethereum v1.10.26 github.com/gin-gonic/gin v1.9.1 github.com/go-resty/resty/v2 v2.7.0 github.com/holiman/uint256 v1.3.2 diff --git a/rollup/go.sum b/rollup/go.sum index f23e89ef86..18ebbc4e66 100644 --- a/rollup/go.sum +++ b/rollup/go.sum @@ -95,8 +95,6 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= -github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= From 299fef56726313ff492be22eb54f1c7fbfcf5293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 19:47:38 +0100 Subject: [PATCH 12/20] make changes backward compatible with tests --- rollup/internal/controller/sender/sender.go | 25 +++++++++++-------- .../internal/controller/watcher/l1_watcher.go | 23 +++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 4f6e959254..5b37473596 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -841,17 +841,20 @@ func (s *Sender) getBlockNumberAndTimestampAndBaseFeeAndBlobFee(ctx context.Cont baseFee = header.BaseFee.Uint64() } - // Leave it up to the L1 node to compute the correct blob base fee. - // Previously we would compute it locally using `CalcBlobFee`, but - // that approach requires syncing any future L1 configuration changes. - // Note: The fetched blob base fee might not correspond to the block - // that we fetched in the previous step, but this is acceptable. - var blobBaseFeeHex hexutil.Big - if err := s.rpcClient.CallContext(ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { - return 0, 0, 0, 0, fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) - } - // A correct L1 node could not return a value that overflows uint64 - blobBaseFee := blobBaseFeeHex.ToInt().Uint64() + var blobBaseFee uint64 + if excess := header.ExcessBlobGas; excess != nil { + // Leave it up to the L1 node to compute the correct blob base fee. + // Previously we would compute it locally using `CalcBlobFee`, but + // that approach requires syncing any future L1 configuration changes. + // Note: The fetched blob base fee might not correspond to the block + // that we fetched in the previous step, but this is acceptable. + var blobBaseFeeHex hexutil.Big + if err := s.rpcClient.CallContext(ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { + return 0, 0, 0, 0, fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) + } + // A correct L1 node could not return a value that overflows uint64 + blobBaseFee = blobBaseFeeHex.ToInt().Uint64() + } // header.Number.Uint64() returns the pendingBlockNumber, so we minus 1 to get the latestBlockNumber. return header.Number.Uint64() - 1, header.Time, baseFee, blobBaseFee, nil diff --git a/rollup/internal/controller/watcher/l1_watcher.go b/rollup/internal/controller/watcher/l1_watcher.go index 7bd7dc8c76..3a93589a73 100644 --- a/rollup/internal/controller/watcher/l1_watcher.go +++ b/rollup/internal/controller/watcher/l1_watcher.go @@ -82,17 +82,20 @@ func (w *L1WatcherClient) FetchBlockHeader(blockHeight uint64) error { baseFee = block.BaseFee.Uint64() } - // Leave it up to the L1 node to compute the correct blob base fee. - // Previously we would compute it locally using `CalcBlobFee`, but - // that approach requires syncing any future L1 configuration changes. - // Note: The fetched blob base fee might not correspond to the block - // that we fetched in the previous step, but this is acceptable. - var blobBaseFeeHex hexutil.Big - if err := w.rpcClient.CallContext(w.ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { - return fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) + var blobBaseFee uint64 + if excess := block.ExcessBlobGas; excess != nil { + // Leave it up to the L1 node to compute the correct blob base fee. + // Previously we would compute it locally using `CalcBlobFee`, but + // that approach requires syncing any future L1 configuration changes. + // Note: The fetched blob base fee might not correspond to the block + // that we fetched in the previous step, but this is acceptable. + var blobBaseFeeHex hexutil.Big + if err := w.rpcClient.CallContext(w.ctx, &blobBaseFeeHex, "eth_blobBaseFee"); err != nil { + return fmt.Errorf("failed to call eth_blobBaseFee, err: %w", err) + } + // A correct L1 node could not return a value that overflows uint64 + blobBaseFee = blobBaseFeeHex.ToInt().Uint64() } - // A correct L1 node could not return a value that overflows uint64 - blobBaseFee := blobBaseFeeHex.ToInt().Uint64() l1Block := orm.L1Block{ Number: blockHeight, From 7527e4dfb1a0f265fd2c470957a4a4c81db8508c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 19:52:17 +0100 Subject: [PATCH 13/20] consider limit not set scenario --- rollup/internal/config/relayer.go | 2 +- rollup/internal/controller/relayer/l1_relayer.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rollup/internal/config/relayer.go b/rollup/internal/config/relayer.go index c993b4e3cf..55f6393db9 100644 --- a/rollup/internal/config/relayer.go +++ b/rollup/internal/config/relayer.go @@ -109,7 +109,7 @@ type GasOracleConfig struct { L1BaseFeeDefault uint64 `json:"l1_base_fee_default"` L1BlobBaseFeeDefault uint64 `json:"l1_blob_base_fee_default"` - // Limit values for gas oracle updates + // Upper limit values for gas oracle updates L1BaseFeeLimit uint64 `json:"l1_base_fee_limit"` L1BlobBaseFeeLimit uint64 `json:"l1_blob_base_fee_limit"` diff --git a/rollup/internal/controller/relayer/l1_relayer.go b/rollup/internal/controller/relayer/l1_relayer.go index da6a19f292..581a1d752a 100644 --- a/rollup/internal/controller/relayer/l1_relayer.go +++ b/rollup/internal/controller/relayer/l1_relayer.go @@ -173,16 +173,17 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { } else if err != nil { return } - // set limit - if baseFee > r.cfg.GasOracleConfig.L1BaseFeeLimit { - log.Error("L1 base fee exceed max limit, set to max limit", "baseFee", baseFee, "maxLimit", r.cfg.GasOracleConfig.L1BaseFeeLimit) + // Cap base fee update at the configured upper limit + if limit := r.cfg.GasOracleConfig.L1BaseFeeLimit; limit > 0 && baseFee > limit { + log.Error("L1 base fee exceed max limit, set to max limit", "baseFee", baseFee, "maxLimit", limit) r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() - baseFee = r.cfg.GasOracleConfig.L1BaseFeeLimit + baseFee = limit } - if blobBaseFee > r.cfg.GasOracleConfig.L1BlobBaseFeeLimit { - log.Error("L1 blob base fee exceed max limit, set to max limit", "blobBaseFee", blobBaseFee, "maxLimit", r.cfg.GasOracleConfig.L1BlobBaseFeeLimit) + // Cap blob base fee update at the configured upper limit + if limit := r.cfg.GasOracleConfig.L1BlobBaseFeeLimit; limit > 0 && blobBaseFee > limit { + log.Error("L1 blob base fee exceed max limit, set to max limit", "blobBaseFee", blobBaseFee, "maxLimit", limit) r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() - blobBaseFee = r.cfg.GasOracleConfig.L1BlobBaseFeeLimit + blobBaseFee = limit } data, err := r.l1GasOracleABI.Pack("setL1BaseFeeAndBlobBaseFee", new(big.Int).SetUint64(baseFee), new(big.Int).SetUint64(blobBaseFee)) if err != nil { From 8f2f75c330986579298d760d9441b63c3be4485b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 20:33:28 +0100 Subject: [PATCH 14/20] upgrade testcontainers --- common/testcontainers/docker-compose.yml | 4 ++-- common/testcontainers/docker/l1geth/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/testcontainers/docker-compose.yml b/common/testcontainers/docker-compose.yml index 111e5d2aa3..f875edc901 100644 --- a/common/testcontainers/docker-compose.yml +++ b/common/testcontainers/docker-compose.yml @@ -34,7 +34,7 @@ services: # Sets up the genesis configuration for the go-ethereum client from a JSON file. geth-genesis: - image: "ethereum/client-go:v1.13.14" + image: "ethereum/client-go:v1.16.7" command: --datadir=/data/execution init /data/execution/genesis.json volumes: - data:/data @@ -80,7 +80,7 @@ services: # Runs the go-ethereum execution client with the specified, unlocked account and necessary # APIs to allow for proof-of-stake consensus via Prysm. geth: - image: "ethereum/client-go:v1.13.14" + image: "ethereum/client-go:v1.16.7" command: - --http - --http.api=eth,net,web3 diff --git a/common/testcontainers/docker/l1geth/Dockerfile b/common/testcontainers/docker/l1geth/Dockerfile index 886227fed0..91dd7eb63e 100644 --- a/common/testcontainers/docker/l1geth/Dockerfile +++ b/common/testcontainers/docker/l1geth/Dockerfile @@ -1,4 +1,4 @@ -FROM ethereum/client-go:v1.13.14 +FROM ethereum/client-go:v1.16.7 COPY password /l1geth/ COPY genesis.json /l1geth/ From 740bc6f2ff1569760dc92b4c19bab3a7305910e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 27 Nov 2025 20:46:28 +0100 Subject: [PATCH 15/20] use earliest geth version that supports eth_blobBaseFee --- common/testcontainers/docker-compose.yml | 4 ++-- common/testcontainers/docker/l1geth/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/testcontainers/docker-compose.yml b/common/testcontainers/docker-compose.yml index f875edc901..c7664782fc 100644 --- a/common/testcontainers/docker-compose.yml +++ b/common/testcontainers/docker-compose.yml @@ -34,7 +34,7 @@ services: # Sets up the genesis configuration for the go-ethereum client from a JSON file. geth-genesis: - image: "ethereum/client-go:v1.16.7" + image: "ethereum/client-go:v1.14.0" command: --datadir=/data/execution init /data/execution/genesis.json volumes: - data:/data @@ -80,7 +80,7 @@ services: # Runs the go-ethereum execution client with the specified, unlocked account and necessary # APIs to allow for proof-of-stake consensus via Prysm. geth: - image: "ethereum/client-go:v1.16.7" + image: "ethereum/client-go:v1.14.0" command: - --http - --http.api=eth,net,web3 diff --git a/common/testcontainers/docker/l1geth/Dockerfile b/common/testcontainers/docker/l1geth/Dockerfile index 91dd7eb63e..59fcb16c11 100644 --- a/common/testcontainers/docker/l1geth/Dockerfile +++ b/common/testcontainers/docker/l1geth/Dockerfile @@ -1,4 +1,4 @@ -FROM ethereum/client-go:v1.16.7 +FROM ethereum/client-go:v1.14.0 COPY password /l1geth/ COPY genesis.json /l1geth/ From d4948d58766cfb2a5fcce2a0b35e120edb006626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Fri, 28 Nov 2025 14:24:39 +0100 Subject: [PATCH 16/20] clean up tests --- common/testcontainers/testcontainers.go | 20 +++++++++---------- common/testcontainers/testcontainers_test.go | 10 ++++------ .../controller/relayer/relayer_test.go | 2 +- .../internal/controller/sender/sender_test.go | 4 +++- .../controller/watcher/l1_watcher_test.go | 4 ++-- rollup/tests/bridge_test.go | 5 +++-- tests/integration-test/contracts_test.go | 4 ++-- tests/integration-test/integration_test.go | 2 +- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/common/testcontainers/testcontainers.go b/common/testcontainers/testcontainers.go index e2c224b251..5f8e82b0c8 100644 --- a/common/testcontainers/testcontainers.go +++ b/common/testcontainers/testcontainers.go @@ -167,17 +167,17 @@ func (t *TestcontainerApps) GetPoSL1EndPoint() (string, error) { return contrainer.PortEndpoint(context.Background(), "8545/tcp", "http") } -// GetPoSL1Client returns a raw rpc client and an ethclient by dialing the L1 node -func (t *TestcontainerApps) GetPoSL1Client() (*rpc.Client, *ethclient.Client, error) { +// GetPoSL1Client returns a raw rpc client by dialing the L1 node +func (t *TestcontainerApps) GetPoSL1Client() (*rpc.Client, error) { endpoint, err := t.GetPoSL1EndPoint() if err != nil { - return nil, nil, err + return nil, err } - rpcClient, err := rpc.Dial(endpoint) + rawClient, err := rpc.Dial(endpoint) if err != nil { - return nil, nil, fmt.Errorf("failed to dial L1 rpc endpoint, endpoint: %s, err: %w", endpoint, err) + return nil, fmt.Errorf("failed to dial L1 rpc endpoint, endpoint: %s, err: %w", endpoint, err) } - return rpcClient, ethclient.NewClient(rpcClient), nil + return rawClient, nil } // GetDBEndPoint returns the endpoint of the running postgres container @@ -223,13 +223,13 @@ func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) { return database.InitDB(dbCfg) } -// GetL2GethClient returns a raw rpc client and an ethclient by dialing the L2 node -func (t *TestcontainerApps) GetL2GethClient() (*rpc.Client, *ethclient.Client, error) { +// GetL2GethClient returns a ethclient by dialing running L2Geth +func (t *TestcontainerApps) GetL2GethClient() (*ethclient.Client, error) { rpcCli, err := t.GetL2Client() if err != nil { - return nil, nil, err + return nil, err } - return rpcCli, ethclient.NewClient(rpcCli), nil + return ethclient.NewClient(rpcCli), nil } // GetL2GethClient returns a rpc client by dialing running L2Geth diff --git a/common/testcontainers/testcontainers_test.go b/common/testcontainers/testcontainers_test.go index 0bdc2181d6..a0d4fa77f6 100644 --- a/common/testcontainers/testcontainers_test.go +++ b/common/testcontainers/testcontainers_test.go @@ -3,7 +3,6 @@ package testcontainers import ( "testing" - "github.com/scroll-tech/go-ethereum/ethclient" "github.com/stretchr/testify/assert" "gorm.io/gorm" ) @@ -14,7 +13,6 @@ func TestNewTestcontainerApps(t *testing.T) { err error endpoint string gormDBclient *gorm.DB - ethclient *ethclient.Client ) testApps := NewTestcontainerApps() @@ -32,17 +30,17 @@ func TestNewTestcontainerApps(t *testing.T) { endpoint, err = testApps.GetL2GethEndPoint() assert.NoError(t, err) assert.NotEmpty(t, endpoint) - _, ethclient, err = testApps.GetL2GethClient() + l2RawClient, err := testApps.GetL2Client() assert.NoError(t, err) - assert.NotNil(t, ethclient) + assert.NotNil(t, l2RawClient) assert.NoError(t, testApps.StartPoSL1Container()) endpoint, err = testApps.GetPoSL1EndPoint() assert.NoError(t, err) assert.NotEmpty(t, endpoint) - _, ethclient, err = testApps.GetPoSL1Client() + l1RawClient, err := testApps.GetPoSL1Client() assert.NoError(t, err) - assert.NotNil(t, ethclient) + assert.NotNil(t, l1RawClient) assert.NoError(t, testApps.StartWeb3SignerContainer(1)) endpoint, err = testApps.GetWeb3SignerEndpoint() diff --git a/rollup/internal/controller/relayer/relayer_test.go b/rollup/internal/controller/relayer/relayer_test.go index 53118c75cc..322a259aed 100644 --- a/rollup/internal/controller/relayer/relayer_test.go +++ b/rollup/internal/controller/relayer/relayer_test.go @@ -74,7 +74,7 @@ func setupEnv(t *testing.T) { cfg.L2Config.RelayerConfig.ChainMonitor.BaseURL = "http://localhost:" + svrPort // Create l2geth client. - _, l2Cli, err = testApps.GetL2GethClient() + l2Cli, err = testApps.GetL2GethClient() assert.NoError(t, err) templateBlockTrace1, err := os.ReadFile("../../../testdata/blockTrace_02.json") diff --git a/rollup/internal/controller/sender/sender_test.go b/rollup/internal/controller/sender/sender_test.go index 4e73ab253e..5ad64e50c6 100644 --- a/rollup/internal/controller/sender/sender_test.go +++ b/rollup/internal/controller/sender/sender_test.go @@ -21,6 +21,7 @@ import ( gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto" "github.com/scroll-tech/go-ethereum/crypto/kzg4844" + "github.com/scroll-tech/go-ethereum/ethclient" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/rpc" "github.com/stretchr/testify/assert" @@ -94,8 +95,9 @@ func setupEnv(t *testing.T) { assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) - _, l1Client, err := testApps.GetPoSL1Client() + l1RawClient, err := testApps.GetPoSL1Client() assert.NoError(t, err) + l1Client := ethclient.NewClient(l1RawClient) chainID, err := l1Client.ChainID(context.Background()) assert.NoError(t, err) diff --git a/rollup/internal/controller/watcher/l1_watcher_test.go b/rollup/internal/controller/watcher/l1_watcher_test.go index b530692b1f..7b5d929ebf 100644 --- a/rollup/internal/controller/watcher/l1_watcher_test.go +++ b/rollup/internal/controller/watcher/l1_watcher_test.go @@ -21,10 +21,10 @@ import ( func setupL1Watcher(t *testing.T) (*L1WatcherClient, *gorm.DB) { db := setupDB(t) - rawClient, _, err := testApps.GetPoSL1Client() + l1RawClient, err := testApps.GetPoSL1Client() assert.NoError(t, err) l1Cfg := cfg.L1Config - watcher := NewL1WatcherClient(context.Background(), rawClient, l1Cfg.StartHeight, db, nil) + watcher := NewL1WatcherClient(context.Background(), l1RawClient, l1Cfg.StartHeight, db, nil) return watcher, db } diff --git a/rollup/tests/bridge_test.go b/rollup/tests/bridge_test.go index daa87827cf..ac21082465 100644 --- a/rollup/tests/bridge_test.go +++ b/rollup/tests/bridge_test.go @@ -93,9 +93,10 @@ func setupEnv(t *testing.T) { assert.NoError(t, testApps.StartPoSL1Container()) rollupApp = bcmd.NewRollupApp(testApps, "../conf/config.json") - l1RawClient, l1Client, err = testApps.GetPoSL1Client() + l1RawClient, err = testApps.GetPoSL1Client() + l1Client = ethclient.NewClient(l1RawClient) assert.NoError(t, err) - _, l2Client, err = testApps.GetL2GethClient() + l2Client, err = testApps.GetL2GethClient() assert.NoError(t, err) l1GethChainID, err = l1Client.ChainID(context.Background()) assert.NoError(t, err) diff --git a/tests/integration-test/contracts_test.go b/tests/integration-test/contracts_test.go index 01c47e902f..4483f394bf 100644 --- a/tests/integration-test/contracts_test.go +++ b/tests/integration-test/contracts_test.go @@ -24,7 +24,7 @@ func testERC20(t *testing.T) { assert.NoError(t, testApps.StartL2GethContainer()) time.Sleep(time.Second * 3) - _, l2Cli, err := testApps.GetL2GethClient() + l2Cli, err := testApps.GetL2GethClient() assert.Nil(t, err) token, err := erc20.NewERC20Mock(erc20Address, l2Cli) @@ -63,7 +63,7 @@ func testERC20(t *testing.T) { func testGreeter(t *testing.T) { assert.NoError(t, testApps.StartL2GethContainer()) - _, l2Cli, err := testApps.GetL2GethClient() + l2Cli, err := testApps.GetL2GethClient() assert.Nil(t, err) chainID, err := l2Cli.ChainID(context.Background()) diff --git a/tests/integration-test/integration_test.go b/tests/integration-test/integration_test.go index ec8548970d..28527e0247 100644 --- a/tests/integration-test/integration_test.go +++ b/tests/integration-test/integration_test.go @@ -79,7 +79,7 @@ func testCoordinatorProverInteraction(t *testing.T) { l2BlockOrm := orm.NewL2Block(db) // Connect to l2geth client - _, l2Client, err := testApps.GetL2GethClient() + l2Client, err := testApps.GetL2GethClient() if err != nil { log.Fatalf("Failed to connect to the l2geth client: %v", err) } From 9be56ff97d2ab90798a4296486073987dd8e0700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Fri, 28 Nov 2025 14:30:13 +0100 Subject: [PATCH 17/20] check invalid config --- rollup/cmd/gas_oracle/app/app.go | 8 ++++++++ rollup/internal/controller/relayer/l1_relayer.go | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rollup/cmd/gas_oracle/app/app.go b/rollup/cmd/gas_oracle/app/app.go index 3c14826712..536258882f 100644 --- a/rollup/cmd/gas_oracle/app/app.go +++ b/rollup/cmd/gas_oracle/app/app.go @@ -66,18 +66,26 @@ func action(ctx *cli.Context) error { registry := prometheus.DefaultRegisterer observability.Server(ctx, db) + // Init L1 connection l1RpcClient, err := rpc.Dial(cfg.L1Config.Endpoint) if err != nil { log.Crit("failed to dial raw RPC client to L1 endpoint", "endpoint", cfg.L1Config.Endpoint, "error", err) } l1client := ethclient.NewClient(l1RpcClient) + // sanity check config + if cfg.L1Config.RelayerConfig.GasOracleConfig.L1BaseFeeLimit == 0 || cfg.L1Config.RelayerConfig.GasOracleConfig.L1BlobBaseFeeLimit == 0 { + log.Crit("gas-oracle `l1_base_fee_limit` and `l1_blob_base_fee_limit` configs must be set") + } + + // Init watcher and relayer l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1RpcClient, cfg.L1Config.StartHeight, db, registry) l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig, relayer.ServiceTypeL1GasOracle, registry) if err != nil { log.Crit("failed to create new l1 relayer", "config file", cfgFile, "error", err) } + // Start l1 watcher process go utils.LoopWithContext(subCtx, 10*time.Second, func(ctx context.Context) { // Fetch the latest block number to decrease the delay when fetching gas prices diff --git a/rollup/internal/controller/relayer/l1_relayer.go b/rollup/internal/controller/relayer/l1_relayer.go index 581a1d752a..abc53907e1 100644 --- a/rollup/internal/controller/relayer/l1_relayer.go +++ b/rollup/internal/controller/relayer/l1_relayer.go @@ -174,13 +174,13 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { return } // Cap base fee update at the configured upper limit - if limit := r.cfg.GasOracleConfig.L1BaseFeeLimit; limit > 0 && baseFee > limit { + if limit := r.cfg.GasOracleConfig.L1BaseFeeLimit; baseFee > limit { log.Error("L1 base fee exceed max limit, set to max limit", "baseFee", baseFee, "maxLimit", limit) r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() baseFee = limit } // Cap blob base fee update at the configured upper limit - if limit := r.cfg.GasOracleConfig.L1BlobBaseFeeLimit; limit > 0 && blobBaseFee > limit { + if limit := r.cfg.GasOracleConfig.L1BlobBaseFeeLimit; blobBaseFee > limit { log.Error("L1 blob base fee exceed max limit, set to max limit", "blobBaseFee", blobBaseFee, "maxLimit", limit) r.metrics.rollupL1RelayerGasPriceOracleFeeOverLimitTotal.Inc() blobBaseFee = limit From 40eb9c379c1de0c17c5563667658f74e9d03f22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Fri, 28 Nov 2025 14:36:40 +0100 Subject: [PATCH 18/20] clean up --- common/testcontainers/testcontainers.go | 6 +----- rollup/internal/controller/relayer/l1_relayer_metrics.go | 2 +- rollup/tests/bridge_test.go | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/common/testcontainers/testcontainers.go b/common/testcontainers/testcontainers.go index 5f8e82b0c8..8a523767fb 100644 --- a/common/testcontainers/testcontainers.go +++ b/common/testcontainers/testcontainers.go @@ -173,11 +173,7 @@ func (t *TestcontainerApps) GetPoSL1Client() (*rpc.Client, error) { if err != nil { return nil, err } - rawClient, err := rpc.Dial(endpoint) - if err != nil { - return nil, fmt.Errorf("failed to dial L1 rpc endpoint, endpoint: %s, err: %w", endpoint, err) - } - return rawClient, nil + return rpc.Dial(endpoint) } // GetDBEndPoint returns the endpoint of the running postgres container diff --git a/rollup/internal/controller/relayer/l1_relayer_metrics.go b/rollup/internal/controller/relayer/l1_relayer_metrics.go index 0efb6264ee..b8dd0f555e 100644 --- a/rollup/internal/controller/relayer/l1_relayer_metrics.go +++ b/rollup/internal/controller/relayer/l1_relayer_metrics.go @@ -46,7 +46,7 @@ func initL1RelayerMetrics(reg prometheus.Registerer) *l1RelayerMetrics { }), rollupL1RelayerGasPriceOracleFeeOverLimitTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{ Name: "rollup_layer1_gas_price_oracle_fee_over_limit_total", - Help: "The total number of layer1 gas price oracle fee over limit", + Help: "The total number of times when a gas price oracle fee update went over the configured limit", }), } }) diff --git a/rollup/tests/bridge_test.go b/rollup/tests/bridge_test.go index ac21082465..5d69f2c918 100644 --- a/rollup/tests/bridge_test.go +++ b/rollup/tests/bridge_test.go @@ -94,8 +94,8 @@ func setupEnv(t *testing.T) { rollupApp = bcmd.NewRollupApp(testApps, "../conf/config.json") l1RawClient, err = testApps.GetPoSL1Client() - l1Client = ethclient.NewClient(l1RawClient) assert.NoError(t, err) + l1Client = ethclient.NewClient(l1RawClient) l2Client, err = testApps.GetL2GethClient() assert.NoError(t, err) l1GethChainID, err = l1Client.ChainID(context.Background()) From 626c7c747fca2dd3b765c022e5000b6e3287f85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Fri, 28 Nov 2025 15:06:01 +0100 Subject: [PATCH 19/20] fix test config --- rollup/conf/config.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rollup/conf/config.json b/rollup/conf/config.json index 5ad9d26a40..d79b8452ac 100644 --- a/rollup/conf/config.json +++ b/rollup/conf/config.json @@ -21,7 +21,9 @@ "check_committed_batches_window_minutes": 5, "l1_base_fee_default": 15000000000, "l1_blob_base_fee_default": 1, - "l1_blob_base_fee_threshold": 0 + "l1_blob_base_fee_threshold": 0, + "l1_base_fee_limit": 10000000000, + "l1_blob_base_fee_limit": 10000000000 }, "gas_oracle_sender_signer_config": { "signer_type": "PrivateKey", From 9a3c48b2c4c2182fb5d8a338b87f284ebd7d4eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Fri, 28 Nov 2025 15:18:46 +0100 Subject: [PATCH 20/20] update --- rollup/conf/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rollup/conf/config.json b/rollup/conf/config.json index d79b8452ac..537ac144b4 100644 --- a/rollup/conf/config.json +++ b/rollup/conf/config.json @@ -22,8 +22,8 @@ "l1_base_fee_default": 15000000000, "l1_blob_base_fee_default": 1, "l1_blob_base_fee_threshold": 0, - "l1_base_fee_limit": 10000000000, - "l1_blob_base_fee_limit": 10000000000 + "l1_base_fee_limit": 20000000000, + "l1_blob_base_fee_limit": 20000000000 }, "gas_oracle_sender_signer_config": { "signer_type": "PrivateKey",