From 25b956f9b54e99b0877e8675465bc3c98a98b887 Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:21:06 +0800 Subject: [PATCH] fix(gas-oracle): fetch base fee from the latest L1 block (#920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Péter Garamvölgyi Co-authored-by: colinlyguo --- common/version/version.go | 2 +- rollup/cmd/gas_oracle/app/app.go | 9 ++++--- rollup/internal/orm/l1_block.go | 27 +++++++++++++------ rollup/internal/orm/orm_test.go | 45 ++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/common/version/version.go b/common/version/version.go index 083d28abe0..ff369603f4 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.2.20" +var tag = "v4.2.21" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/cmd/gas_oracle/app/app.go b/rollup/cmd/gas_oracle/app/app.go index ea8038a604..7cf08a8952 100644 --- a/rollup/cmd/gas_oracle/app/app.go +++ b/rollup/cmd/gas_oracle/app/app.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/scroll-tech/go-ethereum/ethclient" "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rpc" "github.com/urfave/cli/v2" "scroll-tech/common/database" @@ -92,14 +93,16 @@ func action(ctx *cli.Context) error { } // Start l1 watcher process go utils.LoopWithContext(subCtx, 10*time.Second, func(ctx context.Context) { - number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l1client, cfg.L1Config.Confirmations) + // Fetch the latest block number to decrease the delay when fetching gas prices + // Use latest block number - 1 to prevent frequent reorg + number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l1client, rpc.LatestBlockNumber) if loopErr != nil { log.Error("failed to get block number", "err", loopErr) return } - if loopErr = l1watcher.FetchBlockHeader(number); loopErr != nil { - log.Error("Failed to fetch L1 block header", "lastest", number, "err", loopErr) + if loopErr = l1watcher.FetchBlockHeader(number - 1); loopErr != nil { + log.Error("Failed to fetch L1 block header", "lastest", number-1, "err", loopErr) } }) diff --git a/rollup/internal/orm/l1_block.go b/rollup/internal/orm/l1_block.go index 644f7926a5..7a1be88d6a 100644 --- a/rollup/internal/orm/l1_block.go +++ b/rollup/internal/orm/l1_block.go @@ -70,19 +70,30 @@ func (o *L1Block) GetL1Blocks(ctx context.Context, fields map[string]interface{} return l1Blocks, nil } -// InsertL1Blocks batch insert l1 blocks +// InsertL1Blocks batch inserts l1 blocks. +// If there's a block number conflict (e.g., due to reorg), soft deletes the existing block and inserts the new one. func (o *L1Block) InsertL1Blocks(ctx context.Context, blocks []L1Block) error { if len(blocks) == 0 { return nil } - db := o.db.WithContext(ctx) - db = db.Model(&L1Block{}) - - if err := db.Create(&blocks).Error; err != nil { - return fmt.Errorf("L1Block.InsertL1Blocks error: %w", err) - } - return nil + return o.db.Transaction(func(tx *gorm.DB) error { + var blockNumbers []uint64 + for _, block := range blocks { + blockNumbers = append(blockNumbers, block.Number) + } + + db := tx.WithContext(ctx) + db = db.Model(&L1Block{}) + if err := db.Where("number IN (?)", blockNumbers).Delete(&L1Block{}).Error; err != nil { + return fmt.Errorf("L1Block.InsertL1Blocks error: soft deleting blocks failed, block numbers: %v, error: %w", blockNumbers, err) + } + + if err := db.Create(&blocks).Error; err != nil { + return fmt.Errorf("L1Block.InsertL1Blocks error: %w", err) + } + return nil + }) } // UpdateL1GasOracleStatusAndOracleTxHash update l1 gas oracle status and oracle tx hash diff --git a/rollup/internal/orm/orm_test.go b/rollup/internal/orm/orm_test.go index 3542aa3cc6..70a94a56f6 100644 --- a/rollup/internal/orm/orm_test.go +++ b/rollup/internal/orm/orm_test.go @@ -89,6 +89,51 @@ func tearDownEnv(t *testing.T) { base.Free() } +func TestL1BlockOrm(t *testing.T) { + sqlDB, err := db.DB() + assert.NoError(t, err) + assert.NoError(t, migrate.ResetDB(sqlDB)) + + l1BlockOrm := NewL1Block(db) + + // mock blocks + block1 := L1Block{Number: 1, Hash: "hash1"} + block2 := L1Block{Number: 2, Hash: "hash2"} + block2AfterReorg := L1Block{Number: 2, Hash: "hash3"} + + err = l1BlockOrm.InsertL1Blocks(context.Background(), []L1Block{block1, block2}) + assert.NoError(t, err) + + height, err := l1BlockOrm.GetLatestL1BlockHeight(context.Background()) + assert.NoError(t, err) + assert.Equal(t, uint64(2), height) + + blocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) + assert.NoError(t, err) + assert.Len(t, blocks, 2) + assert.Equal(t, "hash1", blocks[0].Hash) + assert.Equal(t, "hash2", blocks[1].Hash) + + // reorg handling: insert another block with same height and different hash + err = l1BlockOrm.InsertL1Blocks(context.Background(), []L1Block{block2AfterReorg}) + assert.NoError(t, err) + + blocks, err = l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) + assert.NoError(t, err) + assert.Len(t, blocks, 2) + assert.Equal(t, "hash1", blocks[0].Hash) + assert.Equal(t, "hash3", blocks[1].Hash) + + err = l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(context.Background(), "hash1", types.GasOracleImported, "txhash1") + assert.NoError(t, err) + + updatedBlocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) + assert.NoError(t, err) + assert.Len(t, updatedBlocks, 2) + assert.Equal(t, types.GasOracleImported, types.GasOracleStatus(updatedBlocks[0].GasOracleStatus)) + assert.Equal(t, "txhash1", updatedBlocks[0].OracleTxHash) +} + func TestL2BlockOrm(t *testing.T) { sqlDB, err := db.DB() assert.NoError(t, err)