mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-02-18 01:21:40 -05:00
integration tests
This commit is contained in:
189
rollup/tests/integration_tool/imports.go
Normal file
189
rollup/tests/integration_tool/imports.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/scroll-tech/da-codec/encoding"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
|
||||
"scroll-tech/common/database"
|
||||
"scroll-tech/rollup/internal/orm"
|
||||
"scroll-tech/rollup/internal/utils"
|
||||
)
|
||||
|
||||
type importRecord struct {
|
||||
Chunk []string `json:"chunks"`
|
||||
Batch []string `json:"batches"`
|
||||
Bundle []string `json:"bundles"`
|
||||
}
|
||||
|
||||
func randomPickKfromN(n, k int, rng *rand.Rand) []int {
|
||||
ret := make([]int, n-1)
|
||||
for i := 1; i < n; i++ {
|
||||
ret[i-1] = i
|
||||
}
|
||||
|
||||
rng.Shuffle(len(ret), func(i, j int) {
|
||||
ret[i], ret[j] = ret[j], ret[i]
|
||||
})
|
||||
|
||||
ret = ret[:k-1]
|
||||
sort.Ints(ret)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func importData(ctx context.Context, beginBlk, endBlk uint64, chkNum, batchNum, bundleNum int, seed int64) (*importRecord, error) {
|
||||
|
||||
db, err := database.InitDB(cfg.DBConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &importRecord{}
|
||||
// Create a new random source with the provided seed
|
||||
source := rand.NewSource(seed)
|
||||
rng := rand.New(source)
|
||||
|
||||
chkSepIdx := randomPickKfromN(int(endBlk-beginBlk)+1, chkNum, rng)
|
||||
chkSep := make([]uint64, len(chkSepIdx))
|
||||
for i, ind := range chkSepIdx {
|
||||
chkSep[i] = beginBlk + uint64(ind)
|
||||
}
|
||||
chkSep = append(chkSep, endBlk)
|
||||
|
||||
log.Info("separated chunk", "border", chkSep)
|
||||
head := beginBlk
|
||||
lastMsgHash := common.Hash{}
|
||||
|
||||
ormChks := make([]*orm.Chunk, 0, chkNum)
|
||||
encChks := make([]*encoding.Chunk, 0, chkNum)
|
||||
for _, edBlk := range chkSep {
|
||||
ormChk, chk, err := importChunk(ctx, db, head, edBlk-1, lastMsgHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastMsgHash = chk.PostL1MessageQueueHash
|
||||
ormChks = append(ormChks, ormChk)
|
||||
encChks = append(encChks, chk)
|
||||
head = edBlk
|
||||
}
|
||||
|
||||
for _, chk := range ormChks {
|
||||
ret.Chunk = append(ret.Chunk, chk.Hash)
|
||||
}
|
||||
|
||||
batchSep := randomPickKfromN(chkNum, batchNum, rng)
|
||||
batchSep = append(batchSep, batchNum)
|
||||
log.Info("separated batch", "border", batchSep)
|
||||
|
||||
headChk := int(0)
|
||||
batches := make([]*orm.Batch, 0, batchNum)
|
||||
var lastBatch *orm.Batch
|
||||
for _, endChk := range batchSep {
|
||||
batch, err := importBatch(ctx, db, ormChks[headChk:endChk], encChks[headChk:endChk], lastBatch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastBatch = batch
|
||||
batches = append(batches, batch)
|
||||
headChk = endChk
|
||||
}
|
||||
|
||||
for _, batch := range batches {
|
||||
ret.Batch = append(ret.Batch, batch.Hash)
|
||||
}
|
||||
|
||||
bundleSep := randomPickKfromN(batchNum, bundleNum, rng)
|
||||
bundleSep = append(bundleSep, bundleNum)
|
||||
log.Info("separated bundle", "border", bundleSep)
|
||||
|
||||
headBatch := int(0)
|
||||
for _, endBatch := range bundleSep {
|
||||
hash, err := importBundle(ctx, db, batches[headBatch:endBatch])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Bundle = append(ret.Bundle, hash)
|
||||
headBatch = endBatch
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func importChunk(ctx context.Context, db *gorm.DB, beginBlk, endBlk uint64, prevMsgQueueHash common.Hash) (*orm.Chunk, *encoding.Chunk, error) {
|
||||
nblk := int(endBlk-beginBlk) + 1
|
||||
blockOrm := orm.NewL2Block(db)
|
||||
|
||||
blks, err := blockOrm.GetL2BlocksGEHeight(ctx, beginBlk, nblk)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
postHash, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(prevMsgQueueHash, blks)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
theChunk := &encoding.Chunk{
|
||||
Blocks: blks,
|
||||
PrevL1MessageQueueHash: prevMsgQueueHash,
|
||||
PostL1MessageQueueHash: postHash,
|
||||
}
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
|
||||
dbChk, err := chunkOrm.InsertChunk(ctx, theChunk, codecCfg, utils.ChunkMetrics{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Info("insert chunk", "From", beginBlk, "To", endBlk)
|
||||
return dbChk, theChunk, nil
|
||||
}
|
||||
|
||||
func importBatch(ctx context.Context, db *gorm.DB, chks []*orm.Chunk, encChks []*encoding.Chunk, last *orm.Batch) (*orm.Batch, error) {
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
|
||||
var index uint64
|
||||
var parentHash common.Hash
|
||||
if last != nil {
|
||||
index = last.Index + 1
|
||||
parentHash = common.HexToHash(last.Hash)
|
||||
}
|
||||
|
||||
var blks []*encoding.Block
|
||||
for _, chk := range encChks {
|
||||
blks = append(blks, chk.Blocks...)
|
||||
}
|
||||
|
||||
batch := &encoding.Batch{
|
||||
Index: index,
|
||||
TotalL1MessagePoppedBefore: chks[0].TotalL1MessagesPoppedBefore,
|
||||
ParentBatchHash: parentHash,
|
||||
Chunks: encChks,
|
||||
Blocks: blks,
|
||||
}
|
||||
|
||||
dbBatch, err := batchOrm.InsertBatch(ctx, batch, codecCfg, utils.BatchMetrics{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("insert batch", "index", index)
|
||||
return dbBatch, nil
|
||||
}
|
||||
|
||||
func importBundle(ctx context.Context, db *gorm.DB, batches []*orm.Batch) (string, error) {
|
||||
|
||||
bundleOrm := orm.NewBundle(db)
|
||||
bundle, err := bundleOrm.InsertBundle(ctx, batches, codecCfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Info("insert bundle", "hash", bundle.Hash)
|
||||
return bundle.Hash, nil
|
||||
}
|
||||
174
rollup/tests/integration_tool/main.go
Normal file
174
rollup/tests/integration_tool/main.go
Normal file
@@ -0,0 +1,174 @@
|
||||
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/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.CodecV8
|
||||
|
||||
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() (int, int, int, error) {
|
||||
// Split the input string by comma
|
||||
parts := strings.Split(outputNumFlag.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", outputNumFlag.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
|
||||
}
|
||||
|
||||
// load a comptabile type of config for rollup
|
||||
type config struct {
|
||||
DBConfig *database.Config `json:"db_config"`
|
||||
}
|
||||
|
||||
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, 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
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
err = json.Unmarshal(buf, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func action(ctx *cli.Context) error {
|
||||
|
||||
if ctx.Args().Len() < 2 {
|
||||
return fmt.Errorf("specify begin and end block number")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
chkNum, batchNum, bundleNum, err := parseThreeIntegers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seed := seedFlag.Value
|
||||
if seed == 0 {
|
||||
seed = rand.Int63()
|
||||
}
|
||||
|
||||
log.Info("output", "Seed", seed, "file", outputPathFlag.Value)
|
||||
ret, err := importData(ctx.Context, beginBlk, endBlk, 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(outputPathFlag.Value, jsonData, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write result to file %s: %w", outputPathFlag.Value, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
_, _ = fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user