package main import ( "encoding/json" "fmt" "math/rand" "os" "path/filepath" "strconv" "strings" "github.com/scroll-tech/da-codec/encoding" "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/log" "github.com/urfave/cli/v2" "scroll-tech/common/database" "scroll-tech/common/utils" "scroll-tech/common/version" ) var app *cli.App var cfg *config var codecCfg encoding.CodecVersion = encoding.CodecV9 var outputNumFlag = cli.StringFlag{ Name: "counts", Usage: "Counts for output (chunks,batches,bundles)", Value: "4,2,1", } var outputPathFlag = cli.StringFlag{ Name: "output", Usage: "output file path", Value: "testset.json", } var seedFlag = cli.Int64Flag{ Name: "seed", Usage: "random seed, 0 to use random selected seed", Value: 0, } func parseThreeIntegers(value string) (int, int, int, error) { // Split the input string by comma parts := strings.Split(value, ",") // Check that we have exactly 3 parts if len(parts) != 3 { return 0, 0, 0, fmt.Errorf("input must contain exactly 3 comma-separated integers, got %s", value) } // Parse the three integers values := make([]int, 3) for i, part := range parts { // Trim any whitespace part = strings.TrimSpace(part) // Parse the integer val, err := strconv.Atoi(part) if err != nil { return 0, 0, 0, fmt.Errorf("failed to parse '%s' as integer: %w", part, err) } // Check that it's positive if val <= 0 { return 0, 0, 0, fmt.Errorf("all integers must be greater than 0, got %d", val) } values[i] = val } // Check that first >= second >= third if values[0] < values[1] || values[1] < values[2] { return 0, 0, 0, fmt.Errorf("integers must be in descending order: %d >= %d >= %d", values[0], values[1], values[2]) } return values[0], values[1], values[2], nil } type fetchConfig struct { // node url. Endpoint string `json:"endpoint"` // The L2MessageQueue contract address deployed on layer 2 chain. L2MessageQueueAddress common.Address `json:"l2_message_queue_address"` // The WithdrawTrieRootSlot in L2MessageQueue contract. WithdrawTrieRootSlot common.Hash `json:"withdraw_trie_root_slot,omitempty"` } // load a comptabile type of config for rollup type config struct { DBConfig *database.Config `json:"db_config"` FetchConfig *fetchConfig `json:"fetch_config,omitempty"` ValidiumMode bool `json:"validium_mode"` CodecVersion int `json:"codec_version"` } func init() { // Set up coordinator app info. app = cli.NewApp() app.Action = action app.Name = "integration-test-tool" app.Usage = "The Scroll L2 Integration Test Tool" app.Version = version.Version app.Flags = append(app.Flags, &seedFlag, &outputNumFlag, &outputPathFlag) app.Flags = append(app.Flags, utils.CommonFlags...) app.Before = func(ctx *cli.Context) error { if err := utils.LogSetup(ctx); err != nil { return err } cfgFile := ctx.String(utils.ConfigFileFlag.Name) var err error cfg, err = newConfig(cfgFile) if err != nil { log.Crit("failed to load config file", "config file", cfgFile, "error", err) } return nil } } func newConfig(file string) (*config, error) { buf, err := os.ReadFile(filepath.Clean(file)) if err != nil { return nil, err } loadCfg := &config{} err = json.Unmarshal(buf, loadCfg) if err != nil { return nil, err } return loadCfg, nil } func action(ctx *cli.Context) error { if ctx.Args().Len() < 2 { return fmt.Errorf("specify begin and end block number") } if cfg.CodecVersion != 0 { switch cfg.CodecVersion { case 6: codecCfg = encoding.CodecV6 case 7: codecCfg = encoding.CodecV7 case 8: codecCfg = encoding.CodecV8 case 9: codecCfg = encoding.CodecV9 case 10: codecCfg = encoding.CodecV10 default: return fmt.Errorf("invalid codec version %d", cfg.CodecVersion) } log.Info("set codec", "version", codecCfg) } beginBlk, err := strconv.ParseUint(ctx.Args().First(), 10, 64) if err != nil { return fmt.Errorf("invalid begin block number: %w", err) } endBlk, err := strconv.ParseUint(ctx.Args().Get(1), 10, 64) if err != nil { return fmt.Errorf("invalid begin block number: %w", err) } var import_blocks []*encoding.Block if cfg.FetchConfig != nil { import_blocks, err = fetchAndStoreBlocks(ctx.Context, beginBlk, endBlk) if err != nil { return err } } chkNum, batchNum, bundleNum, err := parseThreeIntegers(ctx.String(outputNumFlag.Name)) if err != nil { return err } seed := ctx.Int64(seedFlag.Name) //nolint:all if seed == 0 { seed = rand.Int63() } outputPath := ctx.String(outputPathFlag.Name) log.Info("output", "Seed", seed, "file", outputPath) ret, err := importData(ctx.Context, beginBlk, endBlk, import_blocks, chkNum, batchNum, bundleNum, seed) if err != nil { return err } // Marshal the ret variable to JSON with indentation for readability jsonData, err := json.MarshalIndent(ret, "", " ") if err != nil { return fmt.Errorf("failed to marshal result data to JSON: %w", err) } // Write the JSON data to the specified file err = os.WriteFile(outputPath, jsonData, 0600) if err != nil { return fmt.Errorf("failed to write result to file %s: %w", outputPath, err) } return nil } func main() { if err := app.Run(os.Args); err != nil { _, _ = fmt.Fprintln(os.Stderr, err) os.Exit(1) } }