From a4e06a539c9916e920ab8b447a30f80520ba8df3 Mon Sep 17 00:00:00 2001
From: colinlyguo <colinlyguo@scroll.io>
Date: Mon, 18 Nov 2024 21:56:39 +0800
Subject: [PATCH] add mandatory flag to control minimum codec version

---
 common/utils/flags.go                            |  7 +++++++
 rollup/cmd/rollup_relayer/app/app.go             |  8 +++++---
 .../controller/watcher/batch_proposer.go         | 10 ++++++----
 .../controller/watcher/batch_proposer_test.go    | 16 ++++++++--------
 .../controller/watcher/bundle_proposer.go        | 12 +++++++-----
 .../controller/watcher/bundle_proposer_test.go   |  6 +++---
 .../controller/watcher/chunk_proposer.go         | 10 ++++++----
 .../controller/watcher/chunk_proposer_test.go    |  4 ++--
 rollup/tests/rollup_test.go                      |  6 +++---
 9 files changed, 47 insertions(+), 32 deletions(-)

diff --git a/common/utils/flags.go b/common/utils/flags.go
index e6e4f64222..ef5919d54a 100644
--- a/common/utils/flags.go
+++ b/common/utils/flags.go
@@ -21,6 +21,7 @@ var (
 	// RollupRelayerFlags contains flags only used in rollup-relayer
 	RollupRelayerFlags = []cli.Flag{
 		&ImportGenesisFlag,
+		&MinCodecVersionFlag,
 	}
 	// ConfigFileFlag load json type config file.
 	ConfigFileFlag = cli.StringFlag{
@@ -90,4 +91,10 @@ var (
 		Usage: "Genesis file of the network",
 		Value: "./conf/genesis.json",
 	}
+	// MinCodecVersionFlag defines the minimum codec version required for the chunk/batch/bundle proposers
+	MinCodecVersionFlag = cli.UintFlag{
+		Name:     "min-codec-version",
+		Usage:    "Minimum required codec version for the chunk/batch/bundle proposers",
+		Required: true,
+	}
 )
diff --git a/rollup/cmd/rollup_relayer/app/app.go b/rollup/cmd/rollup_relayer/app/app.go
index ebaeb5723c..080f27cdf7 100644
--- a/rollup/cmd/rollup_relayer/app/app.go
+++ b/rollup/cmd/rollup_relayer/app/app.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
+	"github.com/scroll-tech/da-codec/encoding"
 	"github.com/scroll-tech/go-ethereum/ethclient"
 	"github.com/scroll-tech/go-ethereum/log"
 	"github.com/urfave/cli/v2"
@@ -84,9 +85,10 @@ func action(ctx *cli.Context) error {
 		log.Crit("failed to create l2 relayer", "config file", cfgFile, "error", err)
 	}
 
-	chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, genesis.Config, db, registry)
-	batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, genesis.Config, db, registry)
-	bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, genesis.Config, db, registry)
+	minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name))
+	chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, registry)
+	batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, registry)
+	bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry)
 
 	l2watcher := watcher.NewL2WatcherClient(subCtx, l2client, cfg.L2Config.Confirmations, cfg.L2Config.L2MessageQueueAddress, cfg.L2Config.WithdrawTrieRootSlot, genesis.Config, db, registry)
 
diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go
index f4a5a890b8..8b69cd9c25 100644
--- a/rollup/internal/controller/watcher/batch_proposer.go
+++ b/rollup/internal/controller/watcher/batch_proposer.go
@@ -34,7 +34,8 @@ type BatchProposer struct {
 	gasCostIncreaseMultiplier       float64
 	maxUncompressedBatchBytesSize   uint64
 
-	chainCfg *params.ChainConfig
+	minCodecVersion encoding.CodecVersion
+	chainCfg        *params.ChainConfig
 
 	batchProposerCircleTotal           prometheus.Counter
 	proposeBatchFailureTotal           prometheus.Counter
@@ -58,7 +59,7 @@ type BatchProposer struct {
 }
 
 // NewBatchProposer creates a new BatchProposer instance.
-func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *BatchProposer {
+func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *BatchProposer {
 	log.Info("new batch proposer",
 		"maxL1CommitGasPerBatch", cfg.MaxL1CommitGasPerBatch,
 		"maxL1CommitCalldataSizePerBatch", cfg.MaxL1CommitCalldataSizePerBatch,
@@ -78,6 +79,7 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, chai
 		batchTimeoutSec:                 cfg.BatchTimeoutSec,
 		gasCostIncreaseMultiplier:       cfg.GasCostIncreaseMultiplier,
 		maxUncompressedBatchBytesSize:   cfg.MaxUncompressedBatchBytesSize,
+		minCodecVersion:                 minCodecVersion,
 		chainCfg:                        chainCfg,
 
 		batchProposerCircleTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
@@ -248,8 +250,8 @@ func (p *BatchProposer) proposeBatch() error {
 		return fmt.Errorf("failed to retrieve codec for block number %v and time %v", firstUnbatchedChunk.StartBlockNumber, firstUnbatchedChunk.StartBlockTime)
 	}
 
-	if codec.Version() < encoding.CodecV4 {
-		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codec.Version(), encoding.CodecV4)
+	if codec.Version() < p.minCodecVersion {
+		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codec.Version(), p.minCodecVersion)
 	}
 
 	maxChunksThisBatch := codec.MaxNumChunksPerBatch()
diff --git a/rollup/internal/controller/watcher/batch_proposer_test.go b/rollup/internal/controller/watcher/batch_proposer_test.go
index 7484d4fd36..7c426d5b10 100644
--- a/rollup/internal/controller/watcher/batch_proposer_test.go
+++ b/rollup/internal/controller/watcher/batch_proposer_test.go
@@ -117,7 +117,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) {
 				ChunkTimeoutSec:                 300,
 				GasCostIncreaseMultiplier:       1.2,
 				MaxUncompressedBatchBytesSize:   math.MaxUint64,
-			}, &params.ChainConfig{
+			}, encoding.CodecV4, &params.ChainConfig{
 				LondonBlock:    big.NewInt(0),
 				BernoulliBlock: big.NewInt(0),
 				CurieBlock:     big.NewInt(0),
@@ -140,7 +140,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) {
 				BatchTimeoutSec:                 tt.batchTimeoutSec,
 				GasCostIncreaseMultiplier:       1.2,
 				MaxUncompressedBatchBytesSize:   math.MaxUint64,
-			}, &params.ChainConfig{
+			}, encoding.CodecV4, &params.ChainConfig{
 				LondonBlock:    big.NewInt(0),
 				BernoulliBlock: big.NewInt(0),
 				CurieBlock:     big.NewInt(0),
@@ -211,7 +211,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) {
 		ChunkTimeoutSec:                 300,
 		GasCostIncreaseMultiplier:       1.2,
 		MaxUncompressedBatchBytesSize:   math.MaxUint64,
-	}, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
+	}, encoding.CodecV4, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
 	cp.TryProposeChunk() // chunk1 contains block1
 	cp.TryProposeChunk() // chunk2 contains block2
 
@@ -228,7 +228,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) {
 		BatchTimeoutSec:                 0,
 		GasCostIncreaseMultiplier:       1.2,
 		MaxUncompressedBatchBytesSize:   math.MaxUint64,
-	}, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
+	}, encoding.CodecV4, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
 	bp.TryProposeBatch()
 
 	batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0)
@@ -296,7 +296,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) {
 			ChunkTimeoutSec:                 0,
 			GasCostIncreaseMultiplier:       1,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		blockHeight := int64(0)
 		block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json")
@@ -317,7 +317,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) {
 			BatchTimeoutSec:                 math.MaxUint32,
 			GasCostIncreaseMultiplier:       1,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		for i := 0; i < 2; i++ {
 			bp.TryProposeBatch()
@@ -390,7 +390,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) {
 			ChunkTimeoutSec:                 0,
 			GasCostIncreaseMultiplier:       1,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json")
 		for blockHeight := int64(1); blockHeight <= 60; blockHeight++ {
@@ -406,7 +406,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) {
 			BatchTimeoutSec:                 math.MaxUint32,
 			GasCostIncreaseMultiplier:       1,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 		bp.TryProposeBatch()
 
 		batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0)
diff --git a/rollup/internal/controller/watcher/bundle_proposer.go b/rollup/internal/controller/watcher/bundle_proposer.go
index a71b0d998c..1a37a6265a 100644
--- a/rollup/internal/controller/watcher/bundle_proposer.go
+++ b/rollup/internal/controller/watcher/bundle_proposer.go
@@ -29,7 +29,8 @@ type BundleProposer struct {
 	maxBatchNumPerBundle uint64
 	bundleTimeoutSec     uint64
 
-	chainCfg *params.ChainConfig
+	minCodecVersion encoding.CodecVersion
+	chainCfg        *params.ChainConfig
 
 	bundleProposerCircleTotal           prometheus.Counter
 	proposeBundleFailureTotal           prometheus.Counter
@@ -41,7 +42,7 @@ type BundleProposer struct {
 }
 
 // NewBundleProposer creates a new BundleProposer instance.
-func NewBundleProposer(ctx context.Context, cfg *config.BundleProposerConfig, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *BundleProposer {
+func NewBundleProposer(ctx context.Context, cfg *config.BundleProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *BundleProposer {
 	log.Info("new bundle proposer", "bundleBatchesNum", cfg.MaxBatchNumPerBundle, "bundleTimeoutSec", cfg.BundleTimeoutSec)
 
 	p := &BundleProposer{
@@ -52,6 +53,7 @@ func NewBundleProposer(ctx context.Context, cfg *config.BundleProposerConfig, ch
 		bundleOrm:            orm.NewBundle(db),
 		maxBatchNumPerBundle: cfg.MaxBatchNumPerBundle,
 		bundleTimeoutSec:     cfg.BundleTimeoutSec,
+		minCodecVersion:      minCodecVersion,
 		chainCfg:             chainCfg,
 
 		bundleProposerCircleTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
@@ -131,7 +133,7 @@ func (p *BundleProposer) proposeBundle() error {
 
 	// select at most maxBlocksThisChunk blocks
 	maxBatchesThisBundle := p.maxBatchNumPerBundle
-	batches, err := p.batchOrm.GetBatchesGEIndexGECodecVersion(p.ctx, firstUnbundledBatchIndex, encoding.CodecV4, int(maxBatchesThisBundle))
+	batches, err := p.batchOrm.GetBatchesGEIndexGECodecVersion(p.ctx, firstUnbundledBatchIndex, p.minCodecVersion, int(maxBatchesThisBundle))
 	if err != nil {
 		return err
 	}
@@ -155,8 +157,8 @@ func (p *BundleProposer) proposeBundle() error {
 	hardforkName := encoding.GetHardforkName(p.chainCfg, firstChunk.StartBlockNumber, firstChunk.StartBlockTime)
 	codecVersion := encoding.CodecVersion(batches[0].CodecVersion)
 
-	if codecVersion < encoding.CodecV4 {
-		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codecVersion, encoding.CodecV4)
+	if codecVersion < p.minCodecVersion {
+		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codecVersion, p.minCodecVersion)
 	}
 
 	for i := 1; i < len(batches); i++ {
diff --git a/rollup/internal/controller/watcher/bundle_proposer_test.go b/rollup/internal/controller/watcher/bundle_proposer_test.go
index 5f9c5a9062..4a0a4219b0 100644
--- a/rollup/internal/controller/watcher/bundle_proposer_test.go
+++ b/rollup/internal/controller/watcher/bundle_proposer_test.go
@@ -99,7 +99,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) {
 				ChunkTimeoutSec:                 math.MaxUint32,
 				GasCostIncreaseMultiplier:       1,
 				MaxUncompressedBatchBytesSize:   math.MaxUint64,
-			}, chainConfig, db, nil)
+			}, encoding.CodecV4, chainConfig, db, nil)
 
 			bap := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
 				MaxL1CommitGasPerBatch:          math.MaxUint64,
@@ -107,7 +107,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) {
 				BatchTimeoutSec:                 0,
 				GasCostIncreaseMultiplier:       1,
 				MaxUncompressedBatchBytesSize:   math.MaxUint64,
-			}, chainConfig, db, nil)
+			}, encoding.CodecV4, chainConfig, db, nil)
 
 			cp.TryProposeChunk()  // chunk1 contains block1
 			bap.TryProposeBatch() // batch1 contains chunk1
@@ -117,7 +117,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) {
 			bup := NewBundleProposer(context.Background(), &config.BundleProposerConfig{
 				MaxBatchNumPerBundle: tt.maxBatchNumPerBundle,
 				BundleTimeoutSec:     tt.bundleTimeoutSec,
-			}, chainConfig, db, nil)
+			}, encoding.CodecV4, chainConfig, db, nil)
 
 			bup.TryProposeBundle()
 
diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go
index 8a9d0249d9..87056e6464 100644
--- a/rollup/internal/controller/watcher/chunk_proposer.go
+++ b/rollup/internal/controller/watcher/chunk_proposer.go
@@ -34,7 +34,8 @@ type ChunkProposer struct {
 	gasCostIncreaseMultiplier       float64
 	maxUncompressedBatchBytesSize   uint64
 
-	chainCfg *params.ChainConfig
+	minCodecVersion encoding.CodecVersion
+	chainCfg        *params.ChainConfig
 
 	chunkProposerCircleTotal           prometheus.Counter
 	proposeChunkFailureTotal           prometheus.Counter
@@ -60,7 +61,7 @@ type ChunkProposer struct {
 }
 
 // NewChunkProposer creates a new ChunkProposer instance.
-func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *ChunkProposer {
+func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *ChunkProposer {
 	log.Info("new chunk proposer",
 		"maxBlockNumPerChunk", cfg.MaxBlockNumPerChunk,
 		"maxTxNumPerChunk", cfg.MaxTxNumPerChunk,
@@ -85,6 +86,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, chai
 		chunkTimeoutSec:                 cfg.ChunkTimeoutSec,
 		gasCostIncreaseMultiplier:       cfg.GasCostIncreaseMultiplier,
 		maxUncompressedBatchBytesSize:   cfg.MaxUncompressedBatchBytesSize,
+		minCodecVersion:                 minCodecVersion,
 		chainCfg:                        chainCfg,
 
 		chunkProposerCircleTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
@@ -277,8 +279,8 @@ func (p *ChunkProposer) proposeChunk() error {
 
 	codecVersion := encoding.GetCodecVersion(p.chainCfg, blocks[0].Header.Number.Uint64(), blocks[0].Header.Time)
 
-	if codecVersion < encoding.CodecV4 {
-		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codecVersion, encoding.CodecV4)
+	if codecVersion < p.minCodecVersion {
+		return fmt.Errorf("unsupported codec version: %v, expected at least %v", codecVersion, p.minCodecVersion)
 	}
 
 	// Including Curie block in a sole chunk.
diff --git a/rollup/internal/controller/watcher/chunk_proposer_test.go b/rollup/internal/controller/watcher/chunk_proposer_test.go
index 23733154ab..4886fbebe0 100644
--- a/rollup/internal/controller/watcher/chunk_proposer_test.go
+++ b/rollup/internal/controller/watcher/chunk_proposer_test.go
@@ -164,7 +164,7 @@ func testChunkProposerLimitsCodecV4(t *testing.T) {
 				ChunkTimeoutSec:                 tt.chunkTimeoutSec,
 				GasCostIncreaseMultiplier:       1.2,
 				MaxUncompressedBatchBytesSize:   math.MaxUint64,
-			}, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
+			}, encoding.CodecV4, &params.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil)
 			cp.TryProposeChunk()
 
 			chunkOrm := orm.NewChunk(db)
@@ -214,7 +214,7 @@ func testChunkProposerBlobSizeLimitCodecV4(t *testing.T) {
 			ChunkTimeoutSec:                 math.MaxUint32,
 			GasCostIncreaseMultiplier:       1,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		for i := 0; i < 2; i++ {
 			cp.TryProposeChunk()
diff --git a/rollup/tests/rollup_test.go b/rollup/tests/rollup_test.go
index 4a217298d2..4251aa273b 100644
--- a/rollup/tests/rollup_test.go
+++ b/rollup/tests/rollup_test.go
@@ -97,19 +97,19 @@ func testCommitBatchAndFinalizeBundleCodecV4(t *testing.T) {
 			MaxRowConsumptionPerChunk:       1048319,
 			ChunkTimeoutSec:                 300,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{
 			MaxL1CommitGasPerBatch:          50000000000,
 			MaxL1CommitCalldataSizePerBatch: 1000000,
 			BatchTimeoutSec:                 300,
 			MaxUncompressedBatchBytesSize:   math.MaxUint64,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		bup := watcher.NewBundleProposer(context.Background(), &config.BundleProposerConfig{
 			MaxBatchNumPerBundle: 1000000,
 			BundleTimeoutSec:     300,
-		}, chainConfig, db, nil)
+		}, encoding.CodecV4, chainConfig, db, nil)
 
 		l2BlockOrm := orm.NewL2Block(db)
 		err = l2BlockOrm.InsertL2Blocks(context.Background(), blocks[:5])