From 4e5839ff538f97742d5e71eafa64d90406908243 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 14 Apr 2025 21:45:03 +0800 Subject: [PATCH 01/21] add proposer tool --- common/version/version.go | 2 +- database/cmd/app/client.go | 2 +- rollup/README.md | 29 +++ rollup/cmd/proposer_tool/app/app.go | 179 ++++++++++++++++++ rollup/cmd/proposer_tool/main.go | 7 + rollup/cmd/rollup_relayer/app/app.go | 4 +- rollup/docker-compose-proposer-tool.yml | 50 +++++ rollup/internal/config/config.go | 7 +- .../controller/watcher/batch_proposer.go | 4 +- .../controller/watcher/batch_proposer_test.go | 16 +- .../watcher/bundle_proposer_test.go | 4 +- .../controller/watcher/chunk_proposer.go | 16 +- .../controller/watcher/chunk_proposer_test.go | 4 +- rollup/proposer-tool-config.json | 40 ++++ rollup/proposer-tool-db-config.json | 6 + rollup/proposer-tool-genesis.json | 19 ++ rollup/proposer_tool.Dockerfile | 30 +++ rollup/tests/rollup_test.go | 8 +- 18 files changed, 396 insertions(+), 31 deletions(-) create mode 100644 rollup/cmd/proposer_tool/app/app.go create mode 100644 rollup/cmd/proposer_tool/main.go create mode 100644 rollup/docker-compose-proposer-tool.yml create mode 100644 rollup/proposer-tool-config.json create mode 100644 rollup/proposer-tool-db-config.json create mode 100644 rollup/proposer-tool-genesis.json create mode 100644 rollup/proposer_tool.Dockerfile diff --git a/common/version/version.go b/common/version/version.go index 60bc144895..7eeb26db3d 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.5.4" +var tag = "v4.5.5" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/database/cmd/app/client.go b/database/cmd/app/client.go index bcd0c9246a..68c8fc0009 100644 --- a/database/cmd/app/client.go +++ b/database/cmd/app/client.go @@ -42,7 +42,7 @@ func resetDB(ctx *cli.Context) error { } var version int64 - err = migrate.Rollback(db.DB, &version) + err = migrate.ResetDB(db.DB) if err != nil { return err } diff --git a/rollup/README.md b/rollup/README.md index 6b53fb065e..7f912dc5fc 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -33,3 +33,32 @@ make rollup_bins ./build/bin/gas_oracle --config ./conf/config.json ./build/bin/rollup_relayer --config ./conf/config.json ``` + +## How to run the proposer tool? + +### Set the configs + +1. Set genesis config to enable desired hardforks in [`proposer-tool-genesis.json`](./proposer-tool-genesis.json). +2. Set proposer config in [`proposer-tool-config.json`](./proposer-tool-config.json) for data analysis. + +### Start the proposer tool using docker-compose. + +``` +cd rollup +DOCKER_BUILDKIT=1 docker-compose -f docker-compose-proposer-tool.yml up -d +``` + +> Note: The port 5432 of database is mapped to the host machine. You can use `psql` or any db clients to connect to the database. + +> The DSN for the database is `postgres://postgres:postgres@db:5432/scroll?sslmode=disable`. + + +### Reset env +``` +docker-compose -f docker-compose-proposer-tool.yml down -v +``` + +If you need to rebuild the images, removing the old images is necessary. You can do this by running the following command: +``` +docker images | grep rollup | awk '{print $3}' | xargs docker rmi -f +``` diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go new file mode 100644 index 0000000000..94089c27fa --- /dev/null +++ b/rollup/cmd/proposer_tool/app/app.go @@ -0,0 +1,179 @@ +package app + +import ( + "context" + "fmt" + "math/big" + "os" + "os/signal" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/scroll-tech/da-codec/encoding" + "github.com/scroll-tech/go-ethereum/common" + 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/urfave/cli/v2" + + "scroll-tech/common/database" + "scroll-tech/common/types" + "scroll-tech/common/utils" + "scroll-tech/common/version" + + "scroll-tech/rollup/internal/config" + "scroll-tech/rollup/internal/controller/watcher" + "scroll-tech/rollup/internal/orm" + rutils "scroll-tech/rollup/internal/utils" +) + +var app *cli.App + +func init() { + // Set up proposer-tool app info. + app = cli.NewApp() + app.Action = action + app.Name = "proposer-tool" + app.Usage = "The Scroll Proposer Tool" + app.Version = version.Version + app.Flags = append(app.Flags, utils.CommonFlags...) + app.Flags = append(app.Flags, utils.RollupRelayerFlags...) + app.Commands = []*cli.Command{} + app.Before = func(ctx *cli.Context) error { + return utils.LogSetup(ctx) + } +} + +func action(ctx *cli.Context) error { + // Load config file. + cfgFile := ctx.String(utils.ConfigFileFlag.Name) + cfg, err := config.NewConfig(cfgFile) + if err != nil { + log.Crit("failed to load config file", "config file", cfgFile, "error", err) + } + + subCtx, cancel := context.WithCancel(ctx.Context) + // Init db connection + db, err := database.InitDB(cfg.DBConfig) + if err != nil { + log.Crit("failed to init db connection", "err", err) + } + defer func() { + cancel() + if err = database.CloseDB(db); err != nil { + log.Crit("failed to close db connection", "error", err) + } + }() + + // Init l2BlockOrm connection + dbForReplay, err := database.InitDB(cfg.DBConfigForReplay) + if err != nil { + log.Crit("failed to init l2BlockOrm connection", "err", err) + } + defer func() { + cancel() + if err = database.CloseDB(dbForReplay); err != nil { + log.Crit("failed to close l2BlockOrm connection", "error", err) + } + }() + + // Init l2geth connection + l2Client, err := ethclient.Dial(cfg.L2Config.Endpoint) + if err != nil { + log.Crit("failed to connect l2 geth", "config file", cfgFile, "error", err) + } + + genesisHeader, err := l2Client.HeaderByNumber(subCtx, big.NewInt(0)) + if err != nil { + return fmt.Errorf("failed to retrieve L2 genesis header: %v", err) + } + + genesisTime := genesisHeader.Time + currentTime := uint64(time.Now().Unix()) + timeDrift := currentTime - genesisTime + + cfg.L2Config.ChunkProposerConfig.ChunkTimeoutSec += timeDrift + cfg.L2Config.BatchProposerConfig.BatchTimeoutSec += timeDrift + cfg.L2Config.BundleProposerConfig.BundleTimeoutSec += timeDrift + + chunk := &encoding.Chunk{ + Blocks: []*encoding.Block{{ + Header: genesisHeader, + Transactions: nil, + WithdrawRoot: common.Hash{}, + RowConsumption: &gethTypes.RowConsumption{}, + }}, + } + + var dbChunk *orm.Chunk + dbChunk, err = orm.NewChunk(db).InsertChunk(subCtx, chunk, encoding.CodecV0, rutils.ChunkMetrics{}) + if err != nil { + log.Crit("failed to insert chunk", "error", err) + } + + if err = orm.NewChunk(db).UpdateProvingStatus(subCtx, dbChunk.Hash, types.ProvingTaskVerified); err != nil { + log.Crit("failed to update genesis chunk proving status", "error", err) + } + + batch := &encoding.Batch{ + Index: 0, + TotalL1MessagePoppedBefore: 0, + ParentBatchHash: common.Hash{}, + Chunks: []*encoding.Chunk{chunk}, + } + + var dbBatch *orm.Batch + dbBatch, err = orm.NewBatch(db).InsertBatch(subCtx, batch, encoding.CodecV0, rutils.BatchMetrics{}) + if err != nil { + log.Crit("failed to insert batch", "error", err) + } + + if err = orm.NewChunk(db).UpdateBatchHashInRange(subCtx, 0, 0, dbBatch.Hash); err != nil { + log.Crit("failed to update batch hash for chunks", "error", err) + } + + registry := prometheus.DefaultRegisterer + + genesisPath := ctx.String(utils.Genesis.Name) + genesis, err := utils.ReadGenesis(genesisPath) + if err != nil { + log.Crit("failed to read genesis", "genesis file", genesisPath, "error", err) + } + + // sanity check config + if cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch <= 0 { + log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") + } + if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { + log.Crit("cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk must be greater than 0") + } + + minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) + chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry, true /* used by tool */) + batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry) + bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry) + + go utils.Loop(subCtx, 100*time.Millisecond, chunkProposer.TryProposeChunk) + go utils.Loop(subCtx, 100*time.Millisecond, batchProposer.TryProposeBatch) + go utils.Loop(subCtx, 100*time.Millisecond, bundleProposer.TryProposeBundle) + + // Finish start all proposer tool functions. + log.Info("Start proposer-tool successfully", "version", version.Version) + + // Catch CTRL-C to ensure a graceful shutdown. + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + + // Wait until the interrupt signal is received from an OS signal. + <-interrupt + + return nil +} + +// Run proposer tool cmd instance. +func Run() { + if err := app.Run(os.Args); err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/rollup/cmd/proposer_tool/main.go b/rollup/cmd/proposer_tool/main.go new file mode 100644 index 0000000000..2143b6ef2e --- /dev/null +++ b/rollup/cmd/proposer_tool/main.go @@ -0,0 +1,7 @@ +package main + +import "scroll-tech/rollup/cmd/proposer_tool/app" + +func main() { + app.Run() +} diff --git a/rollup/cmd/rollup_relayer/app/app.go b/rollup/cmd/rollup_relayer/app/app.go index d6d1e8a763..171315e2ec 100644 --- a/rollup/cmd/rollup_relayer/app/app.go +++ b/rollup/cmd/rollup_relayer/app/app.go @@ -102,8 +102,8 @@ func action(ctx *cli.Context) error { } 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) + chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, db, registry, false /* not used by tool */) + batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, 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/docker-compose-proposer-tool.yml b/rollup/docker-compose-proposer-tool.yml new file mode 100644 index 0000000000..dbe37965c8 --- /dev/null +++ b/rollup/docker-compose-proposer-tool.yml @@ -0,0 +1,50 @@ +version: '3' + +services: + db: + image: postgres:14 + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=scroll + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + + db-client: + build: + context: .. + dockerfile: ./build/dockerfiles/db_cli.Dockerfile + depends_on: + db: + condition: service_healthy + command: ["reset", "--config", "/app/conf/proposer-tool-db-config.json"] + volumes: + - ./proposer-tool-db-config.json:/app/conf/proposer-tool-db-config.json + + proposer-tool: + build: + context: .. + dockerfile: ./rollup/proposer_tool.Dockerfile + depends_on: + db-client: + condition: service_completed_successfully + command: [ + "--config", "/app/conf/proposer-tool-config.json", + "--genesis", "/app/conf/proposer-tool-genesis.json", + "--min-codec-version", "4", + "--log.debug", "--verbosity", "3" + ] + volumes: + - ./proposer-tool-config.json:/app/conf/proposer-tool-config.json + - ./proposer-tool-genesis.json:/app/conf/proposer-tool-genesis.json + restart: unless-stopped + +volumes: + postgres_data: diff --git a/rollup/internal/config/config.go b/rollup/internal/config/config.go index 71d57b9101..b730e78617 100644 --- a/rollup/internal/config/config.go +++ b/rollup/internal/config/config.go @@ -15,9 +15,10 @@ import ( // Config load configuration items. type Config struct { - L1Config *L1Config `json:"l1_config"` - L2Config *L2Config `json:"l2_config"` - DBConfig *database.Config `json:"db_config"` + L1Config *L1Config `json:"l1_config"` + L2Config *L2Config `json:"l2_config"` + DBConfig *database.Config `json:"db_config"` + DBConfigForReplay *database.Config `json:"db_config_for_replay"` } // NewConfig returns a new instance of Config. diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go index e782896fdb..d33bc70552 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -59,7 +59,7 @@ type BatchProposer struct { } // NewBatchProposer creates a new BatchProposer instance. -func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *BatchProposer { +func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, db *gorm.DB, reg prometheus.Registerer) *BatchProposer { log.Info("new batch proposer", "maxL1CommitGasPerBatch", cfg.MaxL1CommitGasPerBatch, "maxL1CommitCalldataSizePerBatch", cfg.MaxL1CommitCalldataSizePerBatch, @@ -73,7 +73,7 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minC db: db, batchOrm: orm.NewBatch(db), chunkOrm: orm.NewChunk(db), - l2BlockOrm: orm.NewL2Block(db), + l2BlockOrm: orm.NewL2Block(l2BlockDB), maxL1CommitGasPerBatch: cfg.MaxL1CommitGasPerBatch, maxL1CommitCalldataSizePerBatch: cfg.MaxL1CommitCalldataSizePerBatch, batchTimeoutSec: cfg.BatchTimeoutSec, diff --git a/rollup/internal/controller/watcher/batch_proposer_test.go b/rollup/internal/controller/watcher/batch_proposer_test.go index 9bd03fde4a..0a1111c76a 100644 --- a/rollup/internal/controller/watcher/batch_proposer_test.go +++ b/rollup/internal/controller/watcher/batch_proposer_test.go @@ -124,7 +124,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) { CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64), - }, db, nil) + }, db, db, nil, false /* not used by tool */) cp.TryProposeChunk() // chunk1 contains block1 cp.TryProposeChunk() // chunk2 contains block2 @@ -148,7 +148,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) { CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64), - }, db, nil) + }, db, db, nil) bp.TryProposeBatch() batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0) @@ -214,7 +214,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) { ChunkTimeoutSec: 300, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil) + }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil, false /* not used by tool */) cp.TryProposeChunk() // chunk1 contains block1 cp.TryProposeChunk() // chunk2 contains block2 @@ -232,7 +232,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil) + }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) bp.TryProposeBatch() batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0) @@ -301,7 +301,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) blockHeight := int64(0) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") @@ -323,7 +323,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil) for i := 0; i < 2; i++ { bp.TryProposeBatch() @@ -397,7 +397,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") for blockHeight := int64(1); blockHeight <= 60; blockHeight++ { @@ -414,7 +414,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil) bp.TryProposeBatch() batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0) diff --git a/rollup/internal/controller/watcher/bundle_proposer_test.go b/rollup/internal/controller/watcher/bundle_proposer_test.go index 50f39fdb38..08242f2b5e 100644 --- a/rollup/internal/controller/watcher/bundle_proposer_test.go +++ b/rollup/internal/controller/watcher/bundle_proposer_test.go @@ -103,7 +103,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) bap := NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: math.MaxUint64, @@ -111,7 +111,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) { BatchTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil) cp.TryProposeChunk() // chunk1 contains block1 bap.TryProposeBatch() // batch1 contains chunk1 diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index f20a1ff3c3..c90ad53bc1 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -36,6 +36,7 @@ type ChunkProposer struct { gasCostIncreaseMultiplier float64 maxUncompressedBatchBytesSize uint64 + toolFlag bool minCodecVersion encoding.CodecVersion chainCfg *params.ChainConfig @@ -64,7 +65,7 @@ type ChunkProposer struct { } // NewChunkProposer creates a new ChunkProposer instance. -func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *ChunkProposer { +func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, db *gorm.DB, reg prometheus.Registerer, toolFlag bool) *ChunkProposer { log.Info("new chunk proposer", "maxBlockNumPerChunk", cfg.MaxBlockNumPerChunk, "maxTxNumPerChunk", cfg.MaxTxNumPerChunk, @@ -81,7 +82,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC ctx: ctx, db: db, chunkOrm: orm.NewChunk(db), - l2BlockOrm: orm.NewL2Block(db), + l2BlockOrm: orm.NewL2Block(l2BlockDB), maxBlockNumPerChunk: cfg.MaxBlockNumPerChunk, maxTxNumPerChunk: cfg.MaxTxNumPerChunk, maxL2GasPerChunk: cfg.MaxL2GasPerChunk, @@ -91,6 +92,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC chunkTimeoutSec: cfg.ChunkTimeoutSec, gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier, maxUncompressedBatchBytesSize: cfg.MaxUncompressedBatchBytesSize, + toolFlag: toolFlag, minCodecVersion: minCodecVersion, chainCfg: chainCfg, @@ -241,9 +243,11 @@ func (p *ChunkProposer) updateDBChunkInfo(chunk *encoding.Chunk, codecVersion en log.Warn("ChunkProposer.InsertChunk failed", "codec version", codecVersion, "err", err) return err } - if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil { - log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", dbChunk.Hash, "start block", dbChunk.StartBlockNumber, "end block", dbChunk.EndBlockNumber, "err", err) - return err + if !p.toolFlag { + if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil { + log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", dbChunk.Hash, "start block", dbChunk.StartBlockNumber, "end block", dbChunk.EndBlockNumber, "err", err) + return err + } } return nil }) @@ -441,7 +445,7 @@ func (p *ChunkProposer) tryProposeEuclidTransitionChunk(blocks []*encoding.Block } prevBlocks, err := p.l2BlockOrm.GetL2BlocksGEHeight(p.ctx, blocks[0].Header.Number.Uint64()-1, 1) - if err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash { + if !p.toolFlag && (err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash) { return false, fmt.Errorf("failed to get parent block: %w", err) } diff --git a/rollup/internal/controller/watcher/chunk_proposer_test.go b/rollup/internal/controller/watcher/chunk_proposer_test.go index 974db79b06..3414bab4d7 100644 --- a/rollup/internal/controller/watcher/chunk_proposer_test.go +++ b/rollup/internal/controller/watcher/chunk_proposer_test.go @@ -202,7 +202,7 @@ func testChunkProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: tt.chunkTimeoutSec, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, nil) + }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil, false /* not used by tool */) cp.TryProposeChunk() chunkOrm := orm.NewChunk(db) @@ -253,7 +253,7 @@ func testChunkProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) for i := 0; i < 2; i++ { cp.TryProposeChunk() diff --git a/rollup/proposer-tool-config.json b/rollup/proposer-tool-config.json new file mode 100644 index 0000000000..46d06903c3 --- /dev/null +++ b/rollup/proposer-tool-config.json @@ -0,0 +1,40 @@ +{ + "l2_config": { + "endpoint": "https://rpc.scroll.io", + "chunk_proposer_config": { + "max_block_num_per_chunk": 100, + "max_tx_num_per_chunk": 100, + "max_l2_gas_per_chunk": 20000000, + "max_l1_commit_gas_per_chunk": 5000000, + "max_l1_commit_calldata_size_per_chunk": 123740, + "chunk_timeout_sec": 720000, + "max_row_consumption_per_chunk": 1000000, + "gas_cost_increase_multiplier": 1.2, + "max_uncompressed_batch_bytes_size": 634693 + }, + "batch_proposer_config": { + "max_l1_commit_gas_per_batch": 5000000, + "max_l1_commit_calldata_size_per_batch": 123740, + "batch_timeout_sec": 720000, + "gas_cost_increase_multiplier": 1.2, + "max_uncompressed_batch_bytes_size": 634693, + "max_chunks_per_batch": 45 + }, + "bundle_proposer_config": { + "max_batch_num_per_bundle": 45, + "bundle_timeout_sec": 3600 + } + }, + "db_config": { + "driver_name": "postgres", + "dsn": "postgres://postgres:postgres@db:5432/scroll?sslmode=disable", + "maxOpenNum": 200, + "maxIdleNum": 20 + }, + "db_config_for_replay": { + "driver_name": "postgres", + "dsn": "", + "maxOpenNum": 200, + "maxIdleNum": 20 + } +} diff --git a/rollup/proposer-tool-db-config.json b/rollup/proposer-tool-db-config.json new file mode 100644 index 0000000000..b377f879d2 --- /dev/null +++ b/rollup/proposer-tool-db-config.json @@ -0,0 +1,6 @@ +{ + "dsn": "postgres://postgres:postgres@db:5432/scroll?sslmode=disable", + "driver_name": "postgres", + "maxOpenNum": 200, + "maxIdleNum": 20 +} diff --git a/rollup/proposer-tool-genesis.json b/rollup/proposer-tool-genesis.json new file mode 100644 index 0000000000..34aa53082c --- /dev/null +++ b/rollup/proposer-tool-genesis.json @@ -0,0 +1,19 @@ +{ + "config": { + "chainId": 534352, + "bernoulliBlock": 0, + "curieBlock": 0, + "darwinTime": 0, + "darwinV2Time": 0, + "euclidTime": 0, + "euclidV2Time": 0 + }, + "nonce": "0x0000000000000033", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x8000000", + "difficulty": "0x100", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x3333333333333333333333333333333333333333", + "alloc": {} +} diff --git a/rollup/proposer_tool.Dockerfile b/rollup/proposer_tool.Dockerfile new file mode 100644 index 0000000000..2ce3aeb0df --- /dev/null +++ b/rollup/proposer_tool.Dockerfile @@ -0,0 +1,30 @@ +# Download Go dependencies +FROM scrolltech/go-rust-builder:go-1.22-rust-nightly-2023-12-03 as base + +WORKDIR /src +COPY go.work* ./ +COPY ./rollup/go.* ./rollup/ +COPY ./common/go.* ./common/ +COPY ./coordinator/go.* ./coordinator/ +COPY ./database/go.* ./database/ +COPY ./tests/integration-test/go.* ./tests/integration-test/ +COPY ./bridge-history-api/go.* ./bridge-history-api/ +RUN go mod download -x + +# Build proposer_tool +FROM base as builder + +RUN --mount=target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + cd /src/rollup/cmd/proposer_tool/ && CGO_LDFLAGS="-ldl" go build -v -p 4 -o /bin/proposer_tool + +# Pull proposer_tool into a second stage deploy ubuntu container +FROM ubuntu:20.04 + +RUN apt update && apt install vim netcat-openbsd net-tools curl ca-certificates -y + +ENV CGO_LDFLAGS="-ldl" + +COPY --from=builder /bin/proposer_tool /bin/ +WORKDIR /app +ENTRYPOINT ["proposer_tool"] diff --git a/rollup/tests/rollup_test.go b/rollup/tests/rollup_test.go index 597b8e23ef..29c0a031eb 100644 --- a/rollup/tests/rollup_test.go +++ b/rollup/tests/rollup_test.go @@ -95,7 +95,7 @@ func testCommitBatchAndFinalizeBundleCodecV4V5V6(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, @@ -103,7 +103,7 @@ func testCommitBatchAndFinalizeBundleCodecV4V5V6(t *testing.T) { BatchTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, nil) + }, encoding.CodecV4, chainConfig, db, db, nil) bup := watcher.NewBundleProposer(context.Background(), &config.BundleProposerConfig{ MaxBatchNumPerBundle: 1000000, @@ -291,14 +291,14 @@ func testCommitBatchAndFinalizeBundleCodecV7(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV7, chainConfig, db, nil) + }, encoding.CodecV7, chainConfig, db, db, nil, false /* not used by tool */) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, MaxL1CommitCalldataSizePerBatch: 1000000, BatchTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV7, chainConfig, db, nil) + }, encoding.CodecV7, chainConfig, db, db, nil) bup := watcher.NewBundleProposer(context.Background(), &config.BundleProposerConfig{ MaxBatchNumPerBundle: 2, From 8167b75abb60942dfb8389421d89855077433e04 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 15 Apr 2025 18:46:06 +0800 Subject: [PATCH 02/21] tweaks --- rollup/cmd/proposer_tool/app/app.go | 10 +--------- rollup/cmd/rollup_relayer/app/app.go | 2 +- rollup/internal/controller/watcher/batch_proposer.go | 12 ++++++++++++ .../controller/watcher/batch_proposer_test.go | 8 ++++---- .../internal/controller/watcher/bundle_proposer.go | 2 +- .../controller/watcher/bundle_proposer_test.go | 2 +- rollup/internal/controller/watcher/chunk_proposer.go | 10 +++++----- .../controller/watcher/chunk_proposer_test.go | 4 ++-- rollup/proposer-tool-config.json | 8 ++++---- rollup/tests/rollup_test.go | 4 ++-- 10 files changed, 33 insertions(+), 29 deletions(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index 94089c27fa..ca2bf4f7a0 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -88,14 +88,6 @@ func action(ctx *cli.Context) error { return fmt.Errorf("failed to retrieve L2 genesis header: %v", err) } - genesisTime := genesisHeader.Time - currentTime := uint64(time.Now().Unix()) - timeDrift := currentTime - genesisTime - - cfg.L2Config.ChunkProposerConfig.ChunkTimeoutSec += timeDrift - cfg.L2Config.BatchProposerConfig.BatchTimeoutSec += timeDrift - cfg.L2Config.BundleProposerConfig.BundleTimeoutSec += timeDrift - chunk := &encoding.Chunk{ Blocks: []*encoding.Block{{ Header: genesisHeader, @@ -149,7 +141,7 @@ func action(ctx *cli.Context) error { } minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) - chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry, true /* used by tool */) + chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry) batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry) bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry) diff --git a/rollup/cmd/rollup_relayer/app/app.go b/rollup/cmd/rollup_relayer/app/app.go index 171315e2ec..5946caf905 100644 --- a/rollup/cmd/rollup_relayer/app/app.go +++ b/rollup/cmd/rollup_relayer/app/app.go @@ -102,7 +102,7 @@ func action(ctx *cli.Context) error { } minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) - chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, db, registry, false /* not used by tool */) + chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, db, registry) batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, db, registry) bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry) diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go index d33bc70552..6c8de9526e 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -13,6 +13,7 @@ import ( "github.com/scroll-tech/go-ethereum/params" "gorm.io/gorm" + "scroll-tech/common/types" "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/orm" "scroll-tech/rollup/internal/utils" @@ -34,6 +35,7 @@ type BatchProposer struct { maxUncompressedBatchBytesSize uint64 maxChunksPerBatch int + replayMode bool minCodecVersion encoding.CodecVersion chainCfg *params.ChainConfig @@ -80,6 +82,7 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minC gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier, maxUncompressedBatchBytesSize: cfg.MaxUncompressedBatchBytesSize, maxChunksPerBatch: cfg.MaxChunksPerBatch, + replayMode: db != l2BlockDB, minCodecVersion: minCodecVersion, chainCfg: chainCfg, @@ -226,6 +229,15 @@ func (p *BatchProposer) updateDBBatchInfo(batch *encoding.Batch, codecVersion en log.Warn("BatchProposer.UpdateBatchHashInRange update the chunk's batch hash failure", "hash", dbBatch.Hash, "error", dbErr) return dbErr } + if p.replayMode { + // if replayMode is true, it means that the batch is proposed by the proposer tool, set the batch status to types.RollupCommitted + // and commit tx hash to a unique value so that new bundles can be proposed + if dbErr = p.batchOrm.UpdateCommitTxHashAndRollupStatus(p.ctx, dbBatch.Hash, dbBatch.Hash, types.RollupCommitted, dbTX); dbErr != nil { + log.Warn("BatchProposer.UpdateCommitTxHashAndRollupStatus update the batch's commit tx hash failure", "hash", dbBatch.Hash, "error", dbErr) + return dbErr + } + } + return nil }) if err != nil { diff --git a/rollup/internal/controller/watcher/batch_proposer_test.go b/rollup/internal/controller/watcher/batch_proposer_test.go index 0a1111c76a..89987114fc 100644 --- a/rollup/internal/controller/watcher/batch_proposer_test.go +++ b/rollup/internal/controller/watcher/batch_proposer_test.go @@ -124,7 +124,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) { CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64), - }, db, db, nil, false /* not used by tool */) + }, db, db, nil) cp.TryProposeChunk() // chunk1 contains block1 cp.TryProposeChunk() // chunk2 contains block2 @@ -214,7 +214,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) { ChunkTimeoutSec: 300, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) cp.TryProposeChunk() // chunk1 contains block1 cp.TryProposeChunk() // chunk2 contains block2 @@ -301,7 +301,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, chainConfig, db, db, nil) blockHeight := int64(0) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") @@ -397,7 +397,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, chainConfig, db, db, nil) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") for blockHeight := int64(1); blockHeight <= 60; blockHeight++ { diff --git a/rollup/internal/controller/watcher/bundle_proposer.go b/rollup/internal/controller/watcher/bundle_proposer.go index 9a150e2bf0..bb180cd61e 100644 --- a/rollup/internal/controller/watcher/bundle_proposer.go +++ b/rollup/internal/controller/watcher/bundle_proposer.go @@ -199,7 +199,7 @@ func (p *BundleProposer) proposeBundle() error { currentTimeSec := uint64(time.Now().Unix()) if firstChunk.StartBlockTime+p.bundleTimeoutSec < currentTimeSec { - log.Info("first block timeout", "batch count", len(batches), "start block number", firstChunk.StartBlockNumber, "start block timestamp", firstChunk.StartBlockTime, "current time", currentTimeSec) + log.Info("first block timeout", "batch count", len(batches), "start block number", firstChunk.StartBlockNumber, "start block timestamp", firstChunk.StartBlockTime, "bundle timeout", p.bundleTimeoutSec, "current time", currentTimeSec) batches, err = p.allBatchesCommittedInSameTXIncluded(batches) if err != nil { diff --git a/rollup/internal/controller/watcher/bundle_proposer_test.go b/rollup/internal/controller/watcher/bundle_proposer_test.go index 08242f2b5e..47cbb44100 100644 --- a/rollup/internal/controller/watcher/bundle_proposer_test.go +++ b/rollup/internal/controller/watcher/bundle_proposer_test.go @@ -103,7 +103,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, chainConfig, db, db, nil) bap := NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: math.MaxUint64, diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index c90ad53bc1..c924f02311 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -36,7 +36,7 @@ type ChunkProposer struct { gasCostIncreaseMultiplier float64 maxUncompressedBatchBytesSize uint64 - toolFlag bool + replayMode bool minCodecVersion encoding.CodecVersion chainCfg *params.ChainConfig @@ -65,7 +65,7 @@ type ChunkProposer struct { } // NewChunkProposer creates a new ChunkProposer instance. -func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, db *gorm.DB, reg prometheus.Registerer, toolFlag bool) *ChunkProposer { +func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, db *gorm.DB, reg prometheus.Registerer) *ChunkProposer { log.Info("new chunk proposer", "maxBlockNumPerChunk", cfg.MaxBlockNumPerChunk, "maxTxNumPerChunk", cfg.MaxTxNumPerChunk, @@ -92,7 +92,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC chunkTimeoutSec: cfg.ChunkTimeoutSec, gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier, maxUncompressedBatchBytesSize: cfg.MaxUncompressedBatchBytesSize, - toolFlag: toolFlag, + replayMode: l2BlockDB != db, minCodecVersion: minCodecVersion, chainCfg: chainCfg, @@ -243,7 +243,7 @@ func (p *ChunkProposer) updateDBChunkInfo(chunk *encoding.Chunk, codecVersion en log.Warn("ChunkProposer.InsertChunk failed", "codec version", codecVersion, "err", err) return err } - if !p.toolFlag { + if !p.replayMode { if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil { log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", dbChunk.Hash, "start block", dbChunk.StartBlockNumber, "end block", dbChunk.EndBlockNumber, "err", err) return err @@ -445,7 +445,7 @@ func (p *ChunkProposer) tryProposeEuclidTransitionChunk(blocks []*encoding.Block } prevBlocks, err := p.l2BlockOrm.GetL2BlocksGEHeight(p.ctx, blocks[0].Header.Number.Uint64()-1, 1) - if !p.toolFlag && (err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash) { + if !p.replayMode && (err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash) { return false, fmt.Errorf("failed to get parent block: %w", err) } diff --git a/rollup/internal/controller/watcher/chunk_proposer_test.go b/rollup/internal/controller/watcher/chunk_proposer_test.go index 3414bab4d7..ffcefef27a 100644 --- a/rollup/internal/controller/watcher/chunk_proposer_test.go +++ b/rollup/internal/controller/watcher/chunk_proposer_test.go @@ -202,7 +202,7 @@ func testChunkProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: tt.chunkTimeoutSec, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) cp.TryProposeChunk() chunkOrm := orm.NewChunk(db) @@ -253,7 +253,7 @@ func testChunkProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, chainConfig, db, db, nil) for i := 0; i < 2; i++ { cp.TryProposeChunk() diff --git a/rollup/proposer-tool-config.json b/rollup/proposer-tool-config.json index 46d06903c3..6c0fa7e198 100644 --- a/rollup/proposer-tool-config.json +++ b/rollup/proposer-tool-config.json @@ -7,22 +7,22 @@ "max_l2_gas_per_chunk": 20000000, "max_l1_commit_gas_per_chunk": 5000000, "max_l1_commit_calldata_size_per_chunk": 123740, - "chunk_timeout_sec": 720000, - "max_row_consumption_per_chunk": 1000000, + "chunk_timeout_sec": 72000000000, + "max_row_consumption_per_chunk": 10000000000, "gas_cost_increase_multiplier": 1.2, "max_uncompressed_batch_bytes_size": 634693 }, "batch_proposer_config": { "max_l1_commit_gas_per_batch": 5000000, "max_l1_commit_calldata_size_per_batch": 123740, - "batch_timeout_sec": 720000, + "batch_timeout_sec": 72000000000, "gas_cost_increase_multiplier": 1.2, "max_uncompressed_batch_bytes_size": 634693, "max_chunks_per_batch": 45 }, "bundle_proposer_config": { "max_batch_num_per_bundle": 45, - "bundle_timeout_sec": 3600 + "bundle_timeout_sec": 36000000000 } }, "db_config": { diff --git a/rollup/tests/rollup_test.go b/rollup/tests/rollup_test.go index 29c0a031eb..a341a42d88 100644 --- a/rollup/tests/rollup_test.go +++ b/rollup/tests/rollup_test.go @@ -95,7 +95,7 @@ func testCommitBatchAndFinalizeBundleCodecV4V5V6(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV4, chainConfig, db, db, nil) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, @@ -291,7 +291,7 @@ func testCommitBatchAndFinalizeBundleCodecV7(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV7, chainConfig, db, db, nil, false /* not used by tool */) + }, encoding.CodecV7, chainConfig, db, db, nil) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, From 8112a12baafcc4410ed3b31d4d3906f294c0b0e9 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 15 Apr 2025 18:54:45 +0800 Subject: [PATCH 03/21] fix goimport --- rollup/internal/controller/watcher/batch_proposer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go index 6c8de9526e..ea49b33fca 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -14,6 +14,7 @@ import ( "gorm.io/gorm" "scroll-tech/common/types" + "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/orm" "scroll-tech/rollup/internal/utils" From 36690d145b4e76ff2a8e8e953c2d779ae81cb189 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 15 Apr 2025 20:28:53 +0800 Subject: [PATCH 04/21] tweaks --- rollup/README.md | 2 +- rollup/cmd/proposer_tool/app/app.go | 6 +++--- rollup/internal/controller/watcher/batch_proposer.go | 4 ++-- rollup/internal/controller/watcher/chunk_proposer.go | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rollup/README.md b/rollup/README.md index 7f912dc5fc..4804352714 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -41,7 +41,7 @@ make rollup_bins 1. Set genesis config to enable desired hardforks in [`proposer-tool-genesis.json`](./proposer-tool-genesis.json). 2. Set proposer config in [`proposer-tool-config.json`](./proposer-tool-config.json) for data analysis. -### Start the proposer tool using docker-compose. +### Start the proposer tool using docker-compose ``` cd rollup diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index ca2bf4f7a0..fb8d1b0a44 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -65,15 +65,15 @@ func action(ctx *cli.Context) error { } }() - // Init l2BlockOrm connection + // Init dbForReplay connection dbForReplay, err := database.InitDB(cfg.DBConfigForReplay) if err != nil { - log.Crit("failed to init l2BlockOrm connection", "err", err) + log.Crit("failed to init dbForReplay connection", "err", err) } defer func() { cancel() if err = database.CloseDB(dbForReplay); err != nil { - log.Crit("failed to close l2BlockOrm connection", "error", err) + log.Crit("failed to close dbForReplay connection", "error", err) } }() diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go index ea49b33fca..6f9cff567c 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -231,8 +231,8 @@ func (p *BatchProposer) updateDBBatchInfo(batch *encoding.Batch, codecVersion en return dbErr } if p.replayMode { - // if replayMode is true, it means that the batch is proposed by the proposer tool, set the batch status to types.RollupCommitted - // and commit tx hash to a unique value so that new bundles can be proposed + // If replayMode is true, meaning the batch was proposed by the proposer tool, + // set batch status to types.RollupCommitted and assign a unique commit tx hash to enable new bundle proposals. if dbErr = p.batchOrm.UpdateCommitTxHashAndRollupStatus(p.ctx, dbBatch.Hash, dbBatch.Hash, types.RollupCommitted, dbTX); dbErr != nil { log.Warn("BatchProposer.UpdateCommitTxHashAndRollupStatus update the batch's commit tx hash failure", "hash", dbBatch.Hash, "error", dbErr) return dbErr diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index c924f02311..17d43f0170 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -243,9 +243,10 @@ func (p *ChunkProposer) updateDBChunkInfo(chunk *encoding.Chunk, codecVersion en log.Warn("ChunkProposer.InsertChunk failed", "codec version", codecVersion, "err", err) return err } + // In replayMode we don't need to update chunk_hash in l2_block table. if !p.replayMode { if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil { - log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", dbChunk.Hash, "start block", dbChunk.StartBlockNumber, "end block", dbChunk.EndBlockNumber, "err", err) + log.Error("failed to update chunk_hash for l2_block", "chunk hash", dbChunk.Hash, "start block", dbChunk.StartBlockNumber, "end block", dbChunk.EndBlockNumber, "err", err) return err } } From d12c2b439f57164dfa7ca41667dfd7236c7dfbce Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 17 Apr 2025 22:16:00 +0800 Subject: [PATCH 05/21] add descriptions of proposer tool --- rollup/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rollup/README.md b/rollup/README.md index 4804352714..0df4670d42 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -34,6 +34,16 @@ make rollup_bins ./build/bin/rollup_relayer --config ./conf/config.json ``` +# Proposer Tool + +The Proposer Tool replays historical blocks with custom configurations (e.g., future hardfork configs, customized chunk/batch/bundle proposer configs) to generate chunks/batches/bundles, helping test parameter changes before protocol upgrade. + +You can: + +1. Enable different hardforks in the genesis configuration. +2. Set custom chunk-proposer, batch-proposer, and bundle-proposer parameters. +3. Analyze resulting metrics (blob size, block count, transaction count, gas usage). + ## How to run the proposer tool? ### Set the configs From 0836a1d78f357fc595fcbdee93db658a2811303e Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 17 Apr 2025 22:53:08 +0800 Subject: [PATCH 06/21] remove db-client --- database/cmd/app/client.go | 2 +- rollup/cmd/proposer_tool/app/app.go | 9 +++++++++ rollup/docker-compose-proposer-tool.yml | 15 ++------------- rollup/proposer-tool-db-config.json | 6 ------ 4 files changed, 12 insertions(+), 20 deletions(-) delete mode 100644 rollup/proposer-tool-db-config.json diff --git a/database/cmd/app/client.go b/database/cmd/app/client.go index 68c8fc0009..bcd0c9246a 100644 --- a/database/cmd/app/client.go +++ b/database/cmd/app/client.go @@ -42,7 +42,7 @@ func resetDB(ctx *cli.Context) error { } var version int64 - err = migrate.ResetDB(db.DB) + err = migrate.Rollback(db.DB, &version) if err != nil { return err } diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index fb8d1b0a44..a851cef48e 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -20,6 +20,7 @@ import ( "scroll-tech/common/types" "scroll-tech/common/utils" "scroll-tech/common/version" + "scroll-tech/database/migrate" "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/controller/watcher" @@ -58,6 +59,14 @@ func action(ctx *cli.Context) error { if err != nil { log.Crit("failed to init db connection", "err", err) } + sqlDB, err := db.DB() + if err != nil { + log.Crit("failed to get db connection", "error", err) + } + if err = migrate.ResetDB(sqlDB); err != nil { + log.Crit("failed to reset db", "error", err) + } + log.Info("successfully reset db") defer func() { cancel() if err = database.CloseDB(db); err != nil { diff --git a/rollup/docker-compose-proposer-tool.yml b/rollup/docker-compose-proposer-tool.yml index dbe37965c8..e048453ae9 100644 --- a/rollup/docker-compose-proposer-tool.yml +++ b/rollup/docker-compose-proposer-tool.yml @@ -17,24 +17,13 @@ services: timeout: 5s retries: 5 - db-client: - build: - context: .. - dockerfile: ./build/dockerfiles/db_cli.Dockerfile - depends_on: - db: - condition: service_healthy - command: ["reset", "--config", "/app/conf/proposer-tool-db-config.json"] - volumes: - - ./proposer-tool-db-config.json:/app/conf/proposer-tool-db-config.json - proposer-tool: build: context: .. dockerfile: ./rollup/proposer_tool.Dockerfile depends_on: - db-client: - condition: service_completed_successfully + db: + condition: service_healthy command: [ "--config", "/app/conf/proposer-tool-config.json", "--genesis", "/app/conf/proposer-tool-genesis.json", diff --git a/rollup/proposer-tool-db-config.json b/rollup/proposer-tool-db-config.json deleted file mode 100644 index b377f879d2..0000000000 --- a/rollup/proposer-tool-db-config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dsn": "postgres://postgres:postgres@db:5432/scroll?sslmode=disable", - "driver_name": "postgres", - "maxOpenNum": 200, - "maxIdleNum": 20 -} From 9fc3ad51acb70ce7458784d87e13a3ed1d3499d1 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 17 Apr 2025 22:54:34 +0800 Subject: [PATCH 07/21] address AI's comment --- rollup/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/README.md b/rollup/README.md index 0df4670d42..19cc229392 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -34,7 +34,7 @@ make rollup_bins ./build/bin/rollup_relayer --config ./conf/config.json ``` -# Proposer Tool +## Proposer Tool The Proposer Tool replays historical blocks with custom configurations (e.g., future hardfork configs, customized chunk/batch/bundle proposer configs) to generate chunks/batches/bundles, helping test parameter changes before protocol upgrade. From 78cf923fa1b742868c14971cac8c7978f032dbeb Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 18 Apr 2025 01:31:43 +0800 Subject: [PATCH 08/21] support start from a specific l2 block --- common/utils/flags.go | 10 ++++ rollup/README.md | 3 ++ rollup/cmd/proposer_tool/app/app.go | 37 ++++++++------- rollup/docker-compose-proposer-tool.yml | 1 + rollup/internal/orm/chunk.go | 62 +++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 16 deletions(-) diff --git a/common/utils/flags.go b/common/utils/flags.go index b2221ffddc..f1fdb2a5b9 100644 --- a/common/utils/flags.go +++ b/common/utils/flags.go @@ -22,6 +22,10 @@ var ( RollupRelayerFlags = []cli.Flag{ &MinCodecVersionFlag, } + // ProposerToolFlags contains flags only used in proposer tool + ProposerToolFlags = []cli.Flag{ + &StartL2BlockFlag, + } // ConfigFileFlag load json type config file. ConfigFileFlag = cli.StringFlag{ Name: "config", @@ -90,4 +94,10 @@ var ( Usage: "Minimum required codec version for the chunk/batch/bundle proposers", Required: true, } + // StartL2BlockFlag indicates the start L2 block number for proposer tool + StartL2BlockFlag = cli.Uint64Flag{ + Name: "start-l2-block", + Usage: "Start L2 block number for proposer tool", + Value: 0, + } ) diff --git a/rollup/README.md b/rollup/README.md index 19cc229392..6bca724aa8 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -50,9 +50,12 @@ You can: 1. Set genesis config to enable desired hardforks in [`proposer-tool-genesis.json`](./proposer-tool-genesis.json). 2. Set proposer config in [`proposer-tool-config.json`](./proposer-tool-config.json) for data analysis. +3. Set `start-l2-block` in the launch command of proposer-tool in [`docker-compose-proposer-tool.yml`](./docker-compose-proposer-tool.yml) to the block number you want to start from. The default is `0`, which means starting from the genesis block. ### Start the proposer tool using docker-compose +Prerequisite: an RPC URL to an archive L2 node. The default url in [`proposer-tool-config.json`](./proposer-tool-config.json) is `https://rpc.scroll.io`. + ``` cd rollup DOCKER_BUILDKIT=1 docker-compose -f docker-compose-proposer-tool.yml up -d diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index a851cef48e..4b0c79b019 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -17,7 +17,6 @@ import ( "github.com/urfave/cli/v2" "scroll-tech/common/database" - "scroll-tech/common/types" "scroll-tech/common/utils" "scroll-tech/common/version" "scroll-tech/database/migrate" @@ -39,6 +38,7 @@ func init() { app.Version = version.Version app.Flags = append(app.Flags, utils.CommonFlags...) app.Flags = append(app.Flags, utils.RollupRelayerFlags...) + app.Flags = append(app.Flags, utils.ProposerToolFlags...) app.Commands = []*cli.Command{} app.Before = func(ctx *cli.Context) error { return utils.LogSetup(ctx) @@ -92,28 +92,33 @@ func action(ctx *cli.Context) error { log.Crit("failed to connect l2 geth", "config file", cfgFile, "error", err) } - genesisHeader, err := l2Client.HeaderByNumber(subCtx, big.NewInt(0)) + startL2BlockHeight := ctx.Uint64(utils.StartL2BlockFlag.Name) + startL2Block, err := l2Client.BlockByNumber(context.Background(), big.NewInt(int64(startL2BlockHeight))) if err != nil { - return fmt.Errorf("failed to retrieve L2 genesis header: %v", err) + log.Crit("failed to get start l2 block", "startL2BlockHeight", startL2BlockHeight, "error", err) } - chunk := &encoding.Chunk{ - Blocks: []*encoding.Block{{ - Header: genesisHeader, - Transactions: nil, - WithdrawRoot: common.Hash{}, - RowConsumption: &gethTypes.RowConsumption{}, - }}, - } + chunk := &encoding.Chunk{Blocks: []*encoding.Block{{Header: startL2Block.Header()}}} - var dbChunk *orm.Chunk - dbChunk, err = orm.NewChunk(db).InsertChunk(subCtx, chunk, encoding.CodecV0, rutils.ChunkMetrics{}) + prevChunk, err := orm.NewChunk(dbForReplay).GetParentChunkByBlockNumber(subCtx, startL2BlockHeight) if err != nil { - log.Crit("failed to insert chunk", "error", err) + log.Crit("failed to get previous chunk", "error", err) } - if err = orm.NewChunk(db).UpdateProvingStatus(subCtx, dbChunk.Hash, types.ProvingTaskVerified); err != nil { - log.Crit("failed to update genesis chunk proving status", "error", err) + var startQueueIndex uint64 + if prevChunk != nil { + startQueueIndex = prevChunk.TotalL1MessagesPoppedBefore + prevChunk.TotalL1MessagesPoppedInChunk + } + + for _, tx := range startL2Block.Transactions() { + if tx.Type() == gethTypes.L1MessageTxType { + startQueueIndex++ + } + } + + _, err = orm.NewChunk(db).InsertTestChunkForProposerTool(subCtx, chunk, encoding.CodecV0, startQueueIndex) + if err != nil { + log.Crit("failed to insert chunk", "error", err) } batch := &encoding.Batch{ diff --git a/rollup/docker-compose-proposer-tool.yml b/rollup/docker-compose-proposer-tool.yml index e048453ae9..3d4d4a723c 100644 --- a/rollup/docker-compose-proposer-tool.yml +++ b/rollup/docker-compose-proposer-tool.yml @@ -28,6 +28,7 @@ services: "--config", "/app/conf/proposer-tool-config.json", "--genesis", "/app/conf/proposer-tool-genesis.json", "--min-codec-version", "4", + "--start-l2-block", "10000", "--log.debug", "--verbosity", "3" ] volumes: diff --git a/rollup/internal/orm/chunk.go b/rollup/internal/orm/chunk.go index 4ad45176fc..0e5b5a5146 100644 --- a/rollup/internal/orm/chunk.go +++ b/rollup/internal/orm/chunk.go @@ -179,6 +179,25 @@ func (o *Chunk) GetChunksByBatchHash(ctx context.Context, batchHash string) ([]* return chunks, nil } +// GetParentChunkByBlockNumber retrieves the parent chunk by block number +// for test +func (o *Chunk) GetParentChunkByBlockNumber(ctx context.Context, blockNumber uint64) (*Chunk, error) { + db := o.db.WithContext(ctx) + db = db.Model(&Chunk{}) + db = db.Where("end_block_number < ?", blockNumber) + db = db.Order("end_block_number DESC") + db = db.Limit(1) + + var chunk Chunk + if err := db.First(&chunk).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, fmt.Errorf("Chunk.GetParentChunkByBlockNumber error: %w", err) + } + return &chunk, nil +} + // InsertChunk inserts a new chunk into the database. func (o *Chunk) InsertChunk(ctx context.Context, chunk *encoding.Chunk, codecVersion encoding.CodecVersion, metrics rutils.ChunkMetrics, dbTX ...*gorm.DB) (*Chunk, error) { if chunk == nil || len(chunk.Blocks) == 0 { @@ -259,6 +278,49 @@ func (o *Chunk) InsertChunk(ctx context.Context, chunk *encoding.Chunk, codecVer return &newChunk, nil } +// InsertTestChunkForProposerTool inserts a new chunk into the database only for testing purposes in the proposer tool. +func (o *Chunk) InsertTestChunkForProposerTool(ctx context.Context, chunk *encoding.Chunk, codecVersion encoding.CodecVersion, totalL1MessagePoppedBefore uint64, dbTX ...*gorm.DB) (*Chunk, error) { + if chunk == nil || len(chunk.Blocks) == 0 { + return nil, errors.New("invalid args") + } + + chunkHash, err := rutils.GetChunkHash(chunk, totalL1MessagePoppedBefore, codecVersion) + if err != nil { + log.Error("failed to get chunk hash", "err", err) + return nil, fmt.Errorf("Chunk.InsertChunk error: %w", err) + } + + numBlocks := len(chunk.Blocks) + newChunk := Chunk{ + Index: 0, + Hash: chunkHash.Hex(), + StartBlockNumber: chunk.Blocks[0].Header.Number.Uint64(), + StartBlockHash: chunk.Blocks[0].Header.Hash().Hex(), + EndBlockNumber: chunk.Blocks[numBlocks-1].Header.Number.Uint64(), + EndBlockHash: chunk.Blocks[numBlocks-1].Header.Hash().Hex(), + TotalL2TxGas: chunk.TotalGasUsed(), + TotalL2TxNum: chunk.NumL2Transactions(), + StartBlockTime: chunk.Blocks[0].Header.Time, + TotalL1MessagesPoppedBefore: totalL1MessagePoppedBefore, + StateRoot: chunk.Blocks[numBlocks-1].Header.Root.Hex(), + WithdrawRoot: chunk.Blocks[numBlocks-1].WithdrawRoot.Hex(), + CodecVersion: int16(codecVersion), + } + + db := o.db + if len(dbTX) > 0 && dbTX[0] != nil { + db = dbTX[0] + } + db = db.WithContext(ctx) + db = db.Model(&Chunk{}) + + if err := db.Create(&newChunk).Error; err != nil { + return nil, fmt.Errorf("Chunk.InsertChunk error: %w, chunk hash: %v", err, newChunk.Hash) + } + + return &newChunk, nil +} + // UpdateProvingStatus updates the proving status of a chunk. func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error { updateFields := make(map[string]interface{}) From c0138d2b6aaaa0bb0f7958c5b8dbc5d44d507327 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 18 Apr 2025 13:04:16 +0800 Subject: [PATCH 09/21] tweak comments --- rollup/internal/orm/chunk.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rollup/internal/orm/chunk.go b/rollup/internal/orm/chunk.go index 0e5b5a5146..69df7078e8 100644 --- a/rollup/internal/orm/chunk.go +++ b/rollup/internal/orm/chunk.go @@ -180,7 +180,7 @@ func (o *Chunk) GetChunksByBatchHash(ctx context.Context, batchHash string) ([]* } // GetParentChunkByBlockNumber retrieves the parent chunk by block number -// for test +// only used by proposer tool for analysis usage func (o *Chunk) GetParentChunkByBlockNumber(ctx context.Context, blockNumber uint64) (*Chunk, error) { db := o.db.WithContext(ctx) db = db.Model(&Chunk{}) @@ -278,7 +278,7 @@ func (o *Chunk) InsertChunk(ctx context.Context, chunk *encoding.Chunk, codecVer return &newChunk, nil } -// InsertTestChunkForProposerTool inserts a new chunk into the database only for testing purposes in the proposer tool. +// InsertTestChunkForProposerTool inserts a new chunk into the database only for analysis usage by proposer tool. func (o *Chunk) InsertTestChunkForProposerTool(ctx context.Context, chunk *encoding.Chunk, codecVersion encoding.CodecVersion, totalL1MessagePoppedBefore uint64, dbTX ...*gorm.DB) (*Chunk, error) { if chunk == nil || len(chunk.Blocks) == 0 { return nil, errors.New("invalid args") From be3f189715eabf54286be33fde3f2bf1e9833b14 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 19:03:11 +0800 Subject: [PATCH 10/21] address comments --- rollup/cmd/proposer_tool/app/app.go | 32 +++++++++++++++++++++-------- rollup/internal/orm/chunk.go | 16 ++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index 4b0c79b019..d085c7e0ab 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -93,12 +93,6 @@ func action(ctx *cli.Context) error { } startL2BlockHeight := ctx.Uint64(utils.StartL2BlockFlag.Name) - startL2Block, err := l2Client.BlockByNumber(context.Background(), big.NewInt(int64(startL2BlockHeight))) - if err != nil { - log.Crit("failed to get start l2 block", "startL2BlockHeight", startL2BlockHeight, "error", err) - } - - chunk := &encoding.Chunk{Blocks: []*encoding.Block{{Header: startL2Block.Header()}}} prevChunk, err := orm.NewChunk(dbForReplay).GetParentChunkByBlockNumber(subCtx, startL2BlockHeight) if err != nil { @@ -110,12 +104,32 @@ func action(ctx *cli.Context) error { startQueueIndex = prevChunk.TotalL1MessagesPoppedBefore + prevChunk.TotalL1MessagesPoppedInChunk } - for _, tx := range startL2Block.Transactions() { - if tx.Type() == gethTypes.L1MessageTxType { - startQueueIndex++ + startBlock := uint64(0) + if prevChunk != nil { + startBlock = prevChunk.EndBlockNumber + 1 + } + + var chunk *encoding.Chunk + for blockNum := startBlock; blockNum <= startL2BlockHeight; blockNum++ { + block, err := l2Client.BlockByNumber(context.Background(), big.NewInt(int64(blockNum))) + if err != nil { + log.Crit("failed to get block", "block number", blockNum, "error", err) + } + + for _, tx := range block.Transactions() { + if tx.Type() == gethTypes.L1MessageTxType { + startQueueIndex++ + } + } + + if blockNum == startL2BlockHeight { + chunk = &encoding.Chunk{Blocks: []*encoding.Block{{Header: block.Header()}}} } } + // Setting empty hash as the post_l1_message_queue_hash of the first chunk, + // i.e., treating the first L1 message after this chunk as the first L1 message in message queue v2. + // Though this setting is different from mainnet, it's simple yet sufficient for data analysis usage. _, err = orm.NewChunk(db).InsertTestChunkForProposerTool(subCtx, chunk, encoding.CodecV0, startQueueIndex) if err != nil { log.Crit("failed to insert chunk", "error", err) diff --git a/rollup/internal/orm/chunk.go b/rollup/internal/orm/chunk.go index 69df7078e8..f8f6c0da08 100644 --- a/rollup/internal/orm/chunk.go +++ b/rollup/internal/orm/chunk.go @@ -291,19 +291,21 @@ func (o *Chunk) InsertTestChunkForProposerTool(ctx context.Context, chunk *encod } numBlocks := len(chunk.Blocks) + firstBlock := chunk.Blocks[0] + lastBlock := chunk.Blocks[numBlocks-1] newChunk := Chunk{ Index: 0, Hash: chunkHash.Hex(), - StartBlockNumber: chunk.Blocks[0].Header.Number.Uint64(), - StartBlockHash: chunk.Blocks[0].Header.Hash().Hex(), - EndBlockNumber: chunk.Blocks[numBlocks-1].Header.Number.Uint64(), - EndBlockHash: chunk.Blocks[numBlocks-1].Header.Hash().Hex(), + StartBlockNumber: firstBlock.Header.Number.Uint64(), + StartBlockHash: firstBlock.Header.Hash().Hex(), + EndBlockNumber: lastBlock.Header.Number.Uint64(), + EndBlockHash: lastBlock.Header.Hash().Hex(), TotalL2TxGas: chunk.TotalGasUsed(), TotalL2TxNum: chunk.NumL2Transactions(), - StartBlockTime: chunk.Blocks[0].Header.Time, + StartBlockTime: firstBlock.Header.Time, TotalL1MessagesPoppedBefore: totalL1MessagePoppedBefore, - StateRoot: chunk.Blocks[numBlocks-1].Header.Root.Hex(), - WithdrawRoot: chunk.Blocks[numBlocks-1].WithdrawRoot.Hex(), + StateRoot: lastBlock.Header.Root.Hex(), + WithdrawRoot: lastBlock.WithdrawRoot.Hex(), CodecVersion: int16(codecVersion), } From 818add4fdc1052b92b070a4704e781a62e6db924 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 19:31:33 +0800 Subject: [PATCH 11/21] address comments --- rollup/cmd/proposer_tool/app/app.go | 6 ++++-- rollup/cmd/rollup_relayer/app/app.go | 4 ++-- .../controller/watcher/batch_proposer.go | 14 +++++++++++--- .../controller/watcher/batch_proposer_test.go | 16 ++++++++-------- .../controller/watcher/bundle_proposer_test.go | 4 ++-- .../controller/watcher/chunk_proposer.go | 14 +++++++++++--- .../controller/watcher/chunk_proposer_test.go | 4 ++-- rollup/tests/rollup_test.go | 8 ++++---- 8 files changed, 44 insertions(+), 26 deletions(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index d085c7e0ab..a4e3596dd3 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -169,8 +169,10 @@ func action(ctx *cli.Context) error { } minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) - chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry) - batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, dbForReplay, db, registry) + chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, registry) + chunkProposer.SetReplayDB(dbForReplay) + batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, registry) + batchProposer.SetReplayDB(dbForReplay) bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry) go utils.Loop(subCtx, 100*time.Millisecond, chunkProposer.TryProposeChunk) diff --git a/rollup/cmd/rollup_relayer/app/app.go b/rollup/cmd/rollup_relayer/app/app.go index 5946caf905..d6d1e8a763 100644 --- a/rollup/cmd/rollup_relayer/app/app.go +++ b/rollup/cmd/rollup_relayer/app/app.go @@ -102,8 +102,8 @@ func action(ctx *cli.Context) error { } minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) - chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, db, registry) - batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, db, registry) + 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 6f9cff567c..bb08e885c8 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -62,7 +62,7 @@ type BatchProposer struct { } // NewBatchProposer creates a new BatchProposer instance. -func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, 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, @@ -76,14 +76,14 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minC db: db, batchOrm: orm.NewBatch(db), chunkOrm: orm.NewChunk(db), - l2BlockOrm: orm.NewL2Block(l2BlockDB), + l2BlockOrm: orm.NewL2Block(db), maxL1CommitGasPerBatch: cfg.MaxL1CommitGasPerBatch, maxL1CommitCalldataSizePerBatch: cfg.MaxL1CommitCalldataSizePerBatch, batchTimeoutSec: cfg.BatchTimeoutSec, gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier, maxUncompressedBatchBytesSize: cfg.MaxUncompressedBatchBytesSize, maxChunksPerBatch: cfg.MaxChunksPerBatch, - replayMode: db != l2BlockDB, + replayMode: false, minCodecVersion: minCodecVersion, chainCfg: chainCfg, @@ -156,6 +156,14 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, minC return p } +// SetReplayDB sets the replay database for the BatchProposer. +// This is used for the proposer tool only, to change the l2_block data source. +// This function is not thread-safe and should be called after initializing the BatchProposer and before starting to propose chunks. +func (p *BatchProposer) SetReplayDB(replayDB *gorm.DB) { + p.l2BlockOrm = orm.NewL2Block(replayDB) + p.replayMode = true +} + // TryProposeBatch tries to propose a new batches. func (p *BatchProposer) TryProposeBatch() { p.batchProposerCircleTotal.Inc() diff --git a/rollup/internal/controller/watcher/batch_proposer_test.go b/rollup/internal/controller/watcher/batch_proposer_test.go index 89987114fc..9bd03fde4a 100644 --- a/rollup/internal/controller/watcher/batch_proposer_test.go +++ b/rollup/internal/controller/watcher/batch_proposer_test.go @@ -124,7 +124,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) { CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64), - }, db, db, nil) + }, db, nil) cp.TryProposeChunk() // chunk1 contains block1 cp.TryProposeChunk() // chunk2 contains block2 @@ -148,7 +148,7 @@ func testBatchProposerLimitsCodecV4(t *testing.T) { CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64), - }, db, db, nil) + }, db, nil) bp.TryProposeBatch() batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0) @@ -214,7 +214,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) { ChunkTimeoutSec: 300, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) + }, encoding.CodecV4, ¶ms.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 @@ -232,7 +232,7 @@ func testBatchCommitGasAndCalldataSizeEstimationCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) + }, encoding.CodecV4, ¶ms.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) @@ -301,7 +301,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) blockHeight := int64(0) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") @@ -323,7 +323,7 @@ func testBatchProposerBlobSizeLimitCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) for i := 0; i < 2; i++ { bp.TryProposeBatch() @@ -397,7 +397,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) { ChunkTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) block = readBlockFromJSON(t, "../../../testdata/blockTrace_03.json") for blockHeight := int64(1); blockHeight <= 60; blockHeight++ { @@ -414,7 +414,7 @@ func testBatchProposerMaxChunkNumPerBatchLimitCodecV4(t *testing.T) { GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, 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_test.go b/rollup/internal/controller/watcher/bundle_proposer_test.go index 47cbb44100..50f39fdb38 100644 --- a/rollup/internal/controller/watcher/bundle_proposer_test.go +++ b/rollup/internal/controller/watcher/bundle_proposer_test.go @@ -103,7 +103,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) bap := NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: math.MaxUint64, @@ -111,7 +111,7 @@ func testBundleProposerLimitsCodecV4(t *testing.T) { BatchTimeoutSec: 0, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) cp.TryProposeChunk() // chunk1 contains block1 bap.TryProposeBatch() // batch1 contains chunk1 diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index 17d43f0170..a5ee896e3d 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -65,7 +65,7 @@ type ChunkProposer struct { } // NewChunkProposer creates a new ChunkProposer instance. -func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig, l2BlockDB, 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, @@ -82,7 +82,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC ctx: ctx, db: db, chunkOrm: orm.NewChunk(db), - l2BlockOrm: orm.NewL2Block(l2BlockDB), + l2BlockOrm: orm.NewL2Block(db), maxBlockNumPerChunk: cfg.MaxBlockNumPerChunk, maxTxNumPerChunk: cfg.MaxTxNumPerChunk, maxL2GasPerChunk: cfg.MaxL2GasPerChunk, @@ -92,7 +92,7 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC chunkTimeoutSec: cfg.ChunkTimeoutSec, gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier, maxUncompressedBatchBytesSize: cfg.MaxUncompressedBatchBytesSize, - replayMode: l2BlockDB != db, + replayMode: false, minCodecVersion: minCodecVersion, chainCfg: chainCfg, @@ -177,6 +177,14 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, minC return p } +// SetReplayDB sets the replay database for the ChunkProposer. +// This is used for the proposer tool only, to change the l2_block data source. +// This function is not thread-safe and should be called after initializing the ChunkProposer and before starting to propose chunks. +func (p *ChunkProposer) SetReplayDB(replayDB *gorm.DB) { + p.l2BlockOrm = orm.NewL2Block(replayDB) + p.replayMode = true +} + // TryProposeChunk tries to propose a new chunk. func (p *ChunkProposer) TryProposeChunk() { p.chunkProposerCircleTotal.Inc() diff --git a/rollup/internal/controller/watcher/chunk_proposer_test.go b/rollup/internal/controller/watcher/chunk_proposer_test.go index ffcefef27a..974db79b06 100644 --- a/rollup/internal/controller/watcher/chunk_proposer_test.go +++ b/rollup/internal/controller/watcher/chunk_proposer_test.go @@ -202,7 +202,7 @@ func testChunkProposerLimitsCodecV4(t *testing.T) { ChunkTimeoutSec: tt.chunkTimeoutSec, GasCostIncreaseMultiplier: 1.2, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, db, db, nil) + }, encoding.CodecV4, ¶ms.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) @@ -253,7 +253,7 @@ func testChunkProposerBlobSizeLimitCodecV4(t *testing.T) { ChunkTimeoutSec: math.MaxUint32, GasCostIncreaseMultiplier: 1, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, 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 a341a42d88..597b8e23ef 100644 --- a/rollup/tests/rollup_test.go +++ b/rollup/tests/rollup_test.go @@ -95,7 +95,7 @@ func testCommitBatchAndFinalizeBundleCodecV4V5V6(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, @@ -103,7 +103,7 @@ func testCommitBatchAndFinalizeBundleCodecV4V5V6(t *testing.T) { BatchTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, MaxChunksPerBatch: math.MaxInt32, - }, encoding.CodecV4, chainConfig, db, db, nil) + }, encoding.CodecV4, chainConfig, db, nil) bup := watcher.NewBundleProposer(context.Background(), &config.BundleProposerConfig{ MaxBatchNumPerBundle: 1000000, @@ -291,14 +291,14 @@ func testCommitBatchAndFinalizeBundleCodecV7(t *testing.T) { MaxRowConsumptionPerChunk: 1048319, ChunkTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV7, chainConfig, db, db, nil) + }, encoding.CodecV7, chainConfig, db, nil) bap := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{ MaxL1CommitGasPerBatch: 50000000000, MaxL1CommitCalldataSizePerBatch: 1000000, BatchTimeoutSec: 300, MaxUncompressedBatchBytesSize: math.MaxUint64, - }, encoding.CodecV7, chainConfig, db, db, nil) + }, encoding.CodecV7, chainConfig, db, nil) bup := watcher.NewBundleProposer(context.Background(), &config.BundleProposerConfig{ MaxBatchNumPerBundle: 2, From f24cc7c73e5f9095aeecb9a2a15092fc66967feb Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 19:41:27 +0800 Subject: [PATCH 12/21] separate configs --- rollup/cmd/proposer_tool/app/app.go | 2 +- rollup/internal/config/config.go | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index a4e3596dd3..d10df725af 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -48,7 +48,7 @@ func init() { func action(ctx *cli.Context) error { // Load config file. cfgFile := ctx.String(utils.ConfigFileFlag.Name) - cfg, err := config.NewConfig(cfgFile) + cfg, err := config.NewConfigForReplay(cfgFile) if err != nil { log.Crit("failed to load config file", "config file", cfgFile, "error", err) } diff --git a/rollup/internal/config/config.go b/rollup/internal/config/config.go index b730e78617..ca58f3bebf 100644 --- a/rollup/internal/config/config.go +++ b/rollup/internal/config/config.go @@ -1,7 +1,10 @@ package config import ( + "encoding/json" "fmt" + "os" + "path/filepath" "reflect" "strings" @@ -15,9 +18,13 @@ import ( // Config load configuration items. type Config struct { - L1Config *L1Config `json:"l1_config"` - L2Config *L2Config `json:"l2_config"` - DBConfig *database.Config `json:"db_config"` + L1Config *L1Config `json:"l1_config"` + L2Config *L2Config `json:"l2_config"` + DBConfig *database.Config `json:"db_config"` +} + +type ConfigForReplay struct { + Config DBConfigForReplay *database.Config `json:"db_config_for_replay"` } @@ -88,3 +95,19 @@ func NewConfig(file string) (*Config, error) { return cfg, nil } + +// NewConfigForReplay returns a new instance of Config for replay. +func NewConfigForReplay(file string) (*ConfigForReplay, error) { + buf, err := os.ReadFile(filepath.Clean(file)) + if err != nil { + return nil, err + } + + cfg := &ConfigForReplay{} + err = json.Unmarshal(buf, cfg) + if err != nil { + return nil, err + } + + return cfg, nil +} From 11ab6c70cd3206da17937980a00778317094769d Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 19:47:43 +0800 Subject: [PATCH 13/21] tweak --- rollup/internal/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/internal/config/config.go b/rollup/internal/config/config.go index ca58f3bebf..0b9ce5ac36 100644 --- a/rollup/internal/config/config.go +++ b/rollup/internal/config/config.go @@ -96,7 +96,7 @@ func NewConfig(file string) (*Config, error) { return cfg, nil } -// NewConfigForReplay returns a new instance of Config for replay. +// NewConfigForReplay returns a new instance of ConfigForReplay. func NewConfigForReplay(file string) (*ConfigForReplay, error) { buf, err := os.ReadFile(filepath.Clean(file)) if err != nil { From 9571c8976d15248fad599945227938d2502b7223 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 19:48:51 +0800 Subject: [PATCH 14/21] tweak --- rollup/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/README.md b/rollup/README.md index 6bca724aa8..5b7fcc89c4 100644 --- a/rollup/README.md +++ b/rollup/README.md @@ -36,7 +36,7 @@ make rollup_bins ## Proposer Tool -The Proposer Tool replays historical blocks with custom configurations (e.g., future hardfork configs, customized chunk/batch/bundle proposer configs) to generate chunks/batches/bundles, helping test parameter changes before protocol upgrade. +The Proposer Tool replays historical blocks with custom configurations (e.g., future hardfork configs, custom chunk/batch/bundle proposer configs) to generate chunks/batches/bundles, helping test parameter changes before protocol upgrade. You can: From b80e59aabe252bde6ab369d340c28f021cd62dd6 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 21:01:29 +0800 Subject: [PATCH 15/21] refactor --- rollup/cmd/proposer_tool/app/app.go | 131 +------------- .../controller/watcher/proposer_tool.go | 168 ++++++++++++++++++ 2 files changed, 176 insertions(+), 123 deletions(-) create mode 100644 rollup/internal/controller/watcher/proposer_tool.go diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index d10df725af..423a828a40 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -3,28 +3,18 @@ package app import ( "context" "fmt" - "math/big" "os" "os/signal" - "time" - "github.com/prometheus/client_golang/prometheus" "github.com/scroll-tech/da-codec/encoding" - "github.com/scroll-tech/go-ethereum/common" - 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/urfave/cli/v2" - "scroll-tech/common/database" "scroll-tech/common/utils" "scroll-tech/common/version" - "scroll-tech/database/migrate" "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/controller/watcher" - "scroll-tech/rollup/internal/orm" - rutils "scroll-tech/rollup/internal/utils" ) var app *cli.App @@ -54,130 +44,22 @@ func action(ctx *cli.Context) error { } subCtx, cancel := context.WithCancel(ctx.Context) - // Init db connection - db, err := database.InitDB(cfg.DBConfig) - if err != nil { - log.Crit("failed to init db connection", "err", err) - } - sqlDB, err := db.DB() - if err != nil { - log.Crit("failed to get db connection", "error", err) - } - if err = migrate.ResetDB(sqlDB); err != nil { - log.Crit("failed to reset db", "error", err) - } - log.Info("successfully reset db") - defer func() { - cancel() - if err = database.CloseDB(db); err != nil { - log.Crit("failed to close db connection", "error", err) - } - }() - - // Init dbForReplay connection - dbForReplay, err := database.InitDB(cfg.DBConfigForReplay) - if err != nil { - log.Crit("failed to init dbForReplay connection", "err", err) - } - defer func() { - cancel() - if err = database.CloseDB(dbForReplay); err != nil { - log.Crit("failed to close dbForReplay connection", "error", err) - } - }() - - // Init l2geth connection - l2Client, err := ethclient.Dial(cfg.L2Config.Endpoint) - if err != nil { - log.Crit("failed to connect l2 geth", "config file", cfgFile, "error", err) - } startL2BlockHeight := ctx.Uint64(utils.StartL2BlockFlag.Name) - prevChunk, err := orm.NewChunk(dbForReplay).GetParentChunkByBlockNumber(subCtx, startL2BlockHeight) - if err != nil { - log.Crit("failed to get previous chunk", "error", err) - } - - var startQueueIndex uint64 - if prevChunk != nil { - startQueueIndex = prevChunk.TotalL1MessagesPoppedBefore + prevChunk.TotalL1MessagesPoppedInChunk - } - - startBlock := uint64(0) - if prevChunk != nil { - startBlock = prevChunk.EndBlockNumber + 1 - } - - var chunk *encoding.Chunk - for blockNum := startBlock; blockNum <= startL2BlockHeight; blockNum++ { - block, err := l2Client.BlockByNumber(context.Background(), big.NewInt(int64(blockNum))) - if err != nil { - log.Crit("failed to get block", "block number", blockNum, "error", err) - } - - for _, tx := range block.Transactions() { - if tx.Type() == gethTypes.L1MessageTxType { - startQueueIndex++ - } - } - - if blockNum == startL2BlockHeight { - chunk = &encoding.Chunk{Blocks: []*encoding.Block{{Header: block.Header()}}} - } - } - - // Setting empty hash as the post_l1_message_queue_hash of the first chunk, - // i.e., treating the first L1 message after this chunk as the first L1 message in message queue v2. - // Though this setting is different from mainnet, it's simple yet sufficient for data analysis usage. - _, err = orm.NewChunk(db).InsertTestChunkForProposerTool(subCtx, chunk, encoding.CodecV0, startQueueIndex) - if err != nil { - log.Crit("failed to insert chunk", "error", err) - } - - batch := &encoding.Batch{ - Index: 0, - TotalL1MessagePoppedBefore: 0, - ParentBatchHash: common.Hash{}, - Chunks: []*encoding.Chunk{chunk}, - } - - var dbBatch *orm.Batch - dbBatch, err = orm.NewBatch(db).InsertBatch(subCtx, batch, encoding.CodecV0, rutils.BatchMetrics{}) - if err != nil { - log.Crit("failed to insert batch", "error", err) - } - - if err = orm.NewChunk(db).UpdateBatchHashInRange(subCtx, 0, 0, dbBatch.Hash); err != nil { - log.Crit("failed to update batch hash for chunks", "error", err) - } - - registry := prometheus.DefaultRegisterer - genesisPath := ctx.String(utils.Genesis.Name) genesis, err := utils.ReadGenesis(genesisPath) if err != nil { log.Crit("failed to read genesis", "genesis file", genesisPath, "error", err) } - // sanity check config - if cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch <= 0 { - log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") - } - if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { - log.Crit("cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk must be greater than 0") - } - minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) - chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, genesis.Config, db, registry) - chunkProposer.SetReplayDB(dbForReplay) - batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, minCodecVersion, genesis.Config, db, registry) - batchProposer.SetReplayDB(dbForReplay) - bundleProposer := watcher.NewBundleProposer(subCtx, cfg.L2Config.BundleProposerConfig, minCodecVersion, genesis.Config, db, registry) - go utils.Loop(subCtx, 100*time.Millisecond, chunkProposer.TryProposeChunk) - go utils.Loop(subCtx, 100*time.Millisecond, batchProposer.TryProposeBatch) - go utils.Loop(subCtx, 100*time.Millisecond, bundleProposer.TryProposeBundle) + proposerTool, err := watcher.NewProposerTool(subCtx, cancel, cfg, startL2BlockHeight, minCodecVersion, genesis.Config) + if err != nil { + log.Crit("failed to create proposer tool", "startL2BlockHeight", startL2BlockHeight, "minCodecVersion", minCodecVersion, "error", err) + } + proposerTool.Start() // Finish start all proposer tool functions. log.Info("Start proposer-tool successfully", "version", version.Version) @@ -189,6 +71,9 @@ func action(ctx *cli.Context) error { // Wait until the interrupt signal is received from an OS signal. <-interrupt + cancel() + proposerTool.Stop() + return nil } diff --git a/rollup/internal/controller/watcher/proposer_tool.go b/rollup/internal/controller/watcher/proposer_tool.go new file mode 100644 index 0000000000..9f34100e72 --- /dev/null +++ b/rollup/internal/controller/watcher/proposer_tool.go @@ -0,0 +1,168 @@ +package watcher + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/scroll-tech/da-codec/encoding" + "github.com/scroll-tech/go-ethereum/common" + 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/params" + "gorm.io/gorm" + + "scroll-tech/common/database" + "scroll-tech/common/utils" + "scroll-tech/database/migrate" + + "scroll-tech/rollup/internal/config" + "scroll-tech/rollup/internal/orm" + rutils "scroll-tech/rollup/internal/utils" +) + +// ProposerTool is a tool for proposing chunks and bundles to the L1 chain. +type ProposerTool struct { + ctx context.Context + cancel context.CancelFunc + + db *gorm.DB + dbForReplay *gorm.DB + client *ethclient.Client + + chunkProposer *ChunkProposer + batchProposer *BatchProposer + bundleProposer *BundleProposer +} + +// NewProposerTool creates a new ProposerTool instance. +func NewProposerTool(ctx context.Context, cancel context.CancelFunc, cfg *config.ConfigForReplay, startL2BlockHeight uint64, minCodecVersion encoding.CodecVersion, chainCfg *params.ChainConfig) (*ProposerTool, error) { + // Init db connection + db, err := database.InitDB(cfg.DBConfig) + if err != nil { + return nil, fmt.Errorf("failed to init db connection: %w", err) + } + sqlDB, err := db.DB() + if err != nil { + return nil, fmt.Errorf("failed to get db connection: %w", err) + } + if err = migrate.ResetDB(sqlDB); err != nil { + return nil, fmt.Errorf("failed to reset db: %w", err) + } + log.Info("successfully reset db") + + // Init dbForReplay connection + dbForReplay, err := database.InitDB(cfg.DBConfigForReplay) + if err != nil { + return nil, fmt.Errorf("failed to init dbForReplay connection: %w", err) + } + + client, err := ethclient.Dial(cfg.L2Config.Endpoint) + if err != nil { + return nil, fmt.Errorf("failed to connect to L2 geth, endpoint: %s, err: %w", cfg.L2Config.Endpoint, err) + } + + prevChunk, err := orm.NewChunk(dbForReplay).GetParentChunkByBlockNumber(ctx, startL2BlockHeight) + if err != nil { + return nil, fmt.Errorf("failed to get previous chunk: %w", err) + } + + var startQueueIndex uint64 + if prevChunk != nil { + startQueueIndex = prevChunk.TotalL1MessagesPoppedBefore + prevChunk.TotalL1MessagesPoppedInChunk + } + + startBlock := uint64(0) + if prevChunk != nil { + startBlock = prevChunk.EndBlockNumber + 1 + } + + var chunk *encoding.Chunk + for blockNum := startBlock; blockNum <= startL2BlockHeight; blockNum++ { + block, err := client.BlockByNumber(ctx, new(big.Int).SetUint64(blockNum)) + if err != nil { + return nil, fmt.Errorf("failed to get block %d: %w", blockNum, err) + } + + for _, tx := range block.Transactions() { + if tx.Type() == gethTypes.L1MessageTxType { + startQueueIndex++ + } + } + + if blockNum == startL2BlockHeight { + chunk = &encoding.Chunk{Blocks: []*encoding.Block{{Header: block.Header()}}} + } + } + + // Setting empty hash as the post_l1_message_queue_hash of the first chunk, + // i.e., treating the first L1 message after this chunk as the first L1 message in message queue v2. + // Though this setting is different from mainnet, it's simple yet sufficient for data analysis usage. + _, err = orm.NewChunk(db).InsertTestChunkForProposerTool(ctx, chunk, minCodecVersion, startQueueIndex) + if err != nil { + return nil, fmt.Errorf("failed to insert chunk, minCodecVersion: %d, startQueueIndex: %d, err: %w", minCodecVersion, startQueueIndex, err) + } + + batch := &encoding.Batch{ + Index: 0, + TotalL1MessagePoppedBefore: 0, + ParentBatchHash: common.Hash{}, + Chunks: []*encoding.Chunk{chunk}, + } + + var dbBatch *orm.Batch + dbBatch, err = orm.NewBatch(db).InsertBatch(ctx, batch, encoding.CodecV0, rutils.BatchMetrics{}) + if err != nil { + return nil, fmt.Errorf("failed to insert batch: %w", err) + } + + if err = orm.NewChunk(db).UpdateBatchHashInRange(ctx, 0, 0, dbBatch.Hash); err != nil { + return nil, fmt.Errorf("failed to update batch hash for chunks: %w", err) + } + + // sanity check config + if cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch <= 0 { + return nil, fmt.Errorf("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") + } + if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { + return nil, fmt.Errorf("cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk must be greater than 0") + } + + chunkProposer := NewChunkProposer(ctx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, chainCfg, db, nil) + chunkProposer.SetReplayDB(dbForReplay) + batchProposer := NewBatchProposer(ctx, cfg.L2Config.BatchProposerConfig, minCodecVersion, chainCfg, db, nil) + batchProposer.SetReplayDB(dbForReplay) + bundleProposer := NewBundleProposer(ctx, cfg.L2Config.BundleProposerConfig, minCodecVersion, chainCfg, db, nil) + + return &ProposerTool{ + ctx: ctx, + cancel: cancel, + + db: db, + dbForReplay: dbForReplay, + client: client, + + chunkProposer: chunkProposer, + batchProposer: batchProposer, + bundleProposer: bundleProposer, + }, nil +} + +func (p *ProposerTool) Start() { + go utils.Loop(p.ctx, 100*time.Millisecond, p.chunkProposer.TryProposeChunk) + go utils.Loop(p.ctx, 100*time.Millisecond, p.batchProposer.TryProposeBatch) + go utils.Loop(p.ctx, 100*time.Millisecond, p.bundleProposer.TryProposeBundle) +} + +func (p *ProposerTool) Stop() { + p.cancel() + if err := database.CloseDB(p.db); err != nil { + log.Error("failed to close db connection", "error", err) + } + if err := database.CloseDB(p.dbForReplay); err != nil { + log.Error("failed to close dbForReplay connection", "error", err) + } + p.client.Close() +} From c6b779fd79cbdacd0591d233e251fdc56a194809 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 22:22:42 +0800 Subject: [PATCH 16/21] tweak --- rollup/cmd/proposer_tool/app/app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index 423a828a40..efa32ffa72 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -61,7 +61,6 @@ func action(ctx *cli.Context) error { } proposerTool.Start() - // Finish start all proposer tool functions. log.Info("Start proposer-tool successfully", "version", version.Version) // Catch CTRL-C to ensure a graceful shutdown. From 934f4bb310cef11ba7efb5343d7f2b4643011f96 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 22:25:22 +0800 Subject: [PATCH 17/21] address AI's comments --- rollup/cmd/proposer_tool/app/app.go | 8 ++++++++ rollup/internal/controller/watcher/proposer_tool.go | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index efa32ffa72..05bcca38f3 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -55,6 +55,14 @@ func action(ctx *cli.Context) error { minCodecVersion := encoding.CodecVersion(ctx.Uint(utils.MinCodecVersionFlag.Name)) + // sanity check config + if cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch <= 0 { + log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") + } + if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { + log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") + } + proposerTool, err := watcher.NewProposerTool(subCtx, cancel, cfg, startL2BlockHeight, minCodecVersion, genesis.Config) if err != nil { log.Crit("failed to create proposer tool", "startL2BlockHeight", startL2BlockHeight, "minCodecVersion", minCodecVersion, "error", err) diff --git a/rollup/internal/controller/watcher/proposer_tool.go b/rollup/internal/controller/watcher/proposer_tool.go index 9f34100e72..e0c7bc3bc0 100644 --- a/rollup/internal/controller/watcher/proposer_tool.go +++ b/rollup/internal/controller/watcher/proposer_tool.go @@ -122,14 +122,6 @@ func NewProposerTool(ctx context.Context, cancel context.CancelFunc, cfg *config return nil, fmt.Errorf("failed to update batch hash for chunks: %w", err) } - // sanity check config - if cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch <= 0 { - return nil, fmt.Errorf("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") - } - if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { - return nil, fmt.Errorf("cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk must be greater than 0") - } - chunkProposer := NewChunkProposer(ctx, cfg.L2Config.ChunkProposerConfig, minCodecVersion, chainCfg, db, nil) chunkProposer.SetReplayDB(dbForReplay) batchProposer := NewBatchProposer(ctx, cfg.L2Config.BatchProposerConfig, minCodecVersion, chainCfg, db, nil) From 9d6ca3c3559a95be7b6db33c1f6407185380fabe Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 21 Apr 2025 14:50:52 +0000 Subject: [PATCH 18/21] =?UTF-8?q?chore:=20auto=20version=20bump=E2=80=89[b?= =?UTF-8?q?ot]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version/version.go b/common/version/version.go index 7eeb26db3d..0686143491 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.5.5" +var tag = "v4.5.6" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { From 6e7bc388e1dc5ffc291ae0e7a3c5c5c92d7056aa Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 22 Apr 2025 12:13:10 +0800 Subject: [PATCH 19/21] fix wrong error message --- rollup/cmd/proposer_tool/app/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/cmd/proposer_tool/app/app.go b/rollup/cmd/proposer_tool/app/app.go index 05bcca38f3..1ade4c678e 100644 --- a/rollup/cmd/proposer_tool/app/app.go +++ b/rollup/cmd/proposer_tool/app/app.go @@ -60,7 +60,7 @@ func action(ctx *cli.Context) error { log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") } if cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk <= 0 { - log.Crit("cfg.L2Config.BatchProposerConfig.MaxChunksPerBatch must be greater than 0") + log.Crit("cfg.L2Config.ChunkProposerConfig.MaxL2GasPerChunk must be greater than 0") } proposerTool, err := watcher.NewProposerTool(subCtx, cancel, cfg, startL2BlockHeight, minCodecVersion, genesis.Config) From 7e688e8e5b55e701489ae1785714b1218a161666 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 22 Apr 2025 19:42:16 +0800 Subject: [PATCH 20/21] add more comments --- rollup/internal/controller/watcher/chunk_proposer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index a5ee896e3d..94bbc409fd 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -454,6 +454,9 @@ func (p *ChunkProposer) tryProposeEuclidTransitionChunk(blocks []*encoding.Block } prevBlocks, err := p.l2BlockOrm.GetL2BlocksGEHeight(p.ctx, blocks[0].Header.Number.Uint64()-1, 1) + // If we are in replay mode, we don't need to check the parent block. + // This is a corner case when StartL2Block is set as 0, it needs to get genesis block, but in mainnet db there is no genesis block. + // So we need to bypass this check. if !p.replayMode && (err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash) { return false, fmt.Errorf("failed to get parent block: %w", err) } From 48a0625487137362b8cc74b167e7f9c6e05283c8 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 22 Apr 2025 19:44:52 +0800 Subject: [PATCH 21/21] tweaks --- rollup/internal/controller/watcher/chunk_proposer.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rollup/internal/controller/watcher/chunk_proposer.go b/rollup/internal/controller/watcher/chunk_proposer.go index 94bbc409fd..03db91e43d 100644 --- a/rollup/internal/controller/watcher/chunk_proposer.go +++ b/rollup/internal/controller/watcher/chunk_proposer.go @@ -449,15 +449,18 @@ func (p *ChunkProposer) recordTimerChunkMetrics(metrics *utils.ChunkMetrics) { } func (p *ChunkProposer) tryProposeEuclidTransitionChunk(blocks []*encoding.Block) (bool, error) { + // If we are in replay mode, there is a corner case when StartL2Block is set as 0 in this check, + // it needs to get genesis block, but in mainnet db there is no genesis block, so we need to bypass this check. + if p.replayMode { + return false, nil + } + if !p.chainCfg.IsEuclid(blocks[0].Header.Time) { return false, nil } prevBlocks, err := p.l2BlockOrm.GetL2BlocksGEHeight(p.ctx, blocks[0].Header.Number.Uint64()-1, 1) - // If we are in replay mode, we don't need to check the parent block. - // This is a corner case when StartL2Block is set as 0, it needs to get genesis block, but in mainnet db there is no genesis block. - // So we need to bypass this check. - if !p.replayMode && (err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash) { + if err != nil || len(prevBlocks) == 0 || prevBlocks[0].Header.Hash() != blocks[0].Header.ParentHash { return false, fmt.Errorf("failed to get parent block: %w", err) }