From b56dd9f31bb84ba3f11ef1f30c30601ea8869795 Mon Sep 17 00:00:00 2001 From: Jonas Theis <4181434+jonastheis@users.noreply.github.com> Date: Fri, 7 Feb 2025 18:43:27 +0800 Subject: [PATCH] refactor(rollup sync service): use `CalldataBlobSource` to retrieve data from L1 (#1103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * port changes from #1013 * port changes from #1068 * go.mod tidy * fix compile error * fix goimports * fix log * address review comments * upgrade golang.org/x/net to 0.23.0 * port changes from #1018 * fix tests and linter errors * address review comments * refactor rollup sync service / verifier to use CalldataBlobSource to retrieve data from L1 * add configuration and initialize blob clients * fix unit tests * remove unused code * address review comments * address more review comments * Allow using MPT * fix issues after merge * bump version --------- Co-authored-by: Ömer Faruk Irmak Co-authored-by: Péter Garamvölgyi --- cmd/utils/flags.go | 18 +- core/rawdb/accessors_rollup_event.go | 3 +- eth/backend.go | 2 +- params/version.go | 2 +- rollup/da_syncer/da/calldata_blob_source.go | 20 +- rollup/da_syncer/da/commitV0.go | 31 +- rollup/da_syncer/da/commitV1.go | 21 +- rollup/da_syncer/da/da.go | 8 + rollup/da_syncer/da/finalize.go | 20 +- rollup/da_syncer/da/revert.go | 20 +- rollup/l1/abi.go | 20 + rollup/l1/reader.go | 13 +- rollup/l1/types.go | 38 + rollup/rollup_sync_service/abi.go | 55 -- rollup/rollup_sync_service/abi_test.go | 82 -- rollup/rollup_sync_service/l1client.go | 158 --- rollup/rollup_sync_service/l1client_test.go | 74 -- .../rollup_sync_service.go | 382 +++----- .../rollup_sync_service_test.go | 896 ++++++------------ .../commitBatchWithBlobProof_codecv3.rlp | Bin 4693 -> 0 bytes ...ommitBatchWithBlobProof_input_codecv3.json | 31 - .../testdata/commitBatch_codecv0.rlp | Bin 88636 -> 0 bytes .../testdata/commitBatch_codecv1.rlp | Bin 1237 -> 0 bytes .../testdata/commitBatch_codecv2.rlp | Bin 4437 -> 0 bytes .../testdata/commitBatch_input_codecv0.json | 27 - .../testdata/commitBatch_input_codecv1.json | 31 - .../testdata/commitBatch_input_codecv2.json | 31 - trie/zk_trie_test.go | 3 +- 28 files changed, 622 insertions(+), 1364 deletions(-) delete mode 100644 rollup/rollup_sync_service/abi.go delete mode 100644 rollup/rollup_sync_service/abi_test.go delete mode 100644 rollup/rollup_sync_service/l1client.go delete mode 100644 rollup/rollup_sync_service/l1client_test.go delete mode 100644 rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_codecv3.rlp delete mode 100644 rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_input_codecv3.json delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_codecv0.rlp delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_codecv1.rlp delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_codecv2.rlp delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_input_codecv0.json delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_input_codecv1.json delete mode 100644 rollup/rollup_sync_service/testdata/commitBatch_input_codecv2.json diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a8412236a8..5add55be7a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1641,15 +1641,15 @@ func setEnableRollupVerify(ctx *cli.Context, cfg *ethconfig.Config) { func setDA(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.IsSet(DASyncEnabledFlag.Name) { cfg.EnableDASyncing = ctx.Bool(DASyncEnabledFlag.Name) - if ctx.IsSet(DABlobScanAPIEndpointFlag.Name) { - cfg.DA.BlobScanAPIEndpoint = ctx.String(DABlobScanAPIEndpointFlag.Name) - } - if ctx.IsSet(DABlockNativeAPIEndpointFlag.Name) { - cfg.DA.BlockNativeAPIEndpoint = ctx.String(DABlockNativeAPIEndpointFlag.Name) - } - if ctx.IsSet(DABeaconNodeAPIEndpointFlag.Name) { - cfg.DA.BeaconNodeAPIEndpoint = ctx.String(DABeaconNodeAPIEndpointFlag.Name) - } + } + if ctx.IsSet(DABlobScanAPIEndpointFlag.Name) { + cfg.DA.BlobScanAPIEndpoint = ctx.String(DABlobScanAPIEndpointFlag.Name) + } + if ctx.IsSet(DABlockNativeAPIEndpointFlag.Name) { + cfg.DA.BlockNativeAPIEndpoint = ctx.String(DABlockNativeAPIEndpointFlag.Name) + } + if ctx.IsSet(DABeaconNodeAPIEndpointFlag.Name) { + cfg.DA.BeaconNodeAPIEndpoint = ctx.String(DABeaconNodeAPIEndpointFlag.Name) } } diff --git a/core/rawdb/accessors_rollup_event.go b/core/rawdb/accessors_rollup_event.go index 1b60f6e4f0..e5daffcc59 100644 --- a/core/rawdb/accessors_rollup_event.go +++ b/core/rawdb/accessors_rollup_event.go @@ -18,7 +18,8 @@ type ChunkBlockRange struct { // CommittedBatchMeta holds metadata for committed batches. type CommittedBatchMeta struct { - Version uint8 + Version uint8 + // BlobVersionedHashes are the versioned hashes of the blobs in the batch. Currently unused. Left for compatibility. BlobVersionedHashes []common.Hash ChunkBlockRanges []*ChunkBlockRange } diff --git a/eth/backend.go b/eth/backend.go index a119708e52..bd432cb713 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -245,7 +245,7 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether if config.EnableRollupVerify { // initialize and start rollup event sync service - eth.rollupSyncService, err = rollup_sync_service.NewRollupSyncService(context.Background(), chainConfig, eth.chainDb, l1Client, eth.blockchain, stack) + eth.rollupSyncService, err = rollup_sync_service.NewRollupSyncService(context.Background(), chainConfig, eth.chainDb, l1Client, eth.blockchain, stack, config.DA) if err != nil { return nil, fmt.Errorf("cannot initialize rollup event sync service: %w", err) } diff --git a/params/version.go b/params/version.go index 260a9bafbb..b63a376557 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 4 // Patch version component of the current release + VersionPatch = 5 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/da_syncer/da/calldata_blob_source.go b/rollup/da_syncer/da/calldata_blob_source.go index a17ebdb0fc..bf4a2a24ef 100644 --- a/rollup/da_syncer/da/calldata_blob_source.go +++ b/rollup/da_syncer/da/calldata_blob_source.go @@ -81,6 +81,10 @@ func (ds *CalldataBlobSource) NextData() (Entries, error) { return da, nil } +func (ds *CalldataBlobSource) SetL1Height(l1Height uint64) { + ds.l1Height = l1Height +} + func (ds *CalldataBlobSource) L1Height() uint64 { return ds.l1Height } @@ -106,10 +110,22 @@ func (ds *CalldataBlobSource) processRollupEventsToDA(rollupEvents l1.RollupEven } case l1.RevertEventType: - entry = NewRevertBatch(rollupEvent.BatchIndex().Uint64()) + revertEvent, ok := rollupEvent.(*l1.RevertBatchEvent) + // this should never happen because we just check event type + if !ok { + return nil, fmt.Errorf("unexpected type of rollup event: %T", rollupEvent) + } + + entry = NewRevertBatch(revertEvent) case l1.FinalizeEventType: - entry = NewFinalizeBatch(rollupEvent.BatchIndex().Uint64()) + finalizeEvent, ok := rollupEvent.(*l1.FinalizeBatchEvent) + // this should never happen because we just check event type + if !ok { + return nil, fmt.Errorf("unexpected type of rollup event: %T", rollupEvent) + } + + entry = NewFinalizeBatch(finalizeEvent) default: return nil, fmt.Errorf("unknown rollup event, type: %v", rollupEvent.Type()) diff --git a/rollup/da_syncer/da/commitV0.go b/rollup/da_syncer/da/commitV0.go index 2c4f07869d..c8e34ec01a 100644 --- a/rollup/da_syncer/da/commitV0.go +++ b/rollup/da_syncer/da/commitV0.go @@ -6,6 +6,7 @@ import ( "github.com/scroll-tech/da-codec/encoding" + "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethdb" @@ -14,14 +15,14 @@ import ( ) type CommitBatchDAV0 struct { - version uint8 + version encoding.CodecVersion batchIndex uint64 parentTotalL1MessagePopped uint64 skippedL1MessageBitmap []byte chunks []*encoding.DAChunkRawTx l1Txs []*types.L1MessageTx - l1BlockNumber uint64 + event *l1.CommitBatchEvent } func NewCommitBatchDAV0(db ethdb.Database, @@ -36,16 +37,16 @@ func NewCommitBatchDAV0(db ethdb.Database, return nil, fmt.Errorf("failed to unpack chunks: %d, err: %w", commitEvent.BatchIndex().Uint64(), err) } - return NewCommitBatchDAV0WithChunks(db, uint8(codec.Version()), commitEvent.BatchIndex().Uint64(), parentBatchHeader, decodedChunks, skippedL1MessageBitmap, commitEvent.BlockNumber()) + return NewCommitBatchDAV0WithChunks(db, codec.Version(), commitEvent.BatchIndex().Uint64(), parentBatchHeader, decodedChunks, skippedL1MessageBitmap, commitEvent) } func NewCommitBatchDAV0WithChunks(db ethdb.Database, - version uint8, + version encoding.CodecVersion, batchIndex uint64, parentBatchHeader []byte, decodedChunks []*encoding.DAChunkRawTx, skippedL1MessageBitmap []byte, - l1BlockNumber uint64, + event *l1.CommitBatchEvent, ) (*CommitBatchDAV0, error) { parentTotalL1MessagePopped := getBatchTotalL1MessagePopped(parentBatchHeader) l1Txs, err := getL1Messages(db, parentTotalL1MessagePopped, skippedL1MessageBitmap, getTotalMessagesPoppedFromChunks(decodedChunks)) @@ -60,7 +61,7 @@ func NewCommitBatchDAV0WithChunks(db ethdb.Database, skippedL1MessageBitmap: skippedL1MessageBitmap, chunks: decodedChunks, l1Txs: l1Txs, - l1BlockNumber: l1BlockNumber, + event: event, }, nil } @@ -70,12 +71,28 @@ func NewCommitBatchDAV0Empty() *CommitBatchDAV0 { } } +func (c *CommitBatchDAV0) Version() encoding.CodecVersion { + return c.version +} + +func (c *CommitBatchDAV0) Chunks() []*encoding.DAChunkRawTx { + return c.chunks +} + +func (c *CommitBatchDAV0) BlobVersionedHashes() []common.Hash { + return nil +} + func (c *CommitBatchDAV0) Type() Type { return CommitBatchV0Type } func (c *CommitBatchDAV0) L1BlockNumber() uint64 { - return c.l1BlockNumber + return c.event.BlockNumber() +} + +func (c *CommitBatchDAV0) Event() l1.RollupEvent { + return c.event } func (c *CommitBatchDAV0) BatchIndex() uint64 { diff --git a/rollup/da_syncer/da/commitV1.go b/rollup/da_syncer/da/commitV1.go index 0433479c95..6fdcf45b6d 100644 --- a/rollup/da_syncer/da/commitV1.go +++ b/rollup/da_syncer/da/commitV1.go @@ -17,6 +17,8 @@ import ( type CommitBatchDAV1 struct { *CommitBatchDAV0 + + versionedHashes []common.Hash } func NewCommitBatchDAWithBlob(ctx context.Context, db ethdb.Database, @@ -33,11 +35,17 @@ func NewCommitBatchDAWithBlob(ctx context.Context, db ethdb.Database, return nil, fmt.Errorf("failed to unpack chunks: %v, err: %w", commitEvent.BatchIndex().Uint64(), err) } - versionedHash, err := l1Reader.FetchTxBlobHash(commitEvent.TxHash(), commitEvent.BlockHash()) + versionedHashes, err := l1Reader.FetchTxBlobHashes(commitEvent.TxHash(), commitEvent.BlockHash()) if err != nil { return nil, fmt.Errorf("failed to fetch blob hash, err: %w", err) } + // with CommitBatchDAV1 we expect only one versioned hash as we commit only one blob per batch submission + if len(versionedHashes) != 1 { + return nil, fmt.Errorf("unexpected number of versioned hashes: %d", len(versionedHashes)) + } + versionedHash := versionedHashes[0] + header, err := l1Reader.FetchBlockHeaderByNumber(commitEvent.BlockNumber()) if err != nil { return nil, fmt.Errorf("failed to get header by number, err: %w", err) @@ -70,14 +78,21 @@ func NewCommitBatchDAWithBlob(ctx context.Context, db ethdb.Database, return nil, fmt.Errorf("decodedChunks is nil after decoding") } - v0, err := NewCommitBatchDAV0WithChunks(db, uint8(codec.Version()), commitEvent.BatchIndex().Uint64(), parentBatchHeader, decodedChunks, skippedL1MessageBitmap, commitEvent.BlockNumber()) + v0, err := NewCommitBatchDAV0WithChunks(db, codec.Version(), commitEvent.BatchIndex().Uint64(), parentBatchHeader, decodedChunks, skippedL1MessageBitmap, commitEvent) if err != nil { return nil, err } - return &CommitBatchDAV1{v0}, nil + return &CommitBatchDAV1{ + CommitBatchDAV0: v0, + versionedHashes: versionedHashes, + }, nil } func (c *CommitBatchDAV1) Type() Type { return CommitBatchWithBlobType } + +func (c *CommitBatchDAV1) BlobVersionedHashes() []common.Hash { + return c.versionedHashes +} diff --git a/rollup/da_syncer/da/da.go b/rollup/da_syncer/da/da.go index 1ad618d7ba..cd7320f1c0 100644 --- a/rollup/da_syncer/da/da.go +++ b/rollup/da_syncer/da/da.go @@ -3,7 +3,11 @@ package da import ( "math/big" + "github.com/scroll-tech/da-codec/encoding" + + "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/rollup/l1" ) type Type int @@ -25,11 +29,15 @@ type Entry interface { BatchIndex() uint64 L1BlockNumber() uint64 CompareTo(Entry) int + Event() l1.RollupEvent } type EntryWithBlocks interface { Entry Blocks() []*PartialBlock + Version() encoding.CodecVersion + Chunks() []*encoding.DAChunkRawTx + BlobVersionedHashes() []common.Hash } type Entries []Entry diff --git a/rollup/da_syncer/da/finalize.go b/rollup/da_syncer/da/finalize.go index 14d6c2a644..eab805d524 100644 --- a/rollup/da_syncer/da/finalize.go +++ b/rollup/da_syncer/da/finalize.go @@ -1,14 +1,16 @@ package da -type FinalizeBatch struct { - batchIndex uint64 +import ( + "github.com/scroll-tech/go-ethereum/rollup/l1" +) - l1BlockNumber uint64 +type FinalizeBatch struct { + event *l1.FinalizeBatchEvent } -func NewFinalizeBatch(batchIndex uint64) *FinalizeBatch { +func NewFinalizeBatch(event *l1.FinalizeBatchEvent) *FinalizeBatch { return &FinalizeBatch{ - batchIndex: batchIndex, + event: event, } } @@ -17,11 +19,15 @@ func (f *FinalizeBatch) Type() Type { } func (f *FinalizeBatch) L1BlockNumber() uint64 { - return f.l1BlockNumber + return f.event.BlockNumber() } func (f *FinalizeBatch) BatchIndex() uint64 { - return f.batchIndex + return f.event.BatchIndex().Uint64() +} + +func (f *FinalizeBatch) Event() l1.RollupEvent { + return f.event } func (f *FinalizeBatch) CompareTo(other Entry) int { diff --git a/rollup/da_syncer/da/revert.go b/rollup/da_syncer/da/revert.go index d84f22ebaa..f8120fd3f1 100644 --- a/rollup/da_syncer/da/revert.go +++ b/rollup/da_syncer/da/revert.go @@ -1,14 +1,16 @@ package da -type RevertBatch struct { - batchIndex uint64 +import ( + "github.com/scroll-tech/go-ethereum/rollup/l1" +) - l1BlockNumber uint64 +type RevertBatch struct { + event *l1.RevertBatchEvent } -func NewRevertBatch(batchIndex uint64) *RevertBatch { +func NewRevertBatch(event *l1.RevertBatchEvent) *RevertBatch { return &RevertBatch{ - batchIndex: batchIndex, + event: event, } } @@ -17,10 +19,14 @@ func (r *RevertBatch) Type() Type { } func (r *RevertBatch) L1BlockNumber() uint64 { - return r.l1BlockNumber + return r.event.BlockNumber() } func (r *RevertBatch) BatchIndex() uint64 { - return r.batchIndex + return r.event.BatchIndex().Uint64() +} + +func (r *RevertBatch) Event() l1.RollupEvent { + return r.event } func (r *RevertBatch) CompareTo(other Entry) int { diff --git a/rollup/l1/abi.go b/rollup/l1/abi.go index c16123aa5e..dcf09f25fd 100644 --- a/rollup/l1/abi.go +++ b/rollup/l1/abi.go @@ -158,6 +158,26 @@ type FinalizeBatchEvent struct { blockNumber uint64 } +func NewFinalizeBatchEvent( + batchIndex *big.Int, + batchHash common.Hash, + stateRoot common.Hash, + withdrawRoot common.Hash, + txHash common.Hash, + blockHash common.Hash, + blockNumber uint64, +) *FinalizeBatchEvent { + return &FinalizeBatchEvent{ + batchIndex: batchIndex, + batchHash: batchHash, + stateRoot: stateRoot, + withdrawRoot: withdrawRoot, + txHash: txHash, + blockHash: blockHash, + blockNumber: blockNumber, + } +} + func (f *FinalizeBatchEvent) TxHash() common.Hash { return f.txHash } diff --git a/rollup/l1/reader.go b/rollup/l1/reader.go index eddc77d713..2902b48cae 100644 --- a/rollup/l1/reader.go +++ b/rollup/l1/reader.go @@ -139,20 +139,23 @@ func (r *Reader) FetchTxData(txHash, blockHash common.Hash) ([]byte, error) { if err != nil { return nil, err } + return tx.Data(), nil } -// FetchTxBlobHash fetches tx blob hash corresponding to given event log -func (r *Reader) FetchTxBlobHash(txHash, blockHash common.Hash) (common.Hash, error) { +// FetchTxBlobHashes fetches tx blob hash corresponding to given event log +func (r *Reader) FetchTxBlobHashes(txHash, blockHash common.Hash) ([]common.Hash, error) { tx, err := r.fetchTx(txHash, blockHash) if err != nil { - return common.Hash{}, err + return nil, fmt.Errorf("failed to fetch tx, tx hash: %v, block hash: %v, err: %w", txHash.Hex(), blockHash.Hex(), err) } + blobHashes := tx.BlobHashes() if len(blobHashes) == 0 { - return common.Hash{}, fmt.Errorf("transaction does not contain any blobs, tx hash: %v", txHash.Hex()) + return nil, fmt.Errorf("transaction does not contain any blobs, tx hash: %v", txHash.Hex()) } - return blobHashes[0], nil + + return blobHashes, nil } // FetchRollupEventsInRange retrieves and parses commit/revert/finalize rollup events between block numbers: [from, to]. diff --git a/rollup/l1/types.go b/rollup/l1/types.go index 8c030815ec..0adb734bac 100644 --- a/rollup/l1/types.go +++ b/rollup/l1/types.go @@ -20,3 +20,41 @@ type Client interface { BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) } + +type MockNopClient struct{} + +func (m *MockNopClient) BlockNumber(ctx context.Context) (uint64, error) { + return 0, nil +} + +func (m *MockNopClient) ChainID(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + +func (m *MockNopClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return nil, nil +} + +func (m *MockNopClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return nil, nil +} + +func (m *MockNopClient) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + return nil, nil +} + +func (m *MockNopClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + return nil, nil +} + +func (m *MockNopClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) { + return nil, false, nil +} + +func (m *MockNopClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return nil, nil +} + +func (m *MockNopClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return nil, nil +} diff --git a/rollup/rollup_sync_service/abi.go b/rollup/rollup_sync_service/abi.go deleted file mode 100644 index 428413dec9..0000000000 --- a/rollup/rollup_sync_service/abi.go +++ /dev/null @@ -1,55 +0,0 @@ -package rollup_sync_service - -import ( - "fmt" - "math/big" - - "github.com/scroll-tech/go-ethereum/accounts/abi" - "github.com/scroll-tech/go-ethereum/accounts/abi/bind" - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" -) - -// ScrollChainMetaData contains ABI of the ScrollChain contract. -var ScrollChainMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"},{\"indexed\": true,\"internalType\": \"bytes32\",\"name\": \"batchHash\",\"type\": \"bytes32\"}],\"name\": \"CommitBatch\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"},{\"indexed\": true,\"internalType\": \"bytes32\",\"name\": \"batchHash\",\"type\": \"bytes32\"},{\"indexed\": false,\"internalType\": \"bytes32\",\"name\": \"stateRoot\",\"type\": \"bytes32\"},{\"indexed\": false,\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"}],\"name\": \"FinalizeBatch\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"},{\"indexed\": true,\"internalType\": \"bytes32\",\"name\": \"batchHash\",\"type\": \"bytes32\"}],\"name\": \"RevertBatch\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": false,\"internalType\": \"uint256\",\"name\": \"oldMaxNumTxInChunk\",\"type\": \"uint256\"},{\"indexed\": false,\"internalType\": \"uint256\",\"name\": \"newMaxNumTxInChunk\",\"type\": \"uint256\"}],\"name\": \"UpdateMaxNumTxInChunk\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"address\",\"name\": \"account\",\"type\": \"address\"},{\"indexed\": false,\"internalType\": \"bool\",\"name\": \"status\",\"type\": \"bool\"}],\"name\": \"UpdateProver\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"address\",\"name\": \"account\",\"type\": \"address\"},{\"indexed\": false,\"internalType\": \"bool\",\"name\": \"status\",\"type\": \"bool\"}],\"name\": \"UpdateSequencer\",\"type\": \"event\"},{\"inputs\": [{\"internalType\": \"uint8\",\"name\": \"version\",\"type\": \"uint8\"},{\"internalType\": \"bytes\",\"name\": \"parentBatchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes[]\",\"name\": \"chunks\",\"type\": \"bytes[]\"},{\"internalType\": \"bytes\",\"name\": \"skippedL1MessageBitmap\",\"type\": \"bytes\"}],\"name\": \"commitBatch\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"uint8\",\"name\": \"version\",\"type\": \"uint8\"},{\"internalType\": \"bytes\",\"name\": \"parentBatchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes[]\",\"name\": \"chunks\",\"type\": \"bytes[]\"},{\"internalType\": \"bytes\",\"name\": \"skippedL1MessageBitmap\",\"type\": \"bytes\"},{\"internalType\": \"bytes\",\"name\": \"blobDataProof\",\"type\": \"bytes\"}],\"name\": \"commitBatchWithBlobProof\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"}],\"name\": \"committedBatches\",\"outputs\": [{\"internalType\": \"bytes32\",\"name\": \"\",\"type\": \"bytes32\"}],\"stateMutability\": \"view\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"prevStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"}],\"name\": \"finalizeBatch\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"prevStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes\",\"name\": \"blobDataProof\",\"type\": \"bytes\"}],\"name\": \"finalizeBatch4844\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"prevStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes\",\"name\": \"aggrProof\",\"type\": \"bytes\"}],\"name\": \"finalizeBatchWithProof\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"prevStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes\",\"name\": \"blobDataProof\",\"type\": \"bytes\"},{\"internalType\": \"bytes\",\"name\": \"aggrProof\",\"type\": \"bytes\"}],\"name\": \"finalizeBatchWithProof4844\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"}],\"name\": \"finalizeBundle\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"postStateRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"withdrawRoot\",\"type\": \"bytes32\"},{\"internalType\": \"bytes\",\"name\": \"aggrProof\",\"type\": \"bytes\"}],\"name\": \"finalizeBundleWithProof\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"}],\"name\": \"finalizedStateRoots\",\"outputs\": [{\"internalType\": \"bytes32\",\"name\": \"\",\"type\": \"bytes32\"}],\"stateMutability\": \"view\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"_batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"bytes32\",\"name\": \"_stateRoot\",\"type\": \"bytes32\"}],\"name\": \"importGenesisBatch\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"}],\"name\": \"isBatchFinalized\",\"outputs\": [{\"internalType\": \"bool\",\"name\": \"\",\"type\": \"bool\"}],\"stateMutability\": \"view\",\"type\": \"function\"},{\"inputs\": [],\"name\": \"lastFinalizedBatchIndex\",\"outputs\": [{\"internalType\": \"uint256\",\"name\": \"\",\"type\": \"uint256\"}],\"stateMutability\": \"view\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"batchHeader\",\"type\": \"bytes\"},{\"internalType\": \"uint256\",\"name\": \"count\",\"type\": \"uint256\"}],\"name\": \"revertBatch\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"uint256\",\"name\": \"batchIndex\",\"type\": \"uint256\"}],\"name\": \"withdrawRoots\",\"outputs\": [{\"internalType\": \"bytes32\",\"name\": \"\",\"type\": \"bytes32\"}],\"stateMutability\": \"view\",\"type\": \"function\"}]", -} - -// L1CommitBatchEvent represents a CommitBatch event raised by the ScrollChain contract. -type L1CommitBatchEvent struct { - BatchIndex *big.Int - BatchHash common.Hash -} - -// L1RevertBatchEvent represents a RevertBatch event raised by the ScrollChain contract. -type L1RevertBatchEvent struct { - BatchIndex *big.Int - BatchHash common.Hash -} - -// L1FinalizeBatchEvent represents a FinalizeBatch event raised by the ScrollChain contract. -type L1FinalizeBatchEvent struct { - BatchIndex *big.Int - BatchHash common.Hash - StateRoot common.Hash - WithdrawRoot common.Hash -} - -// UnpackLog unpacks a retrieved log into the provided output structure. -func UnpackLog(c *abi.ABI, out interface{}, event string, log types.Log) error { - if log.Topics[0] != c.Events[event].ID { - return fmt.Errorf("event signature mismatch") - } - if len(log.Data) > 0 { - if err := c.UnpackIntoInterface(out, event, log.Data); err != nil { - return err - } - } - var indexed abi.Arguments - for _, arg := range c.Events[event].Inputs { - if arg.Indexed { - indexed = append(indexed, arg) - } - } - return abi.ParseTopics(out, indexed, log.Topics[1:]) -} diff --git a/rollup/rollup_sync_service/abi_test.go b/rollup/rollup_sync_service/abi_test.go deleted file mode 100644 index 550c950bb3..0000000000 --- a/rollup/rollup_sync_service/abi_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package rollup_sync_service - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/crypto" -) - -func TestEventSignatures(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - if err != nil { - t.Fatal("failed to get scroll chain abi", "err", err) - } - - assert.Equal(t, crypto.Keccak256Hash([]byte("CommitBatch(uint256,bytes32)")), scrollChainABI.Events["CommitBatch"].ID) - assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,bytes32)")), scrollChainABI.Events["RevertBatch"].ID) - assert.Equal(t, crypto.Keccak256Hash([]byte("FinalizeBatch(uint256,bytes32,bytes32,bytes32)")), scrollChainABI.Events["FinalizeBatch"].ID) -} - -func TestUnpackLog(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - require.NoError(t, err) - - mockBatchIndex := big.NewInt(123) - mockBatchHash := crypto.Keccak256Hash([]byte("mockBatch")) - mockStateRoot := crypto.Keccak256Hash([]byte("mockStateRoot")) - mockWithdrawRoot := crypto.Keccak256Hash([]byte("mockWithdrawRoot")) - - tests := []struct { - eventName string - mockLog types.Log - expected interface{} - out interface{} - }{ - { - "CommitBatch", - types.Log{ - Data: []byte{}, - Topics: []common.Hash{scrollChainABI.Events["CommitBatch"].ID, common.BigToHash(mockBatchIndex), mockBatchHash}, - }, - &L1CommitBatchEvent{BatchIndex: mockBatchIndex, BatchHash: mockBatchHash}, - &L1CommitBatchEvent{}, - }, - { - "RevertBatch", - types.Log{ - Data: []byte{}, - Topics: []common.Hash{scrollChainABI.Events["RevertBatch"].ID, common.BigToHash(mockBatchIndex), mockBatchHash}, - }, - &L1RevertBatchEvent{BatchIndex: mockBatchIndex, BatchHash: mockBatchHash}, - &L1RevertBatchEvent{}, - }, - { - "FinalizeBatch", - types.Log{ - Data: append(mockStateRoot.Bytes(), mockWithdrawRoot.Bytes()...), - Topics: []common.Hash{scrollChainABI.Events["FinalizeBatch"].ID, common.BigToHash(mockBatchIndex), mockBatchHash}, - }, - &L1FinalizeBatchEvent{ - BatchIndex: mockBatchIndex, - BatchHash: mockBatchHash, - StateRoot: mockStateRoot, - WithdrawRoot: mockWithdrawRoot, - }, - &L1FinalizeBatchEvent{}, - }, - } - - for _, tt := range tests { - t.Run(tt.eventName, func(t *testing.T) { - err := UnpackLog(scrollChainABI, tt.out, tt.eventName, tt.mockLog) - assert.NoError(t, err) - assert.Equal(t, tt.expected, tt.out) - }) - } -} diff --git a/rollup/rollup_sync_service/l1client.go b/rollup/rollup_sync_service/l1client.go deleted file mode 100644 index b6be3e0bc6..0000000000 --- a/rollup/rollup_sync_service/l1client.go +++ /dev/null @@ -1,158 +0,0 @@ -package rollup_sync_service - -import ( - "context" - "errors" - "fmt" - "math/big" - - "github.com/scroll-tech/go-ethereum" - "github.com/scroll-tech/go-ethereum/accounts/abi" - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/log" - "github.com/scroll-tech/go-ethereum/rpc" - - "github.com/scroll-tech/go-ethereum/rollup/sync_service" -) - -// L1Client is a wrapper around EthClient that adds -// methods for conveniently collecting rollup events of ScrollChain contract. -type L1Client struct { - ctx context.Context - client sync_service.EthClient - scrollChainAddress common.Address - l1CommitBatchEventSignature common.Hash - l1RevertBatchEventSignature common.Hash - l1FinalizeBatchEventSignature common.Hash -} - -// NewL1Client initializes a new L1Client instance with the provided configuration. -// It checks for a valid scrollChainAddress and verifies the chain ID. -func NewL1Client(ctx context.Context, l1Client sync_service.EthClient, l1ChainId uint64, scrollChainAddress common.Address, scrollChainABI *abi.ABI) (*L1Client, error) { - if scrollChainAddress == (common.Address{}) { - return nil, errors.New("must pass non-zero scrollChainAddress to L1Client") - } - - // sanity check: compare chain IDs - got, err := l1Client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("failed to query L1 chain ID, err: %w", err) - } - if got.Cmp(big.NewInt(0).SetUint64(l1ChainId)) != 0 { - return nil, fmt.Errorf("unexpected chain ID, expected: %v, got: %v", l1ChainId, got) - } - - client := L1Client{ - ctx: ctx, - client: l1Client, - scrollChainAddress: scrollChainAddress, - l1CommitBatchEventSignature: scrollChainABI.Events["CommitBatch"].ID, - l1RevertBatchEventSignature: scrollChainABI.Events["RevertBatch"].ID, - l1FinalizeBatchEventSignature: scrollChainABI.Events["FinalizeBatch"].ID, - } - - return &client, nil -} - -// FetchRollupEventsInRange retrieves and parses commit/revert/finalize rollup events between block numbers: [from, to]. -func (c *L1Client) FetchRollupEventsInRange(from, to uint64) ([]types.Log, error) { - log.Trace("L1Client FetchRollupEventsInRange", "fromBlock", from, "toBlock", to) - - query := ethereum.FilterQuery{ - FromBlock: big.NewInt(int64(from)), // inclusive - ToBlock: big.NewInt(int64(to)), // inclusive - Addresses: []common.Address{ - c.scrollChainAddress, - }, - Topics: make([][]common.Hash, 1), - } - query.Topics[0] = make([]common.Hash, 3) - query.Topics[0][0] = c.l1CommitBatchEventSignature - query.Topics[0][1] = c.l1RevertBatchEventSignature - query.Topics[0][2] = c.l1FinalizeBatchEventSignature - - logs, err := c.client.FilterLogs(c.ctx, query) - if err != nil { - return nil, fmt.Errorf("failed to filter logs, err: %w", err) - } - return logs, nil -} - -// GetLatestFinalizedBlockNumber fetches the block number of the latest finalized block from the L1 chain. -func (c *L1Client) GetLatestFinalizedBlockNumber() (uint64, error) { - header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) - if err != nil { - return 0, err - } - if !header.Number.IsInt64() { - return 0, fmt.Errorf("received unexpected block number in L1Client: %v", header.Number) - } - return header.Number.Uint64(), nil -} - -// FetchTxData fetches tx data corresponding to given event log -func (c *L1Client) FetchTxData(vLog *types.Log) ([]byte, error) { - tx, _, err := c.client.TransactionByHash(c.ctx, vLog.TxHash) - if err != nil { - log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction", - "tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err) - block, err := c.client.BlockByHash(c.ctx, vLog.BlockHash) - if err != nil { - return nil, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err) - } - - found := false - for _, txInBlock := range block.Transactions() { - if txInBlock.Hash() == vLog.TxHash { - tx = txInBlock - found = true - break - } - } - if !found { - return nil, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex()) - } - } - - return tx.Data(), nil -} - -// FetchTxBlobHash fetches tx blob hash corresponding to given event log -func (c *L1Client) FetchTxBlobHash(vLog *types.Log) (common.Hash, error) { - tx, _, err := c.client.TransactionByHash(c.ctx, vLog.TxHash) - if err != nil { - log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction", - "tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err) - block, err := c.client.BlockByHash(c.ctx, vLog.BlockHash) - if err != nil { - return common.Hash{}, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err) - } - - found := false - for _, txInBlock := range block.Transactions() { - if txInBlock.Hash() == vLog.TxHash { - tx = txInBlock - found = true - break - } - } - if !found { - return common.Hash{}, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex()) - } - } - blobHashes := tx.BlobHashes() - if len(blobHashes) == 0 { - return common.Hash{}, fmt.Errorf("transaction does not contain any blobs, tx hash: %v", vLog.TxHash.Hex()) - } - return blobHashes[0], nil -} - -// GetHeaderByNumber fetches the block header by number -func (c *L1Client) GetHeaderByNumber(blockNumber uint64) (*types.Header, error) { - header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(0).SetUint64(blockNumber)) - if err != nil { - return nil, err - } - return header, nil -} diff --git a/rollup/rollup_sync_service/l1client_test.go b/rollup/rollup_sync_service/l1client_test.go deleted file mode 100644 index 394f455b80..0000000000 --- a/rollup/rollup_sync_service/l1client_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package rollup_sync_service - -import ( - "context" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/scroll-tech/go-ethereum" - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/rlp" -) - -func TestL1Client(t *testing.T) { - ctx := context.Background() - mockClient := &mockEthClient{} - - scrollChainABI, err := ScrollChainMetaData.GetAbi() - if err != nil { - t.Fatal("failed to get scroll chain abi", "err", err) - } - scrollChainAddress := common.HexToAddress("0x0123456789abcdef") - l1Client, err := NewL1Client(ctx, mockClient, 11155111, scrollChainAddress, scrollChainABI) - require.NoError(t, err, "Failed to initialize L1Client") - - blockNumber, err := l1Client.GetLatestFinalizedBlockNumber() - assert.NoError(t, err, "Error getting latest confirmed block number") - assert.Equal(t, uint64(36), blockNumber, "Unexpected block number") - - logs, err := l1Client.FetchRollupEventsInRange(0, blockNumber) - assert.NoError(t, err, "Error fetching rollup events in range") - assert.Empty(t, logs, "Expected no logs from FetchRollupEventsInRange") -} - -type mockEthClient struct { - txRLP []byte -} - -func (m *mockEthClient) BlockNumber(ctx context.Context) (uint64, error) { - return 11155111, nil -} - -func (m *mockEthClient) ChainID(ctx context.Context) (*big.Int, error) { - return big.NewInt(11155111), nil -} - -func (m *mockEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - return []types.Log{}, nil -} - -func (m *mockEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - return &types.Header{ - Number: big.NewInt(100 - 64), - }, nil -} - -func (m *mockEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - return nil, nil -} - -func (m *mockEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) { - var tx types.Transaction - if err := rlp.DecodeBytes(m.txRLP, &tx); err != nil { - return nil, false, err - } - return &tx, false, nil -} - -func (m *mockEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return nil, nil -} diff --git a/rollup/rollup_sync_service/rollup_sync_service.go b/rollup/rollup_sync_service/rollup_sync_service.go index bbb2b49403..406895be11 100644 --- a/rollup/rollup_sync_service/rollup_sync_service.go +++ b/rollup/rollup_sync_service/rollup_sync_service.go @@ -3,34 +3,31 @@ package rollup_sync_service import ( "context" "encoding/json" + "errors" "fmt" "os" - "reflect" "sync" "time" "github.com/scroll-tech/da-codec/encoding" - "github.com/scroll-tech/go-ethereum/accounts/abi" - "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/rawdb" - "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/node" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/da_syncer" + "github.com/scroll-tech/go-ethereum/rollup/da_syncer/blob_client" + "github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" + "github.com/scroll-tech/go-ethereum/rollup/l1" "github.com/scroll-tech/go-ethereum/rollup/rcfg" - "github.com/scroll-tech/go-ethereum/rollup/sync_service" "github.com/scroll-tech/go-ethereum/rollup/withdrawtrie" ) const ( - // defaultFetchBlockRange is the number of blocks that we collect in a single eth_getLogs query. - defaultFetchBlockRange = uint64(100) - // defaultSyncInterval is the frequency at which we query for new rollup event. - defaultSyncInterval = 60 * time.Second + defaultSyncInterval = 30 * time.Second // defaultMaxRetries is the maximum number of retries allowed when the local node is not synced up to the required block height. defaultMaxRetries = 20 @@ -40,47 +37,27 @@ const ( // of a specific L1 batch finalize event. defaultGetBlockInRangeRetryDelay = 60 * time.Second - // defaultLogInterval is the frequency at which we print the latestProcessedBlock. + // defaultLogInterval is the frequency at which we print the latest processed block. defaultLogInterval = 5 * time.Minute ) // RollupSyncService collects ScrollChain batch commit/revert/finalize events and stores metadata into db. type RollupSyncService struct { - ctx context.Context - cancel context.CancelFunc - client *L1Client - db ethdb.Database - latestProcessedBlock uint64 - scrollChainABI *abi.ABI - l1CommitBatchEventSignature common.Hash - l1RevertBatchEventSignature common.Hash - l1FinalizeBatchEventSignature common.Hash - bc *core.BlockChain - stack *node.Node - stateMu sync.Mutex + ctx context.Context + cancel context.CancelFunc + db ethdb.Database + bc *core.BlockChain + stack *node.Node + stateMu sync.Mutex + + callDataBlobSource *da.CalldataBlobSource } -func NewRollupSyncService(ctx context.Context, genesisConfig *params.ChainConfig, db ethdb.Database, l1Client sync_service.EthClient, bc *core.BlockChain, stack *node.Node) (*RollupSyncService, error) { - // terminate if the caller does not provide an L1 client (e.g. in tests) - if l1Client == nil || (reflect.ValueOf(l1Client).Kind() == reflect.Ptr && reflect.ValueOf(l1Client).IsNil()) { - log.Warn("No L1 client provided, L1 rollup sync service will not run") - return nil, nil - } - +func NewRollupSyncService(ctx context.Context, genesisConfig *params.ChainConfig, db ethdb.Database, l1Client l1.Client, bc *core.BlockChain, stack *node.Node, config da_syncer.Config) (*RollupSyncService, error) { if genesisConfig.Scroll.L1Config == nil { return nil, fmt.Errorf("missing L1 config in genesis") } - scrollChainABI, err := ScrollChainMetaData.GetAbi() - if err != nil { - return nil, fmt.Errorf("failed to get scroll chain abi: %w", err) - } - - client, err := NewL1Client(ctx, l1Client, genesisConfig.Scroll.L1Config.L1ChainId, genesisConfig.Scroll.L1Config.ScrollChainAddress, scrollChainABI) - if err != nil { - return nil, fmt.Errorf("failed to initialize l1 client: %w", err) - } - // Initialize the latestProcessedBlock with the block just before the L1 deployment block. // This serves as a default value when there's no L1 rollup events synced in the database. var latestProcessedBlock uint64 @@ -94,23 +71,57 @@ func NewRollupSyncService(ctx context.Context, genesisConfig *params.ChainConfig latestProcessedBlock = *block } + var success bool ctx, cancel := context.WithCancel(ctx) + defer func() { + if !success { + cancel() + } + }() - service := RollupSyncService{ - ctx: ctx, - cancel: cancel, - client: client, - db: db, - latestProcessedBlock: latestProcessedBlock, - scrollChainABI: scrollChainABI, - l1CommitBatchEventSignature: scrollChainABI.Events["CommitBatch"].ID, - l1RevertBatchEventSignature: scrollChainABI.Events["RevertBatch"].ID, - l1FinalizeBatchEventSignature: scrollChainABI.Events["FinalizeBatch"].ID, - bc: bc, - stack: stack, + l1Reader, err := l1.NewReader(ctx, l1.Config{ + ScrollChainAddress: genesisConfig.Scroll.L1Config.ScrollChainAddress, + L1MessageQueueAddress: genesisConfig.Scroll.L1Config.L1MessageQueueAddress, + }, l1Client) + if err != nil { + return nil, fmt.Errorf("failed to initialize l1.Reader, err = %w", err) } - return &service, nil + blobClientList := blob_client.NewBlobClients() + if config.BeaconNodeAPIEndpoint != "" { + beaconNodeClient, err := blob_client.NewBeaconNodeClient(config.BeaconNodeAPIEndpoint) + if err != nil { + log.Warn("failed to create BeaconNodeClient", "err", err) + } else { + blobClientList.AddBlobClient(beaconNodeClient) + } + } + if config.BlobScanAPIEndpoint != "" { + blobClientList.AddBlobClient(blob_client.NewBlobScanClient(config.BlobScanAPIEndpoint)) + } + if config.BlockNativeAPIEndpoint != "" { + blobClientList.AddBlobClient(blob_client.NewBlockNativeClient(config.BlockNativeAPIEndpoint)) + } + if blobClientList.Size() == 0 { + return nil, errors.New("no blob client is configured for rollup verifier. Please provide at least one blob client via command line flag") + } + + calldataBlobSource, err := da.NewCalldataBlobSource(ctx, latestProcessedBlock, l1Reader, blobClientList, db) + if err != nil { + return nil, fmt.Errorf("failed to create calldata blob source: %w", err) + } + + success = true + + return &RollupSyncService{ + ctx: ctx, + cancel: cancel, + db: db, + bc: bc, + stack: stack, + + callDataBlobSource: calldataBlobSource, + }, nil } func (s *RollupSyncService) Start() { @@ -118,7 +129,7 @@ func (s *RollupSyncService) Start() { return } - log.Info("Starting rollup event sync background service", "latest processed block", s.latestProcessedBlock) + log.Info("Starting rollup event sync background service", "latest processed block", s.callDataBlobSource.L1Height()) go func() { syncTicker := time.NewTicker(defaultSyncInterval) @@ -132,9 +143,12 @@ func (s *RollupSyncService) Start() { case <-s.ctx.Done(): return case <-syncTicker.C: - s.fetchRollupEvents() + err := s.fetchRollupEvents() + if err != nil { + log.Error("failed to fetch rollup events", "err", err) + } case <-logTicker.C: - log.Info("Sync rollup events progress update", "latestProcessedBlock", s.latestProcessedBlock) + log.Info("Sync rollup events progress update", "latest processed block", s.callDataBlobSource.L1Height()) } } }() @@ -161,90 +175,79 @@ func (s *RollupSyncService) ResetStartSyncHeight(height uint64) { s.stateMu.Lock() defer s.stateMu.Unlock() - s.latestProcessedBlock = height + s.callDataBlobSource.SetL1Height(height) log.Info("Reset sync service", "height", height) } -func (s *RollupSyncService) fetchRollupEvents() { +func (s *RollupSyncService) fetchRollupEvents() error { s.stateMu.Lock() defer s.stateMu.Unlock() - latestConfirmed, err := s.client.GetLatestFinalizedBlockNumber() - if err != nil { - log.Warn("failed to get latest confirmed block number", "err", err) - return - } + for { + prevL1Height := s.callDataBlobSource.L1Height() - log.Trace("Sync service fetch rollup events", "latest processed block", s.latestProcessedBlock, "latest confirmed", latestConfirmed) - - // query in batches - for from := s.latestProcessedBlock + 1; from <= latestConfirmed; from += defaultFetchBlockRange { - if s.ctx.Err() != nil { - log.Info("Context canceled", "reason", s.ctx.Err()) - return - } - - to := from + defaultFetchBlockRange - 1 - if to > latestConfirmed { - to = latestConfirmed - } - - logs, err := s.client.FetchRollupEventsInRange(from, to) + daEntries, err := s.callDataBlobSource.NextData() if err != nil { - log.Error("failed to fetch rollup events in range", "from block", from, "to block", to, "err", err) - return + if errors.Is(err, da.ErrSourceExhausted) { + log.Trace("Sync service exhausted data source, waiting for next data") + return nil + } + + return fmt.Errorf("failed to get next data: %w", err) } - if err := s.parseAndUpdateRollupEventLogs(logs, to); err != nil { - log.Error("failed to parse and update rollup event logs", "err", err) - return + if err = s.updateRollupEvents(daEntries); err != nil { + // Reset the L1 height to the previous value to retry fetching the same data. + s.callDataBlobSource.SetL1Height(prevL1Height) + return fmt.Errorf("failed to parse and update rollup event logs: %w", err) } - s.latestProcessedBlock = to + log.Trace("Sync service fetched rollup events", "latest processed L1 block", s.callDataBlobSource.L1Height(), "latest finalized L1 block", s.callDataBlobSource.L1Finalized()) + + // note: the batch updates in updateRollupEvents are idempotent, if we crash + // before this line and re-execute the previous steps, we will get the same result. + rawdb.WriteRollupEventSyncedL1BlockNumber(s.db, s.callDataBlobSource.L1Height()) } } -func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endBlockNumber uint64) error { - for _, vLog := range logs { - switch vLog.Topics[0] { - case s.l1CommitBatchEventSignature: - event := &L1CommitBatchEvent{} - if err := UnpackLog(s.scrollChainABI, event, "CommitBatch", vLog); err != nil { - return fmt.Errorf("failed to unpack commit rollup event log, err: %w", err) - } - batchIndex := event.BatchIndex.Uint64() - log.Trace("found new CommitBatch event", "batch index", batchIndex) +func (s *RollupSyncService) updateRollupEvents(daEntries da.Entries) error { + for _, entry := range daEntries { + switch entry.Type() { + case da.CommitBatchV0Type, da.CommitBatchWithBlobType: + log.Trace("found new CommitBatch event", "batch index", entry.BatchIndex()) - committedBatchMeta, err := s.getCommittedBatchMeta(batchIndex, &vLog) + entryWithBlocks, ok := entry.(da.EntryWithBlocks) + if !ok { + return fmt.Errorf("failed to cast to EntryWithBlocks, batch index: %v", entry.BatchIndex()) + } + + committedBatchMeta, err := s.getCommittedBatchMeta(entryWithBlocks) if err != nil { - return fmt.Errorf("failed to get chunk ranges, batch index: %v, err: %w", batchIndex, err) + return fmt.Errorf("failed to get committed batch meta, batch index: %v, err: %w", entry.BatchIndex(), err) } - rawdb.WriteCommittedBatchMeta(s.db, batchIndex, committedBatchMeta) - case s.l1RevertBatchEventSignature: - event := &L1RevertBatchEvent{} - if err := UnpackLog(s.scrollChainABI, event, "RevertBatch", vLog); err != nil { - return fmt.Errorf("failed to unpack revert rollup event log, err: %w", err) + rawdb.WriteCommittedBatchMeta(s.db, entry.BatchIndex(), committedBatchMeta) + + case da.RevertBatchType: + log.Trace("found new RevertBatch event", "batch index", entry.BatchIndex()) + rawdb.DeleteCommittedBatchMeta(s.db, entry.BatchIndex()) + + case da.FinalizeBatchType: + event, ok := entry.Event().(*l1.FinalizeBatchEvent) + // This should never happen because we just checked the batch type + if !ok { + return fmt.Errorf("failed to cast to FinalizeBatchEvent, batch index: %v", entry.BatchIndex()) } - batchIndex := event.BatchIndex.Uint64() - log.Trace("found new RevertBatch event", "batch index", batchIndex) - rawdb.DeleteCommittedBatchMeta(s.db, batchIndex) - - case s.l1FinalizeBatchEventSignature: - event := &L1FinalizeBatchEvent{} - if err := UnpackLog(s.scrollChainABI, event, "FinalizeBatch", vLog); err != nil { - return fmt.Errorf("failed to unpack finalized rollup event log, err: %w", err) - } - batchIndex := event.BatchIndex.Uint64() + batchIndex := entry.BatchIndex() log.Trace("found new FinalizeBatch event", "batch index", batchIndex) lastFinalizedBatchIndex := rawdb.ReadLastFinalizedBatchIndex(s.db) - // After darwin, FinalizeBatch event emitted every bundle, which contains multiple batches. - // Therefore there are a range of finalized batches need to be saved into db. + // After Darwin, FinalizeBatch event emitted every bundle, which contains multiple batches. + // Therefore, there are a range of finalized batches need to be saved into db. // - // The range logic also applies to the batches before darwin when FinalizeBatch event emitted + // The range logic also applies to the batches before Darwin when FinalizeBatch event emitted // per single batch. In this situation, `batchIndex` just equals to `*lastFinalizedBatchIndex + 1` // and only one batch is processed through the for loop. startBatchIndex := batchIndex @@ -293,14 +296,10 @@ func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endB log.Debug("write finalized l2 block number", "batch index", batchIndex, "finalized l2 block height", highestFinalizedBlockNumber) default: - return fmt.Errorf("unknown event, topic: %v, tx hash: %v", vLog.Topics[0].Hex(), vLog.TxHash.Hex()) + return fmt.Errorf("unknown daEntry, type: %d, batch index: %d", entry.Type(), entry.BatchIndex()) } } - // note: the batch updates above are idempotent, if we crash - // before this line and reexecute the previous steps, we will - // get the same result. - rawdb.WriteRollupEventSyncedL1BlockNumber(s.db, endBlockNumber) return nil } @@ -355,8 +354,8 @@ func (s *RollupSyncService) getLocalChunksForBatch(chunkBlockRanges []*rawdb.Chu return chunks, nil } -func (s *RollupSyncService) getCommittedBatchMeta(batchIndex uint64, vLog *types.Log) (*rawdb.CommittedBatchMeta, error) { - if batchIndex == 0 { +func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBlocks) (*rawdb.CommittedBatchMeta, error) { + if commitedBatch.BatchIndex() == 0 { return &rawdb.CommittedBatchMeta{ Version: 0, BlobVersionedHashes: nil, @@ -364,111 +363,16 @@ func (s *RollupSyncService) getCommittedBatchMeta(batchIndex uint64, vLog *types }, nil } - tx, _, err := s.client.client.TransactionByHash(s.ctx, vLog.TxHash) + chunkRanges, err := blockRangesFromChunks(commitedBatch.Chunks()) if err != nil { - log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction", - "tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err) - block, err := s.client.client.BlockByHash(s.ctx, vLog.BlockHash) - if err != nil { - return nil, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err) - } - - if block == nil { - return nil, fmt.Errorf("failed to get block by hash, block not found, block number: %v, block hash: %v", vLog.BlockNumber, vLog.BlockHash.Hex()) - } - - found := false - for _, txInBlock := range block.Transactions() { - if txInBlock.Hash() == vLog.TxHash { - tx = txInBlock - found = true - break - } - } - if !found { - return nil, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex()) - } + return nil, fmt.Errorf("failed to decode block ranges from chunks, batch index: %v, err: %w", commitedBatch.BatchIndex(), err) } - var commitBatchMeta rawdb.CommittedBatchMeta - - if tx.Type() == types.BlobTxType { - blobVersionedHashes := tx.BlobHashes() - if blobVersionedHashes == nil { - return nil, fmt.Errorf("invalid blob transaction, blob hashes is nil, tx hash: %v", tx.Hash().Hex()) - } - commitBatchMeta.BlobVersionedHashes = blobVersionedHashes - } - - version, ranges, err := s.decodeBatchVersionAndChunkBlockRanges(tx.Data()) - if err != nil { - return nil, fmt.Errorf("failed to decode chunk block ranges, batch index: %v, err: %w", batchIndex, err) - } - - commitBatchMeta.Version = version - commitBatchMeta.ChunkBlockRanges = ranges - return &commitBatchMeta, nil -} - -// decodeBatchVersionAndChunkBlockRanges decodes version and chunks' block ranges in a batch based on the commit batch transaction's calldata. -func (s *RollupSyncService) decodeBatchVersionAndChunkBlockRanges(txData []byte) (uint8, []*rawdb.ChunkBlockRange, error) { - const methodIDLength = 4 - if len(txData) < methodIDLength { - return 0, nil, fmt.Errorf("transaction data is too short, length of tx data: %v, minimum length required: %v", len(txData), methodIDLength) - } - - method, err := s.scrollChainABI.MethodById(txData[:methodIDLength]) - if err != nil { - return 0, nil, fmt.Errorf("failed to get method by ID, ID: %v, err: %w", txData[:methodIDLength], err) - } - - values, err := method.Inputs.Unpack(txData[methodIDLength:]) - if err != nil { - return 0, nil, fmt.Errorf("failed to unpack transaction data using ABI, tx data: %v, err: %w", txData, err) - } - - if method.Name == "commitBatch" { - type commitBatchArgs struct { - Version uint8 - ParentBatchHeader []byte - Chunks [][]byte - SkippedL1MessageBitmap []byte - } - - var args commitBatchArgs - if err = method.Inputs.Copy(&args, values); err != nil { - return 0, nil, fmt.Errorf("failed to decode calldata into commitBatch args, values: %+v, err: %w", values, err) - } - - chunkRanges, err := decodeBlockRangesFromEncodedChunks(encoding.CodecVersion(args.Version), args.Chunks) - if err != nil { - return 0, nil, fmt.Errorf("failed to decode block ranges from encoded chunks, version: %v, chunks: %+v, err: %w", args.Version, args.Chunks, err) - } - - return args.Version, chunkRanges, nil - } else if method.Name == "commitBatchWithBlobProof" { - type commitBatchWithBlobProofArgs struct { - Version uint8 - ParentBatchHeader []byte - Chunks [][]byte - SkippedL1MessageBitmap []byte - BlobDataProof []byte - } - - var args commitBatchWithBlobProofArgs - if err = method.Inputs.Copy(&args, values); err != nil { - return 0, nil, fmt.Errorf("failed to decode calldata into commitBatchWithBlobProofArgs args, values: %+v, err: %w", values, err) - } - - chunkRanges, err := decodeBlockRangesFromEncodedChunks(encoding.CodecVersion(args.Version), args.Chunks) - if err != nil { - return 0, nil, fmt.Errorf("failed to decode block ranges from encoded chunks, version: %v, chunks: %+v, err: %w", args.Version, args.Chunks, err) - } - - return args.Version, chunkRanges, nil - } - - return 0, nil, fmt.Errorf("unexpected method name: %v", method.Name) + return &rawdb.CommittedBatchMeta{ + Version: uint8(commitedBatch.Version()), + ChunkBlockRanges: chunkRanges, + BlobVersionedHashes: commitedBatch.BlobVersionedHashes(), + }, nil } // validateBatch verifies the consistency between the L1 contract and L2 node data. @@ -494,7 +398,7 @@ func (s *RollupSyncService) decodeBatchVersionAndChunkBlockRanges(txData []byte) // Note: This function is compatible with both "finalize by batch" and "finalize by bundle" methods. // In "finalize by bundle", only the last batch of each bundle is fully verified. // This check still ensures the correctness of all batch hashes in the bundle due to the parent-child relationship between batch hashes. -func validateBatch(batchIndex uint64, event *L1FinalizeBatchEvent, parentFinalizedBatchMeta *rawdb.FinalizedBatchMeta, committedBatchMeta *rawdb.CommittedBatchMeta, chunks []*encoding.Chunk, stack *node.Node) (uint64, *rawdb.FinalizedBatchMeta, error) { +func validateBatch(batchIndex uint64, event *l1.FinalizeBatchEvent, parentFinalizedBatchMeta *rawdb.FinalizedBatchMeta, committedBatchMeta *rawdb.CommittedBatchMeta, chunks []*encoding.Chunk, stack *node.Node) (uint64, *rawdb.FinalizedBatchMeta, error) { if len(chunks) == 0 { return 0, nil, fmt.Errorf("invalid argument: length of chunks is 0, batch index: %v", batchIndex) } @@ -540,15 +444,15 @@ func validateBatch(batchIndex uint64, event *L1FinalizeBatchEvent, parentFinaliz // Only check when batch index matches the index of the event. This is compatible with both "finalize by batch" and "finalize by bundle": // - finalize by batch: check all batches // - finalize by bundle: check the last batch, because only one event (containing the info of the last batch) is emitted per bundle - if batchIndex == event.BatchIndex.Uint64() { - if localStateRoot != event.StateRoot { - log.Error("State root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "l1 finalized state root", event.StateRoot.Hex(), "l2 state root", localStateRoot.Hex()) + if batchIndex == event.BatchIndex().Uint64() { + if localStateRoot != event.StateRoot() { + log.Error("State root mismatch", "batch index", event.BatchIndex().Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "l1 finalized state root", event.StateRoot().Hex(), "l2 state root", localStateRoot.Hex()) stack.Close() os.Exit(1) } - if localWithdrawRoot != event.WithdrawRoot { - log.Error("Withdraw root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "l1 finalized withdraw root", event.WithdrawRoot.Hex(), "l2 withdraw root", localWithdrawRoot.Hex()) + if localWithdrawRoot != event.WithdrawRoot() { + log.Error("Withdraw root mismatch", "batch index", event.BatchIndex().Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "l1 finalized withdraw root", event.WithdrawRoot().Hex(), "l2 withdraw root", localWithdrawRoot.Hex()) stack.Close() os.Exit(1) } @@ -556,8 +460,8 @@ func validateBatch(batchIndex uint64, event *L1FinalizeBatchEvent, parentFinaliz // Verify batch hash // This check ensures the correctness of all batch hashes in the bundle // due to the parent-child relationship between batch hashes - if localBatchHash != event.BatchHash { - log.Error("Batch hash mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "parent TotalL1MessagePopped", parentFinalizedBatchMeta.TotalL1MessagePopped, "l1 finalized batch hash", event.BatchHash.Hex(), "l2 batch hash", localBatchHash.Hex()) + if localBatchHash != event.BatchHash() { + log.Error("Batch hash mismatch", "batch index", event.BatchIndex().Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentFinalizedBatchMeta.BatchHash.Hex(), "parent TotalL1MessagePopped", parentFinalizedBatchMeta.TotalL1MessagePopped, "l1 finalized batch hash", event.BatchHash().Hex(), "l2 batch hash", localBatchHash.Hex()) chunksJson, err := json.Marshal(chunks) if err != nil { log.Error("marshal chunks failed", "err", err) @@ -581,22 +485,12 @@ func validateBatch(batchIndex uint64, event *L1FinalizeBatchEvent, parentFinaliz return endBlock.Header.Number.Uint64(), finalizedBatchMeta, nil } -// decodeBlockRangesFromEncodedChunks decodes the provided chunks into a list of block ranges. -func decodeBlockRangesFromEncodedChunks(codecVersion encoding.CodecVersion, chunks [][]byte) ([]*rawdb.ChunkBlockRange, error) { - codec, err := encoding.CodecFromVersion(codecVersion) - if err != nil { - return nil, fmt.Errorf("failed to get codec from version: %v, err: %w", codecVersion, err) - } - - daChunksRawTx, err := codec.DecodeDAChunksRawTx(chunks) - if err != nil { - return nil, fmt.Errorf("failed to decode DA chunks, version: %v, err: %w", codecVersion, err) - } - +// blockRangesFromChunks decodes the provided chunks into a list of block ranges. +func blockRangesFromChunks(chunks []*encoding.DAChunkRawTx) ([]*rawdb.ChunkBlockRange, error) { var chunkBlockRanges []*rawdb.ChunkBlockRange - for _, daChunkRawTx := range daChunksRawTx { + for _, daChunkRawTx := range chunks { if len(daChunkRawTx.Blocks) == 0 { - return nil, fmt.Errorf("no blocks found in DA chunk, version: %v", codecVersion) + return nil, fmt.Errorf("no blocks found in DA chunk, chunk: %+v", daChunkRawTx) } chunkBlockRanges = append(chunkBlockRanges, &rawdb.ChunkBlockRange{ diff --git a/rollup/rollup_sync_service/rollup_sync_service_test.go b/rollup/rollup_sync_service/rollup_sync_service_test.go index f1b09a37a1..dca18285d2 100644 --- a/rollup/rollup_sync_service/rollup_sync_service_test.go +++ b/rollup/rollup_sync_service/rollup_sync_service_test.go @@ -2,12 +2,10 @@ package rollup_sync_service import ( "context" - "encoding/hex" "encoding/json" "math/big" "os" "testing" - "time" "github.com/scroll-tech/da-codec/encoding" "github.com/stretchr/testify/assert" @@ -16,415 +14,86 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/rawdb" - "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethdb/memorydb" "github.com/scroll-tech/go-ethereum/node" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/da_syncer" + "github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" + "github.com/scroll-tech/go-ethereum/rollup/l1" ) -func TestRollupSyncServiceStartAndStop(t *testing.T) { +func TestGetCommittedBatchMetaCodecV0(t *testing.T) { genesisConfig := ¶ms.ChainConfig{ Scroll: params.ScrollConfig{ L1Config: ¶ms.L1Config{ - L1ChainId: 11155111, - ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), - }, - }, - } - db := rawdb.NewDatabase(memorydb.New()) - l1Client := &mockEthClient{} - bc := &core.BlockChain{} - stack, err := node.New(&node.DefaultConfig) - if err != nil { - t.Fatalf("Failed to new P2P node: %v", err) - } - defer stack.Close() - service, err := NewRollupSyncService(context.Background(), genesisConfig, db, l1Client, bc, stack) - if err != nil { - t.Fatalf("Failed to new rollup sync service: %v", err) - } - - assert.NotNil(t, service) - service.Start() - time.Sleep(10 * time.Millisecond) - service.Stop() -} - -func TestDecodeBatchVersionAndChunkBlockRangesCodecv0(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - require.NoError(t, err) - - service := &RollupSyncService{ - scrollChainABI: scrollChainABI, - } - - data, err := os.ReadFile("./testdata/commitBatch_input_codecv0.json") - require.NoError(t, err, "Failed to read json file") - - type tx struct { - Input string `json:"input"` - } - var commitBatch tx - err = json.Unmarshal(data, &commitBatch) - require.NoError(t, err, "Failed to unmarshal transaction json") - - testTxData, err := hex.DecodeString(commitBatch.Input[2:]) - if err != nil { - t.Fatalf("Failed to decode string: %v", err) - } - - version, ranges, err := service.decodeBatchVersionAndChunkBlockRanges(testTxData) - if err != nil { - t.Fatalf("Failed to decode chunk ranges: %v", err) - } - - assert.Equal(t, encoding.CodecV0, encoding.CodecVersion(version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 4435142, EndBlockNumber: 4435142}, - {StartBlockNumber: 4435143, EndBlockNumber: 4435144}, - {StartBlockNumber: 4435145, EndBlockNumber: 4435145}, - {StartBlockNumber: 4435146, EndBlockNumber: 4435146}, - {StartBlockNumber: 4435147, EndBlockNumber: 4435147}, - {StartBlockNumber: 4435148, EndBlockNumber: 4435148}, - {StartBlockNumber: 4435149, EndBlockNumber: 4435150}, - {StartBlockNumber: 4435151, EndBlockNumber: 4435151}, - {StartBlockNumber: 4435152, EndBlockNumber: 4435152}, - {StartBlockNumber: 4435153, EndBlockNumber: 4435153}, - {StartBlockNumber: 4435154, EndBlockNumber: 4435154}, - {StartBlockNumber: 4435155, EndBlockNumber: 4435155}, - {StartBlockNumber: 4435156, EndBlockNumber: 4435156}, - {StartBlockNumber: 4435157, EndBlockNumber: 4435157}, - {StartBlockNumber: 4435158, EndBlockNumber: 4435158}, - } - - if len(expectedRanges) != len(ranges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(ranges)) - } - - for i := range ranges { - if *expectedRanges[i] != *ranges[i] { - t.Errorf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *ranges[i]) - } - } -} - -func TestDecodeBatchVersionAndChunkBlockRangesCodecv1(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - require.NoError(t, err) - - service := &RollupSyncService{ - scrollChainABI: scrollChainABI, - } - - data, err := os.ReadFile("./testdata/commitBatch_input_codecv1.json") - require.NoError(t, err, "Failed to read json file") - - type tx struct { - Input string `json:"input"` - } - var commitBatch tx - err = json.Unmarshal(data, &commitBatch) - require.NoError(t, err, "Failed to unmarshal transaction json") - - testTxData, err := hex.DecodeString(commitBatch.Input[2:]) - if err != nil { - t.Fatalf("Failed to decode string: %v", err) - } - - version, ranges, err := service.decodeBatchVersionAndChunkBlockRanges(testTxData) - if err != nil { - t.Fatalf("Failed to decode chunk ranges: %v", err) - } - - assert.Equal(t, encoding.CodecV1, encoding.CodecVersion(version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 1690, EndBlockNumber: 1780}, - {StartBlockNumber: 1781, EndBlockNumber: 1871}, - {StartBlockNumber: 1872, EndBlockNumber: 1962}, - {StartBlockNumber: 1963, EndBlockNumber: 2053}, - {StartBlockNumber: 2054, EndBlockNumber: 2144}, - {StartBlockNumber: 2145, EndBlockNumber: 2235}, - {StartBlockNumber: 2236, EndBlockNumber: 2326}, - {StartBlockNumber: 2327, EndBlockNumber: 2417}, - {StartBlockNumber: 2418, EndBlockNumber: 2508}, - } - - if len(expectedRanges) != len(ranges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(ranges)) - } - - for i := range ranges { - if *expectedRanges[i] != *ranges[i] { - t.Errorf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *ranges[i]) - } - } -} - -func TestDecodeBatchVersionAndChunkBlockRangesCodecv2(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - require.NoError(t, err) - - service := &RollupSyncService{ - scrollChainABI: scrollChainABI, - } - - data, err := os.ReadFile("./testdata/commitBatch_input_codecv2.json") - require.NoError(t, err, "Failed to read json file") - - type tx struct { - Input string `json:"input"` - } - var commitBatch tx - err = json.Unmarshal(data, &commitBatch) - require.NoError(t, err, "Failed to unmarshal transaction json") - - testTxData, err := hex.DecodeString(commitBatch.Input[2:]) - if err != nil { - t.Fatalf("Failed to decode string: %v", err) - } - - version, ranges, err := service.decodeBatchVersionAndChunkBlockRanges(testTxData) - if err != nil { - t.Fatalf("Failed to decode chunk ranges: %v", err) - } - - assert.Equal(t, encoding.CodecV2, encoding.CodecVersion(version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 200, EndBlockNumber: 290}, - {StartBlockNumber: 291, EndBlockNumber: 381}, - {StartBlockNumber: 382, EndBlockNumber: 472}, - {StartBlockNumber: 473, EndBlockNumber: 563}, - {StartBlockNumber: 564, EndBlockNumber: 654}, - {StartBlockNumber: 655, EndBlockNumber: 745}, - {StartBlockNumber: 746, EndBlockNumber: 836}, - {StartBlockNumber: 837, EndBlockNumber: 927}, - {StartBlockNumber: 928, EndBlockNumber: 1018}, - } - - if len(expectedRanges) != len(ranges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(ranges)) - } - - for i := range ranges { - if *expectedRanges[i] != *ranges[i] { - t.Errorf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *ranges[i]) - } - } -} - -func TestDecodeBatchVersionAndChunkBlockRangesCodecv3(t *testing.T) { - scrollChainABI, err := ScrollChainMetaData.GetAbi() - require.NoError(t, err) - - service := &RollupSyncService{ - scrollChainABI: scrollChainABI, - } - - data, err := os.ReadFile("./testdata/commitBatchWithBlobProof_input_codecv3.json") - require.NoError(t, err, "Failed to read json file") - - type tx struct { - Input string `json:"input"` - } - var commitBatch tx - err = json.Unmarshal(data, &commitBatch) - require.NoError(t, err, "Failed to unmarshal transaction json") - - testTxData, err := hex.DecodeString(commitBatch.Input[2:]) - if err != nil { - t.Fatalf("Failed to decode string: %v", err) - } - - version, ranges, err := service.decodeBatchVersionAndChunkBlockRanges(testTxData) - if err != nil { - t.Fatalf("Failed to decode chunk ranges: %v", err) - } - - assert.Equal(t, encoding.CodecV3, encoding.CodecVersion(version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 1, EndBlockNumber: 9}, - {StartBlockNumber: 10, EndBlockNumber: 20}, - {StartBlockNumber: 21, EndBlockNumber: 21}, - {StartBlockNumber: 22, EndBlockNumber: 22}, - {StartBlockNumber: 23, EndBlockNumber: 23}, - {StartBlockNumber: 24, EndBlockNumber: 24}, - {StartBlockNumber: 25, EndBlockNumber: 25}, - {StartBlockNumber: 26, EndBlockNumber: 26}, - {StartBlockNumber: 27, EndBlockNumber: 27}, - {StartBlockNumber: 28, EndBlockNumber: 28}, - {StartBlockNumber: 29, EndBlockNumber: 29}, - {StartBlockNumber: 30, EndBlockNumber: 30}, - {StartBlockNumber: 31, EndBlockNumber: 31}, - {StartBlockNumber: 32, EndBlockNumber: 32}, - {StartBlockNumber: 33, EndBlockNumber: 33}, - {StartBlockNumber: 34, EndBlockNumber: 34}, - {StartBlockNumber: 35, EndBlockNumber: 35}, - {StartBlockNumber: 36, EndBlockNumber: 36}, - {StartBlockNumber: 37, EndBlockNumber: 37}, - {StartBlockNumber: 38, EndBlockNumber: 38}, - {StartBlockNumber: 39, EndBlockNumber: 39}, - {StartBlockNumber: 40, EndBlockNumber: 40}, - } - - if len(expectedRanges) != len(ranges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(ranges)) - } - - for i := range ranges { - if *expectedRanges[i] != *ranges[i] { - t.Errorf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *ranges[i]) - } - } -} - -func TestGetCommittedBatchMetaCodecv0(t *testing.T) { - genesisConfig := ¶ms.ChainConfig{ - Scroll: params.ScrollConfig{ - L1Config: ¶ms.L1Config{ - L1ChainId: 11155111, - ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), + L1ChainId: 11155111, + ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), + L1MessageQueueAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), }, }, } db := rawdb.NewDatabase(memorydb.New()) - rlpData, err := os.ReadFile("./testdata/commitBatch_codecv0.rlp") - if err != nil { - t.Fatalf("Failed to read RLP data: %v", err) - } - l1Client := &mockEthClient{ - txRLP: rlpData, - } - bc := &core.BlockChain{} stack, err := node.New(&node.DefaultConfig) - if err != nil { - t.Fatalf("Failed to new P2P node: %v", err) - } + require.NoError(t, err, "Failed to create new P2P node") defer stack.Close() - service, err := NewRollupSyncService(context.Background(), genesisConfig, db, l1Client, bc, stack) - if err != nil { - t.Fatalf("Failed to new rollup sync service: %v", err) - } - vLog := &types.Log{ - TxHash: common.HexToHash("0x0"), - } - metadata, err := service.getCommittedBatchMeta(1, vLog) + service, err := NewRollupSyncService(context.Background(), genesisConfig, db, &l1.MockNopClient{}, &core.BlockChain{}, stack, da_syncer.Config{ + BlobScanAPIEndpoint: "http://dummy-endpoint:1234", + }) require.NoError(t, err) - assert.Equal(t, encoding.CodecV0, encoding.CodecVersion(metadata.Version)) - expectedRanges := []*rawdb.ChunkBlockRange{ {StartBlockNumber: 911145, EndBlockNumber: 911151}, {StartBlockNumber: 911152, EndBlockNumber: 911155}, {StartBlockNumber: 911156, EndBlockNumber: 911159}, } - if len(expectedRanges) != len(metadata.ChunkBlockRanges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(metadata.ChunkBlockRanges)) + var chunks []*encoding.DAChunkRawTx + for _, r := range expectedRanges { + var blocks []encoding.DABlock + for i := r.StartBlockNumber; i <= r.EndBlockNumber; i++ { + blocks = append(blocks, &mockDABlock{number: i}) + } + chunks = append(chunks, &encoding.DAChunkRawTx{Blocks: blocks}) } - for i := range metadata.ChunkBlockRanges { - if *expectedRanges[i] != *metadata.ChunkBlockRanges[i] { - t.Fatalf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *metadata.ChunkBlockRanges[i]) - } + committedBatch := mockEntryWithBlocks{ + batchIndex: 1, + version: encoding.CodecV0, + chunks: chunks, } + + metadata, err := service.getCommittedBatchMeta(committedBatch) + require.NoError(t, err) + + require.Equal(t, encoding.CodecV0, encoding.CodecVersion(metadata.Version)) + require.EqualValues(t, expectedRanges, metadata.ChunkBlockRanges) } -func TestGetCommittedBatchMetaCodecv1(t *testing.T) { +func TestGetCommittedBatchMetaCodecV1(t *testing.T) { genesisConfig := ¶ms.ChainConfig{ Scroll: params.ScrollConfig{ L1Config: ¶ms.L1Config{ - L1ChainId: 11155111, - ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), + L1ChainId: 11155111, + ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), + L1MessageQueueAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), }, }, } db := rawdb.NewDatabase(memorydb.New()) - rlpData, err := os.ReadFile("./testdata/commitBatch_codecv1.rlp") - if err != nil { - t.Fatalf("Failed to read RLP data: %v", err) - } - l1Client := &mockEthClient{ - txRLP: rlpData, - } - bc := &core.BlockChain{} stack, err := node.New(&node.DefaultConfig) - if err != nil { - t.Fatalf("Failed to new P2P node: %v", err) - } + require.NoError(t, err, "Failed to create new P2P node") defer stack.Close() - service, err := NewRollupSyncService(context.Background(), genesisConfig, db, l1Client, bc, stack) - if err != nil { - t.Fatalf("Failed to new rollup sync service: %v", err) - } - vLog := &types.Log{ - TxHash: common.HexToHash("0x1"), - } - metadata, err := service.getCommittedBatchMeta(1, vLog) + service, err := NewRollupSyncService(context.Background(), genesisConfig, db, &l1.MockNopClient{}, &core.BlockChain{}, stack, da_syncer.Config{ + BlobScanAPIEndpoint: "http://localhost:8080", + }) require.NoError(t, err) - assert.Equal(t, encoding.CodecV1, encoding.CodecVersion(metadata.Version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 1, EndBlockNumber: 11}, - } - - if len(expectedRanges) != len(metadata.ChunkBlockRanges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(metadata.ChunkBlockRanges)) - } - - for i := range metadata.ChunkBlockRanges { - if *expectedRanges[i] != *metadata.ChunkBlockRanges[i] { - t.Fatalf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *metadata.ChunkBlockRanges[i]) - } - } -} - -func TestGetCommittedBatchMetaCodecv2(t *testing.T) { - genesisConfig := ¶ms.ChainConfig{ - Scroll: params.ScrollConfig{ - L1Config: ¶ms.L1Config{ - L1ChainId: 11155111, - ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), - }, - }, - } - db := rawdb.NewDatabase(memorydb.New()) - - rlpData, err := os.ReadFile("./testdata/commitBatch_codecv2.rlp") - if err != nil { - t.Fatalf("Failed to read RLP data: %v", err) - } - l1Client := &mockEthClient{ - txRLP: rlpData, - } - bc := &core.BlockChain{} - stack, err := node.New(&node.DefaultConfig) - if err != nil { - t.Fatalf("Failed to new P2P node: %v", err) - } - defer stack.Close() - service, err := NewRollupSyncService(context.Background(), genesisConfig, db, l1Client, bc, stack) - if err != nil { - t.Fatalf("Failed to new rollup sync service: %v", err) - } - - vLog := &types.Log{ - TxHash: common.HexToHash("0x2"), - } - metadata, err := service.getCommittedBatchMeta(1, vLog) - require.NoError(t, err) - - assert.Equal(t, encoding.CodecV2, encoding.CodecVersion(metadata.Version)) - expectedRanges := []*rawdb.ChunkBlockRange{ + {StartBlockNumber: 100, EndBlockNumber: 142}, {StartBlockNumber: 143, EndBlockNumber: 143}, {StartBlockNumber: 144, EndBlockNumber: 144}, {StartBlockNumber: 145, EndBlockNumber: 145}, @@ -456,99 +125,115 @@ func TestGetCommittedBatchMetaCodecv2(t *testing.T) { {StartBlockNumber: 174, EndBlockNumber: 174}, } - if len(expectedRanges) != len(metadata.ChunkBlockRanges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(metadata.ChunkBlockRanges)) - } - - for i := range metadata.ChunkBlockRanges { - if *expectedRanges[i] != *metadata.ChunkBlockRanges[i] { - t.Fatalf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *metadata.ChunkBlockRanges[i]) + var chunks []*encoding.DAChunkRawTx + for _, r := range expectedRanges { + var blocks []encoding.DABlock + for i := r.StartBlockNumber; i <= r.EndBlockNumber; i++ { + blocks = append(blocks, &mockDABlock{number: i}) } - } -} - -func TestGetCommittedBatchMetaCodecv3(t *testing.T) { - genesisConfig := ¶ms.ChainConfig{ - Scroll: params.ScrollConfig{ - L1Config: ¶ms.L1Config{ - L1ChainId: 11155111, - ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"), - }, - }, - } - db := rawdb.NewDatabase(memorydb.New()) - - rlpData, err := os.ReadFile("./testdata/commitBatchWithBlobProof_codecv3.rlp") - if err != nil { - t.Fatalf("Failed to read RLP data: %v", err) - } - l1Client := &mockEthClient{ - txRLP: rlpData, - } - bc := &core.BlockChain{} - stack, err := node.New(&node.DefaultConfig) - if err != nil { - t.Fatalf("Failed to new P2P node: %v", err) - } - defer stack.Close() - service, err := NewRollupSyncService(context.Background(), genesisConfig, db, l1Client, bc, stack) - if err != nil { - t.Fatalf("Failed to new rollup sync service: %v", err) + chunks = append(chunks, &encoding.DAChunkRawTx{Blocks: blocks}) } - vLog := &types.Log{ - TxHash: common.HexToHash("0x3"), + expectedVersionedHashes := []common.Hash{ + common.HexToHash("0x1"), + common.HexToHash("0x2"), } - metadata, err := service.getCommittedBatchMeta(1, vLog) + + committedBatch := mockEntryWithBlocks{ + batchIndex: 1, + version: encoding.CodecV1, + chunks: chunks, + versionedHashes: expectedVersionedHashes, + } + + metadata, err := service.getCommittedBatchMeta(committedBatch) require.NoError(t, err) - assert.Equal(t, encoding.CodecV3, encoding.CodecVersion(metadata.Version)) - - expectedRanges := []*rawdb.ChunkBlockRange{ - {StartBlockNumber: 41, EndBlockNumber: 41}, - {StartBlockNumber: 42, EndBlockNumber: 42}, - {StartBlockNumber: 43, EndBlockNumber: 43}, - {StartBlockNumber: 44, EndBlockNumber: 44}, - {StartBlockNumber: 45, EndBlockNumber: 45}, - {StartBlockNumber: 46, EndBlockNumber: 46}, - {StartBlockNumber: 47, EndBlockNumber: 47}, - {StartBlockNumber: 48, EndBlockNumber: 48}, - {StartBlockNumber: 49, EndBlockNumber: 49}, - {StartBlockNumber: 50, EndBlockNumber: 50}, - {StartBlockNumber: 51, EndBlockNumber: 51}, - {StartBlockNumber: 52, EndBlockNumber: 52}, - {StartBlockNumber: 53, EndBlockNumber: 53}, - {StartBlockNumber: 54, EndBlockNumber: 54}, - {StartBlockNumber: 55, EndBlockNumber: 55}, - {StartBlockNumber: 56, EndBlockNumber: 56}, - {StartBlockNumber: 57, EndBlockNumber: 57}, - {StartBlockNumber: 58, EndBlockNumber: 58}, - {StartBlockNumber: 59, EndBlockNumber: 59}, - {StartBlockNumber: 60, EndBlockNumber: 60}, - {StartBlockNumber: 61, EndBlockNumber: 61}, - {StartBlockNumber: 62, EndBlockNumber: 62}, - {StartBlockNumber: 63, EndBlockNumber: 63}, - {StartBlockNumber: 64, EndBlockNumber: 64}, - {StartBlockNumber: 65, EndBlockNumber: 65}, - {StartBlockNumber: 66, EndBlockNumber: 66}, - {StartBlockNumber: 67, EndBlockNumber: 67}, - {StartBlockNumber: 68, EndBlockNumber: 68}, - {StartBlockNumber: 69, EndBlockNumber: 69}, - {StartBlockNumber: 70, EndBlockNumber: 70}, - } - - if len(expectedRanges) != len(metadata.ChunkBlockRanges) { - t.Fatalf("Expected range length %v, got %v", len(expectedRanges), len(metadata.ChunkBlockRanges)) - } - - for i := range metadata.ChunkBlockRanges { - if *expectedRanges[i] != *metadata.ChunkBlockRanges[i] { - t.Fatalf("Mismatch at index %d: expected %v, got %v", i, *expectedRanges[i], *metadata.ChunkBlockRanges[i]) - } - } + require.Equal(t, encoding.CodecV1, encoding.CodecVersion(metadata.Version)) + require.EqualValues(t, expectedRanges, metadata.ChunkBlockRanges) + require.EqualValues(t, expectedVersionedHashes, metadata.BlobVersionedHashes) } -func TestValidateBatchCodecv0(t *testing.T) { +type mockEntryWithBlocks struct { + batchIndex uint64 + version encoding.CodecVersion + chunks []*encoding.DAChunkRawTx + versionedHashes []common.Hash +} + +func (m mockEntryWithBlocks) Type() da.Type { + panic("implement me") +} + +func (m mockEntryWithBlocks) BatchIndex() uint64 { + return m.batchIndex +} + +func (m mockEntryWithBlocks) L1BlockNumber() uint64 { + panic("implement me") +} + +func (m mockEntryWithBlocks) CompareTo(entry da.Entry) int { + panic("implement me") +} + +func (m mockEntryWithBlocks) Event() l1.RollupEvent { + panic("implement me") +} + +func (m mockEntryWithBlocks) Blocks() []*da.PartialBlock { + panic("implement me") +} + +func (m mockEntryWithBlocks) Version() encoding.CodecVersion { + return m.version +} + +func (m mockEntryWithBlocks) Chunks() []*encoding.DAChunkRawTx { + return m.chunks +} + +func (m mockEntryWithBlocks) BlobVersionedHashes() []common.Hash { + return m.versionedHashes +} + +type mockDABlock struct { + number uint64 +} + +func (b *mockDABlock) Encode() []byte { + panic("implement me") +} + +func (b *mockDABlock) Decode(bytes []byte) error { + panic("implement me") +} + +func (b *mockDABlock) NumTransactions() uint16 { + panic("implement me") +} + +func (b *mockDABlock) NumL1Messages() uint16 { + panic("implement me") +} + +func (b *mockDABlock) Timestamp() uint64 { + panic("implement me") +} + +func (b *mockDABlock) BaseFee() *big.Int { + panic("implement me") +} + +func (b *mockDABlock) GasLimit() uint64 { + panic("implement me") +} + +func (b *mockDABlock) Number() uint64 { + return b.number +} + +func TestValidateBatchCodecV0(t *testing.T) { block1 := readBlockFromJSON(t, "./testdata/blockTrace_02.json") chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} @@ -559,18 +244,21 @@ func TestValidateBatchCodecv0(t *testing.T) { chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} parentFinalizedBatchMeta1 := &rawdb.FinalizedBatchMeta{} - event1 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(0), - BatchHash: common.HexToHash("0xfd3ecf106ce993adc6db68e42ce701bfe638434395abdeeb871f7bd395ae2368"), - StateRoot: chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, - WithdrawRoot: chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, - } + event1 := l1.NewFinalizeBatchEvent( + big.NewInt(0), + common.HexToHash("0xfd3ecf106ce993adc6db68e42ce701bfe638434395abdeeb871f7bd395ae2368"), + chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, + chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV0), BlobVersionedHashes: nil, } - endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex.Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) + endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex().Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) assert.NoError(t, err) assert.Equal(t, uint64(13), endBlock1) @@ -578,37 +266,41 @@ func TestValidateBatchCodecv0(t *testing.T) { chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} parentFinalizedBatchMeta2 := &rawdb.FinalizedBatchMeta{ - BatchHash: event1.BatchHash, + BatchHash: event1.BatchHash(), TotalL1MessagePopped: 11, - StateRoot: event1.StateRoot, - WithdrawRoot: event1.WithdrawRoot, + StateRoot: event1.StateRoot(), + WithdrawRoot: event1.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta2, finalizedBatchMeta1) - event2 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(1), - BatchHash: common.HexToHash("0xadb8e526c3fdc2045614158300789cd66e7a945efe5a484db00b5ef9a26016d7"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + + event2 := l1.NewFinalizeBatchEvent( + big.NewInt(1), + common.HexToHash("0xadb8e526c3fdc2045614158300789cd66e7a945efe5a484db00b5ef9a26016d7"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 2, + ) committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV0), BlobVersionedHashes: nil, } - endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex.Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) + endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex().Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) assert.NoError(t, err) assert.Equal(t, uint64(17), endBlock2) parentFinalizedBatchMeta3 := &rawdb.FinalizedBatchMeta{ - BatchHash: event2.BatchHash, + BatchHash: event2.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event2.StateRoot, - WithdrawRoot: event2.WithdrawRoot, + StateRoot: event2.StateRoot(), + WithdrawRoot: event2.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta3, finalizedBatchMeta2) } -func TestValidateBatchCodecv1(t *testing.T) { +func TestValidateBatchCodecV1(t *testing.T) { block1 := readBlockFromJSON(t, "./testdata/blockTrace_02.json") chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} @@ -619,18 +311,21 @@ func TestValidateBatchCodecv1(t *testing.T) { chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} parentFinalizedBatchMeta1 := &rawdb.FinalizedBatchMeta{} - event1 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(0), - BatchHash: common.HexToHash("0x73cb3310646716cb782702a0ec4ad33cf55633c85daf96b641953c5defe58031"), - StateRoot: chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, - WithdrawRoot: chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, - } + event1 := l1.NewFinalizeBatchEvent( + big.NewInt(0), + common.HexToHash("0x73cb3310646716cb782702a0ec4ad33cf55633c85daf96b641953c5defe58031"), + chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, + chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV1), BlobVersionedHashes: []common.Hash{common.HexToHash("0x0129554070e4323800ca0e5ddd17bc447854601b306a70870002a058741214b3")}, } - endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex.Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) + endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex().Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) assert.NoError(t, err) assert.Equal(t, uint64(13), endBlock1) @@ -638,36 +333,39 @@ func TestValidateBatchCodecv1(t *testing.T) { chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} parentFinalizedBatchMeta2 := &rawdb.FinalizedBatchMeta{ - BatchHash: event1.BatchHash, + BatchHash: event1.BatchHash(), TotalL1MessagePopped: 11, - StateRoot: event1.StateRoot, - WithdrawRoot: event1.WithdrawRoot, + StateRoot: event1.StateRoot(), + WithdrawRoot: event1.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta2, finalizedBatchMeta1) - event2 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(1), - BatchHash: common.HexToHash("0x7f230ce84b4bf86f8ee22ffb5c145e3ef3ddf2a76da4936a33f33cebdb63a48a"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + event2 := l1.NewFinalizeBatchEvent( + big.NewInt(1), + common.HexToHash("0x7f230ce84b4bf86f8ee22ffb5c145e3ef3ddf2a76da4936a33f33cebdb63a48a"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV1), BlobVersionedHashes: []common.Hash{common.HexToHash("0x01a327088bb2b13151449d8313c281d0006d12e8453e863637b746898b6ad5a6")}, } - endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex.Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) + endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex().Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) assert.NoError(t, err) assert.Equal(t, uint64(17), endBlock2) parentFinalizedBatchMeta3 := &rawdb.FinalizedBatchMeta{ - BatchHash: event2.BatchHash, + BatchHash: event2.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event2.StateRoot, - WithdrawRoot: event2.WithdrawRoot, + StateRoot: event2.StateRoot(), + WithdrawRoot: event2.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta3, finalizedBatchMeta2) } -func TestValidateBatchCodecv2(t *testing.T) { +func TestValidateBatchCodecV2(t *testing.T) { block1 := readBlockFromJSON(t, "./testdata/blockTrace_02.json") chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} @@ -678,18 +376,21 @@ func TestValidateBatchCodecv2(t *testing.T) { chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} parentFinalizedBatchMeta1 := &rawdb.FinalizedBatchMeta{} - event1 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(0), - BatchHash: common.HexToHash("0xaccf37a0b974f2058692d366b2ea85502c99db4a0bcb9b77903b49bf866a463b"), - StateRoot: chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, - WithdrawRoot: chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, - } + event1 := l1.NewFinalizeBatchEvent( + big.NewInt(0), + common.HexToHash("0xaccf37a0b974f2058692d366b2ea85502c99db4a0bcb9b77903b49bf866a463b"), + chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, + chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV2), BlobVersionedHashes: []common.Hash{common.HexToHash("0x018d99636f4b20ccdc1dd11c289eb2a470e2c4dd631b1a7b48a6978805f49d18")}, } - endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex.Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) + endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex().Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) assert.NoError(t, err) assert.Equal(t, uint64(13), endBlock1) @@ -697,36 +398,39 @@ func TestValidateBatchCodecv2(t *testing.T) { chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} parentFinalizedBatchMeta2 := &rawdb.FinalizedBatchMeta{ - BatchHash: event1.BatchHash, + BatchHash: event1.BatchHash(), TotalL1MessagePopped: 11, - StateRoot: event1.StateRoot, - WithdrawRoot: event1.WithdrawRoot, + StateRoot: event1.StateRoot(), + WithdrawRoot: event1.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta2, finalizedBatchMeta1) - event2 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(1), - BatchHash: common.HexToHash("0x62ec61e1fdb334868ffd471df601f6858e692af01d42b5077c805a9fd4558c91"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + event2 := l1.NewFinalizeBatchEvent( + big.NewInt(1), + common.HexToHash("0x62ec61e1fdb334868ffd471df601f6858e692af01d42b5077c805a9fd4558c91"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV2), BlobVersionedHashes: []common.Hash{common.HexToHash("0x015b4e3d3dcd64cc0eb6a5ad535d7a1844a8c4cdad366ec73557bcc533941370")}, } - endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex.Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) + endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex().Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) assert.NoError(t, err) assert.Equal(t, uint64(17), endBlock2) parentFinalizedBatchMeta3 := &rawdb.FinalizedBatchMeta{ - BatchHash: event2.BatchHash, + BatchHash: event2.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event2.StateRoot, - WithdrawRoot: event2.WithdrawRoot, + StateRoot: event2.StateRoot(), + WithdrawRoot: event2.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta3, finalizedBatchMeta2) } -func TestValidateBatchCodecv3(t *testing.T) { +func TestValidateBatchCodecV3(t *testing.T) { block1 := readBlockFromJSON(t, "./testdata/blockTrace_02.json") chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} @@ -737,19 +441,22 @@ func TestValidateBatchCodecv3(t *testing.T) { chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} parentFinalizedBatchMeta1 := &rawdb.FinalizedBatchMeta{} - event1 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(0), - BatchHash: common.HexToHash("0x015eb56fb95bf9a06157cfb8389ba7c2b6b08373e22581ac2ba387003708265d"), - StateRoot: chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, - WithdrawRoot: chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, - } + event1 := l1.NewFinalizeBatchEvent( + big.NewInt(0), + common.HexToHash("0x015eb56fb95bf9a06157cfb8389ba7c2b6b08373e22581ac2ba387003708265d"), + chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, + chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV3), BlobVersionedHashes: []common.Hash{common.HexToHash("0x018d99636f4b20ccdc1dd11c289eb2a470e2c4dd631b1a7b48a6978805f49d18")}, } - endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex.Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) + endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex().Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1, chunk2, chunk3}, nil) assert.NoError(t, err) assert.Equal(t, uint64(13), endBlock1) @@ -757,31 +464,34 @@ func TestValidateBatchCodecv3(t *testing.T) { chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} parentFinalizedBatchMeta2 := &rawdb.FinalizedBatchMeta{ - BatchHash: event1.BatchHash, + BatchHash: event1.BatchHash(), TotalL1MessagePopped: 11, - StateRoot: event1.StateRoot, - WithdrawRoot: event1.WithdrawRoot, + StateRoot: event1.StateRoot(), + WithdrawRoot: event1.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta2, finalizedBatchMeta1) - event2 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(1), - BatchHash: common.HexToHash("0x382cb0d507e3d7507f556c52e05f76b05e364ad26205e7f62c95967a19c2f35d"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + event2 := l1.NewFinalizeBatchEvent( + big.NewInt(1), + common.HexToHash("0x382cb0d507e3d7507f556c52e05f76b05e364ad26205e7f62c95967a19c2f35d"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV3), BlobVersionedHashes: []common.Hash{common.HexToHash("0x015b4e3d3dcd64cc0eb6a5ad535d7a1844a8c4cdad366ec73557bcc533941370")}, } - endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex.Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) + endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex().Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk4}, nil) assert.NoError(t, err) assert.Equal(t, uint64(17), endBlock2) parentFinalizedBatchMeta3 := &rawdb.FinalizedBatchMeta{ - BatchHash: event2.BatchHash, + BatchHash: event2.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event2.StateRoot, - WithdrawRoot: event2.WithdrawRoot, + StateRoot: event2.StateRoot(), + WithdrawRoot: event2.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta3, finalizedBatchMeta2) } @@ -791,19 +501,22 @@ func TestValidateBatchUpgrades(t *testing.T) { chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} parentFinalizedBatchMeta1 := &rawdb.FinalizedBatchMeta{} - event1 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(0), - BatchHash: common.HexToHash("0x4605465b7470c8565b123330d7186805caf9a7f2656d8e9e744b62e14ca22c3d"), - StateRoot: chunk1.Blocks[len(chunk1.Blocks)-1].Header.Root, - WithdrawRoot: chunk1.Blocks[len(chunk1.Blocks)-1].WithdrawRoot, - } + event1 := l1.NewFinalizeBatchEvent( + big.NewInt(0), + common.HexToHash("0x4605465b7470c8565b123330d7186805caf9a7f2656d8e9e744b62e14ca22c3d"), + chunk1.Blocks[len(chunk1.Blocks)-1].Header.Root, + chunk1.Blocks[len(chunk1.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV0), BlobVersionedHashes: nil, } - endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex.Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1}, nil) + endBlock1, finalizedBatchMeta1, err := validateBatch(event1.BatchIndex().Uint64(), event1, parentFinalizedBatchMeta1, committedBatchMeta1, []*encoding.Chunk{chunk1}, nil) assert.NoError(t, err) assert.Equal(t, uint64(2), endBlock1) @@ -811,23 +524,26 @@ func TestValidateBatchUpgrades(t *testing.T) { chunk2 := &encoding.Chunk{Blocks: []*encoding.Block{block2}} parentFinalizedBatchMeta2 := &rawdb.FinalizedBatchMeta{ - BatchHash: event1.BatchHash, + BatchHash: event1.BatchHash(), TotalL1MessagePopped: 0, - StateRoot: event1.StateRoot, - WithdrawRoot: event1.WithdrawRoot, + StateRoot: event1.StateRoot(), + WithdrawRoot: event1.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta2, finalizedBatchMeta1) - event2 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(1), - BatchHash: common.HexToHash("0xc4af33bce87aa702edc3ad4b7d34730d25719427704e250787f99e0f55049252"), - StateRoot: chunk2.Blocks[len(chunk2.Blocks)-1].Header.Root, - WithdrawRoot: chunk2.Blocks[len(chunk2.Blocks)-1].WithdrawRoot, - } + event2 := l1.NewFinalizeBatchEvent( + big.NewInt(1), + common.HexToHash("0xc4af33bce87aa702edc3ad4b7d34730d25719427704e250787f99e0f55049252"), + chunk2.Blocks[len(chunk2.Blocks)-1].Header.Root, + chunk2.Blocks[len(chunk2.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV1), BlobVersionedHashes: []common.Hash{common.HexToHash("0x01a688c6e137310df38a62f5ad1e5119b8cb0455c386a9a4079b14fe92a239aa")}, } - endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex.Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk2}, nil) + endBlock2, finalizedBatchMeta2, err := validateBatch(event2.BatchIndex().Uint64(), event2, parentFinalizedBatchMeta2, committedBatchMeta2, []*encoding.Chunk{chunk2}, nil) assert.NoError(t, err) assert.Equal(t, uint64(3), endBlock2) @@ -835,23 +551,26 @@ func TestValidateBatchUpgrades(t *testing.T) { chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} parentFinalizedBatchMeta3 := &rawdb.FinalizedBatchMeta{ - BatchHash: event2.BatchHash, + BatchHash: event2.BatchHash(), TotalL1MessagePopped: 0, - StateRoot: event2.StateRoot, - WithdrawRoot: event2.WithdrawRoot, + StateRoot: event2.StateRoot(), + WithdrawRoot: event2.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta3, finalizedBatchMeta2) - event3 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(2), - BatchHash: common.HexToHash("0x9f87f2de2019ed635f867b1e61be6a607c3174ced096f370fd18556c38833c62"), - StateRoot: chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, - WithdrawRoot: chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, - } + event3 := l1.NewFinalizeBatchEvent( + big.NewInt(2), + common.HexToHash("0x9f87f2de2019ed635f867b1e61be6a607c3174ced096f370fd18556c38833c62"), + chunk3.Blocks[len(chunk3.Blocks)-1].Header.Root, + chunk3.Blocks[len(chunk3.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta3 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV1), BlobVersionedHashes: []common.Hash{common.HexToHash("0x01ea66c4de196d36e2c3a5d7c0045100b9e46ef65be8f7a921ef20e6f2e99ebd")}, } - endBlock3, finalizedBatchMeta3, err := validateBatch(event3.BatchIndex.Uint64(), event3, parentFinalizedBatchMeta3, committedBatchMeta3, []*encoding.Chunk{chunk3}, nil) + endBlock3, finalizedBatchMeta3, err := validateBatch(event3.BatchIndex().Uint64(), event3, parentFinalizedBatchMeta3, committedBatchMeta3, []*encoding.Chunk{chunk3}, nil) assert.NoError(t, err) assert.Equal(t, uint64(13), endBlock3) @@ -859,31 +578,34 @@ func TestValidateBatchUpgrades(t *testing.T) { chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} parentFinalizedBatchMeta4 := &rawdb.FinalizedBatchMeta{ - BatchHash: event3.BatchHash, + BatchHash: event3.BatchHash(), TotalL1MessagePopped: 11, - StateRoot: event3.StateRoot, - WithdrawRoot: event3.WithdrawRoot, + StateRoot: event3.StateRoot(), + WithdrawRoot: event3.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta4, finalizedBatchMeta3) - event4 := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(3), - BatchHash: common.HexToHash("0xd33332aef8efbc9a0be4c4694088ac0dd052d2d3ad3ffda5e4c2010825e476bc"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + event4 := l1.NewFinalizeBatchEvent( + big.NewInt(3), + common.HexToHash("0xd33332aef8efbc9a0be4c4694088ac0dd052d2d3ad3ffda5e4c2010825e476bc"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta4 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV3), BlobVersionedHashes: []common.Hash{common.HexToHash("0x015b4e3d3dcd64cc0eb6a5ad535d7a1844a8c4cdad366ec73557bcc533941370")}, } - endBlock4, finalizedBatchMeta4, err := validateBatch(event4.BatchIndex.Uint64(), event4, parentFinalizedBatchMeta4, committedBatchMeta4, []*encoding.Chunk{chunk4}, nil) + endBlock4, finalizedBatchMeta4, err := validateBatch(event4.BatchIndex().Uint64(), event4, parentFinalizedBatchMeta4, committedBatchMeta4, []*encoding.Chunk{chunk4}, nil) assert.NoError(t, err) assert.Equal(t, uint64(17), endBlock4) parentFinalizedBatchMeta5 := &rawdb.FinalizedBatchMeta{ - BatchHash: event4.BatchHash, + BatchHash: event4.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event4.StateRoot, - WithdrawRoot: event4.WithdrawRoot, + StateRoot: event4.StateRoot(), + WithdrawRoot: event4.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta5, finalizedBatchMeta4) } @@ -898,13 +620,15 @@ func TestValidateBatchInFinalizeByBundle(t *testing.T) { chunk2 := &encoding.Chunk{Blocks: []*encoding.Block{block2}} chunk3 := &encoding.Chunk{Blocks: []*encoding.Block{block3}} chunk4 := &encoding.Chunk{Blocks: []*encoding.Block{block4}} - - event := &L1FinalizeBatchEvent{ - BatchIndex: big.NewInt(3), - BatchHash: common.HexToHash("0xaa6dc7cc432c8d46a9373e1e96d829a1e24e52fe0468012ff062793ea8f5b55e"), - StateRoot: chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, - WithdrawRoot: chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, - } + event := l1.NewFinalizeBatchEvent( + big.NewInt(3), + common.HexToHash("0xaa6dc7cc432c8d46a9373e1e96d829a1e24e52fe0468012ff062793ea8f5b55e"), + chunk4.Blocks[len(chunk4.Blocks)-1].Header.Root, + chunk4.Blocks[len(chunk4.Blocks)-1].WithdrawRoot, + common.HexToHash("0x1"), + common.HexToHash("0x1"), + 1, + ) committedBatchMeta1 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV3), @@ -943,10 +667,10 @@ func TestValidateBatchInFinalizeByBundle(t *testing.T) { assert.Equal(t, uint64(17), endBlock4) parentFinalizedBatchMeta5 := &rawdb.FinalizedBatchMeta{ - BatchHash: event.BatchHash, + BatchHash: event.BatchHash(), TotalL1MessagePopped: 42, - StateRoot: event.StateRoot, - WithdrawRoot: event.WithdrawRoot, + StateRoot: event.StateRoot(), + WithdrawRoot: event.WithdrawRoot(), } assert.Equal(t, parentFinalizedBatchMeta5, finalizedBatchMeta4) } diff --git a/rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_codecv3.rlp b/rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_codecv3.rlp deleted file mode 100644 index 4640f84687416f77ecd3e0d0207f1bc46d75edbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4693 zcmcJSe@qi+7{{;eC|00Qx@ct66`003eu!+A7GcM*5(cRWC1q@qbWWKV2aXNm9Dxql z+}OZhT@?_$i38e-#WEHM8|kXf%waN+A<1y*7BgfzwL7}$H4yUX3>c#lgn{@{Jz z=kx7{hd0k%(t*f4*~3U*szjj>~3^ zm%ol^pLlmy&DP?=i+^<4Jl~8sXWwr1Cw+zgz2YX{DH;?Aq! zahVs+AJYhMs{r@Ic@QeVF*ujQF(S9X19C5%2WtelRe<~9d$y(mndKMv{voCr^nZZqk)AOH#Q{ZplOHnnJ zUtIshv;lu3<>Ffd@au6O@E_vyQndj5siglX<>Fge{tUhZw?^MT@w9;c?c4ce zF_ZG`X+JRyz~9W5qG~Mv3se=h(tctZfd2$6&GL(zPof*(KecGTxcfnIFYupUv|n8R z#54f^7ypz0MY4bB_c7M}i+g@ENxy5+{iCoX#pwCjj90+@oTCWe8bJRB(tn=MOVwC@ z;)$ooLMz?>7CZs^|8k)uUT66i^?bo>IKx(#uZZnG`Pt~QlcPQB4Z5i{-E$4ty z4hSn8M9AbX*<+1kJfbr4Gk68Z#Fw`%y};4 zs$#IlUc=t_C1^st@y@-?Im1ZCtK~iOrkWk{n)XYcw;J3tir$}ckC+Vg+io|mNi*in zAe$!C-Y9gs`gl>*quu{Zm)a&({gK1VO3m7z^P8`a+tOOr-Y5!GUmWO@3@*uWVx!Z9@(jGS`omW;ro-|RJIJn&Y{r$doPMsZ# I*)@{*A9z%^Qvd(} diff --git a/rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_input_codecv3.json b/rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_input_codecv3.json deleted file mode 100644 index ca13d9a749..0000000000 --- a/rollup/rollup_sync_service/testdata/commitBatchWithBlobProof_input_codecv3.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "blockHash": "0xb7f00b3235ef6066d61e5e5be5472cdd1edc0a64537b84f110f860e6614a4759", - "blockNumber": "0x187", - "from": "0xf472086186382fca55cd182de196520abd76f69d", - "gas": "0x3e052", - "gasPrice": "0x3b9aca07", - "maxFeePerGas": "0x3b9aca0e", - "maxPriorityFeePerGas": "0x3b9aca00", - "maxFeePerBlobGas": "0x2", - "hash": "0x8de0573f1f72ced727838df60088d9084333d27384a77bf331d520cac0e6a298", - "input": "0x86b053a9000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000108000000000000000000000000000000000000000000000000000000000000010a0000000000000000000000000000000000000000000000000000000000000005900000000000000000000000000000000000000000000000000e5a938b077b60c939e58eeede33d4385228b532cf73e54aca76731a27acc86b7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000008e0000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000a600000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000be00000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000ca00000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000d600000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e200000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000ee0000000000000000000000000000000000000000000000000000000000000021d09000000000000000100000000668ebf810000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000200000000668ec0650000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000300000000668ec0680000000000000000000000000000000000000000000000000000000002e71a60000000000098968000070000000000000000000400000000668ec06b0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000500000000668ec08e0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000600000000668ec0910000000000000000000000000000000000000000000000000000000002e71a60000000000098968000080000000000000000000700000000668ec0940000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000800000000668ec0970000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000900000000668ec0b50000000000000000000000000000000000000000000000000000000002e71a6000000000009896800001000000000000000000000000000000000000000000000000000000000000000000000002950b000000000000000a00000000668ec0b80000000000000000000000000000000000000000000000000000000002e71a60000000000098968000020000000000000000000b00000000668ec0bb0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000c00000000668ec1630000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000d00000000668ec1660000000000000000000000000000000000000000000000000000000002e71a60000000000098968000120000000000000000000e00000000668ec1690000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000000f00000000668ec16e0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000001000000000668ec1710000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000001100000000668ec1740000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000001200000000668ec17c0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000001300000000668ec21b0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000010000000000000000001400000000668ec21e0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001500000000668ec2210000000000000000000000000000000000000000000000000000000002e71a60000000000098968000400000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001600000000668ec2240000000000000000000000000000000000000000000000000000000002e71a600000000000989680003d0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001700000000668ec2270000000000000000000000000000000000000000000000000000000002e71a600000000000989680003f0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001800000000668ec22a0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000400000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001900000000668ec22d0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003d0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001a00000000668ec2300000000000000000000000000000000000000000000000000000000002e71a600000000000989680003e0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001b00000000668ec2330000000000000000000000000000000000000000000000000000000002e71a60000000000098968000400000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001c00000000668ec2360000000000000000000000000000000000000000000000000000000002e71a600000000000989680003f0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001d00000000668ec2390000000000000000000000000000000000000000000000000000000002e71a60000000000098968000410000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001e00000000668ec23c0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003e0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000001f00000000668ec23f0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003f0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002000000000668ec2420000000000000000000000000000000000000000000000000000000002e71a600000000000989680003d0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002100000000668ec2450000000000000000000000000000000000000000000000000000000002e71a600000000000989680003a0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002200000000668ec2480000000000000000000000000000000000000000000000000000000002e71a600000000000989680003d0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002300000000668ec24b0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003e0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002400000000668ec24e0000000000000000000000000000000000000000000000000000000002e71a600000000000989680003c0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002500000000668ec2510000000000000000000000000000000000000000000000000000000002e71a600000000000989680003b0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002600000000668ec2540000000000000000000000000000000000000000000000000000000002e71a600000000000989680003d0000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002700000000668ec2570000000000000000000000000000000000000000000000000000000002e71a60000000000098968000370000000000000000000000000000000000000000000000000000000000000000000000003d01000000000000002800000000668ec25a0000000000000000000000000000000000000000000000000000000002e71a60000000000098968000390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00b3a20d1a749d0917ef837a9973e549be83321ecafc3b8388f25c5c247078e383e4b062167826f53032aa9bb0fc2a8ef610e1c4e5d0f70ee133a76b5020224a8b2428ba7ec725c61950716b2758aa21f1b0cc3f0bf1551474537093924829c170e3d74228be4c07acf24b5c1adb1adac92d25a32f79ba51731343fc0f52cb11e32bbcedb1969853cf854f61862e915744fb5fbc3d4ec8701fc0f626f5c97211b", - "nonce": "0x1", - "to": "0x475652655309fa7cb1397537bee9a7fbafdc11ca", - "transactionIndex": "0x0", - "value": "0x0", - "type": "0x3", - "accessList": [ - { - "address": "0x3d9a28f7692cb94740bb64f08cb5cd19aa5cd3dd", - "storageKeys": [] - } - ], - "chainId": "0x1b207", - "blobVersionedHashes": [ - "0x0132d1238782d359051322a61d997a57f5b2f86d6f36c2aad4eda0118e3a213a" - ], - "v": "0x0", - "r": "0xe8f6ca4ef76a295dc6aa0099e0e9c4a2902cb0ffaeff0c4bae258575d00cae94", - "s": "0x20682493a94948fb97b8cb67e8ae86f444a96e39c5945f10d1008cf1a0508851", - "yParity": "0x0" -} diff --git a/rollup/rollup_sync_service/testdata/commitBatch_codecv0.rlp b/rollup/rollup_sync_service/testdata/commitBatch_codecv0.rlp deleted file mode 100644 index e0b42dc0a5815a67dbf48d3d7d6d6c55b368ba24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88636 zcmd>n2Ut@}({KvCgH&k|dj|w*(gj68Lyd^#EKN}a1-l|CG<#4KyRl)v_Flnq?Y&p* zy;p4fv*#osfsnxUeZTL1&!2lu_Uvq#nVp@Po!ztSq4#lTe5d#6Bv{*d6+gc_Up<09 zxRJn;wWYXy?8LLFi`PBgc*fN3*0^5n_0yh-xO?bvM;bL>FQWa~hpW6gT?IH!q`Uwv zP7KezC|6Lov%}zFE?!k5`Z?13Is48SOT16vrni1kyhF{Ue>Q9K*{1_|MA^Qfe{Wsb z_eiitMZuu~E;?yh+vCPnJ-!go_-vC$o^tKg`M|6bLIDWn@D*I;@Nz;qeEEA#vVi{6 z*7!GB=dVS|D~O?U=WuBf-s?YYBd#l|cU=kD`cK=6>n{HXb=!&Sme#wjy11nN)Ar)J zYwBIM8s`q;y6fv*x0+WS#dSBY0`583Ov&xdNl(6#{j=e#NKu^;_9tStfsM ztp3RvJr7MAz@L}L-L0;+!|}4Sinjff!BnG-+tF- z)Vy|ojaAyddg&9Ta2lPZ94?i76~*cV8)PMBW~8OH&q^Mak(P?;EcZ_V6{`4i{>I|_ zmyh)bod2}{Df*t0^|r(8jyFvlr15x6+3UW?%G8v0tX}$5DV#=UDTn{-`S+*tt%7`& z^sC~ZzoSlO@%?@p-*xgzw8!t%zuIYyCQE+_{nYOdV?G-_JLBBEOMks1+wTQFJrdj7 z@b83&eTjw3I^MtRyFt@!+n4US%WYI)s2ncQW_6WPiNCPN$RSTA_tV?ac!H*0#lFCo zKQym6IPhj&wQh9hTJ!yhk>4-m%H)7x!$t-<5`YrMG30# z7Z~*{7x7-CR=v3IadqXvlT-KQy15En9zMUf@W6v<6E>~fZ#-;^h{>7K-ff&6|CihP z@NBOI>gr+Rw@(^Qf8J!F_3{O;U4c3VyOI&k$n~0kk|r<=@GD;M+VA0>A#sAeOL`pp zWN@&3yCH%4+}-p?5q)m+>SBgCpOuj4=ZYaeiDKQ~A zrQC0YkH__wMu*NN`|mYMe_lTSU98rm%qLw=#ZHbJwBl0B=)mX8{qo<&kIcOLX72ob zm*ZCG>W!=lX`N0Nd@jlDqoRMCc8h-Z(OeY2F!_1Xms9R-XB@ZMG`DipndH;W*^7e> z+iCl7Kf9m!J=E&Zmv6(~{J5I5`SA45w7!4m{g_s=qN~P;#j4t`T65-dsKt&CP5P&d z_6cX+h-92&M18hOMU4~Nuk`*wJ)o4dm5CM?XAkl)>(OZ5{B?~zv)P4HEek9c3vb%?CMK4aG&vq3@>{rm zbitg;mOb}5ocHZnR1h~OZGF_G=)kNmi+jCa#z>*j#$2f!b5Vyh;}Q0WT<-2M6A_&t z{32brW(hQ!35TF@f5o#2zX&ejM~8eaq0NgVSoEC0jP$JJ^sKR2a(Gog@FLkzrMeoO zs-}uCs3u#h{+-FzVv13WimJv(K??ff5lkTu2>n$gOrV*@#Kr^CYB+~(D9vHB;n5lX zwMd{b!U>i&!Lo^shjff2P0$E!!W7YII=pBeQN^$_7YfYG7#6}9Ty+qkBS@ezp_+99 z&C=6CSR^D{Ky(%lP{2i|5W0NDMf}JjJZ=Qkp|gkz@{uckgdWM~U_vBm=_E8WMv51g z%y!JGQt&Y+4)GKNFjDXV3cGcJFI#BxinK``$Z3mtV}2T#0IoMYd*NpbTg(*ZT}jCV zn(cwuc!JhR$QI&XU=d9iEyP?BswwVR2;%{z#Eeu}hUBe6h&jpcx=2!7C1*;7=E5lR zIMYU5&)A<|@gdhLB&Wn-`OS?Fw^?QkN#az1RAIp?VhWeD*(4P#L4*-Z4W>T_zC7^2 zR!S%q0?U}0vGI5)djmu*1t2iw6D`Q`CTMglSkYpuKNTr}m0mewZ1BsE9 z8HEvKmn6_M7D%`|UV#itv&^CNQ7C;-QW{gBuFb&&XdNCaQZW0aSz-dzX@F>Bx(xo% zLMy@uT0KD`)_8~o464R`Nf&sIV6qg87GH5T6B-pY2(1!~R+cxa@1r?sF7#YR6r1BJ!-vo_CHcJ<`RmbeAiw8M(K16P? zesFw2YWb!?Ki#ISo!xfT`@|zl&0Br$6aKmB*0%x}A7JTt66Nz!8ka*}iI~Jr5)vt9 zE0qF?q%)$$DIPFMNw;tf^N(&~ur-1g7zgv45Zy#la9C;8?PRW69Kljl1Pm?*XbN^v9YfzlF`T}Ys5|BQ{#uO3gZTiP)2T0aya z4|(q>BwtZT%5W9xx#riRLOBY+X(5-O^P=foehe2>AVC*0g#-;eDVSnds*3P`n6dN- z%sHtMBs(UFq);G@9Kn(l`;YR7&5mcYXR&9I)(9vBrK%e&8CQ7jAizpT7i;7MnzgUQ zaDWm7U?>UHE2e;7Wq?2wP|Bl7t)7EWCp$2KruT@fVdFs65KmmC?BtsBv3l2e)(}0a za}U&_b)rwc1u?aN&E)xC$!n*Ss)U*(qwiznZ zH0HDJR+7*9Q54rkQ(UV{0SgpscqXvj5T>JUC750P-VzicR~w%9$~@12?i+02^oBs& zdNCj?a&0HqXnQ)QM{h#SV$P%A*eb-Je+=?|4i1uhD@8tC|S} zq}OAZN&4-!Eu~_#3>0u(#Ma?Xf(0`gOc85jjET)xOqSx@n#5K+y0wU{gW2^Z)@Xnt zVkNQ&?pPh|*9UL#Dj+ZR6fXr$Z@gl_B%IVpC{BY?SNI?R7 zC;o(5B+-nZ{fe(waR+H}Snf0UYT#be`Q|VNCjB@>4&S^8AYOrM>ny$j_2{P z&^FouOl!0j94h!34T#ctbSxE(ig7u3wkI7UQ*~VI9JFRKmTc`Nv}FS3mPaR8d1ON` zIDjT}WXgb&l`z{eSU|_^&cba;q?pgQspd5lZ$r?matSS{WvUY!Z^aeBm`cY2GJ~*( zXlfA~U&Q7U3z8F97@o%w2q5*NU#~fI80LXk7hypx+ z7ONQYnPRKC0f%5<{+e4shvvP2VZa=59il21qh;O!gW5v_7#9J-&;eUW3Z8j)4Cy9@ zh-(2;1xAS8Nnm77^vpFPbaRc&EpRiFIDEd2kS!nyG#B8i7)#tJBmqbGo|~JSOVA;A z431}jcF3jpVZH?y-z+Z9!}P~XXD?C3H<$7hirs)_@pLW17(D?2H9v@3gOH_}O0|Kf zq1h#Q1#Zzsra=ogJjtQ;nQT6v0c~~z)>6Ik`D(eQ0y7K9M1ZaZAx2j^VX9SyMK;v3 z7z8RI5{nU}r;D4RGU{*(eY0hj?@UiZJ@DnI*+`dFN z#|y}rW;qbV6qdEQbbwRy%d!WqkPA7~v;oZQt+1At9XHD&pa&}q=yMSd`nT*H8;?T~ zmK%9_1S_8oDNvB`y$8s&gb-OSmQ_jb6fQg-6O$SV1@7|zY0F&Mv6NavQsq-M#B&jX zmL_}%PT@hpEB5N_R)Ch}b27|p<$|01gqwu&{(y#+8kPiX7HU=du(_`a7&|~AK&?8_~caK>9 z8;41IBxvxs3~EXya^sCM^A6hKk{YP-N#J#h_p&{<@0@V$MoSVl5d#CFIwT}FJFVAx12 zXgdkS*B10LX$8d+S|3NpfVZ%)v z&bBNyMszO@H*v0Yv-8G?whNM1)YqSb4q^SAWe1jzzOuHckkgXB&~u2EmO7m`mQdHBpq z7Sy$n)Sb5SR6L+i1t6R>Me2tkIX!CeR7l>3WYL`spP;TWBxhe=;19`;kSsAC-UpK5 zl4dY{P{9{KV^0qhLNWxB6VD!?K{6SVxv{6>AUPW+H(8}aa*3pFz?`%}z?%~Q>RZxh zFC?dllcq=w;bhhGyO6|OK+ZAiAc?t zhGfqNXC^}OF(i9WxN8S>%^=y=YE55A21)96?)uow7%_|hgp*J~6Otb6SrL$I4oT18 z&8s0914-Y|)t->dgQQ=Xp%o-&NSe|3b(d+17~ufY?!Rg)B!@uK(p>)zBo{!k>8?zi z+zm;mE{_L7@;L`AKL`(E#2`&7aEcfSMMC7s!xNC)YyAk&JVK4znF;O!VsHWK=_SmUkDIpMZ1ZXkku6 z+Taw?;t_LAb}WOY5Wu=D0W5Tw6zm+*fkh0aj(Hnk@| zRbEXG=g8rs)=^qstqtx8&IKS{SoPxe)w)2Aw-)(;H-%wj)dp0T?;|Zgk;F_b73t0t z*Bpg=k{e^SdDS(Gal3#A(p@7i50R0YDqo89`@wY}BtqTk>P0{-e6@FSu_kHAb+a z#N{=#YW1yQg^bRNp)COF*Fv^04L4*23u&@$SAfdPnMMpUhSdjfLu0tKSB*4Dug0Wj z0=-VG-Rsio@(Xc!EOEv=#6A60yZnXf@~1HrbKB&Axcpt5BPVB?u=gm|JsLkH<1;6M!{q}7`P zd%9dfv>HPYSp?;Z00avudXY48DU64}CY)ZO(}e)%3XG9f3WqOb687d$jEb{}_XxR2 zYc8?@#Ux}St(CmIJY=&0Xp>t5N>2hn;iJ;R7xVae$ZiyHL+ef+)U#)Z>q)&ef;ub% zK9bf?)C7VL(mu8IB+Y2I$9-zs;1B#O{v!l+Z>$we%g&X_T3VjKGd7iY0{+p$&+E23#ela zM1?i5UyY9RG9`8^2wVaP{evV3n6N2O(C0mD4kEoGjyL#^KJW$SQsDyyyvg7ZpAH%V zFHzXoY1G>elQHOREcp6*5a+D7DC)fuqF{)H<9^9QY`{ysD%1kh6cDd(QRtXE3F(_i zW;#|Pu>LgVBK=6bX<3J4Db9v-F-U(n2kEC33JQ__KuM!`ULpMg{58I?fShJXKfjQl zODZbRa9H|g7h~%2^rO{rzO$|^L!^HZR&2;X19xzfbemO1sE~m=U>E!s#Ac--8)VQ2 z>!3QeK^2oMKgaN%7Ha$ovsN5<`p@hHB7@D8?lRa5gtjOo^_jsrJ~B8#&L07#{0(kk z&4!g2$wp+@g3$XTdXPhh+2Fq|Oq1(VZ!H&?OH9?czb3uleImqx53VwjM62N$K`Eab8g-to1RJmrzh{1vL zI$+}Y3~pzE8yD}8fO?H*=NKwQm`06A+COTRpM3A){M?$i@K=8t#-J)WC5udRu=d$Jkoxj#^41}7D_s) z{(PFk-W!c)icDs6{&3F2b{iA#T_ck|9ApxQx`C~c*|^4M-|BmM5}C}!k`k}|d!VP$FVyIB&Lcr<_3^)$SNex+@7Ab; z$GKk%Em<4JFA9s&Of_wOq0hZZlO_SqFt<^oo8-xH)ZK?{N5R2y)Czom>>szfKw3Xj zEgow16Lv;Tk!eT+ur>7qY|qJHi%g?sWHeye_rK^ZjC+_bOv>$tTAU~>EJUVfr;=1i_+a`-;&FsvC5@S* z;0Qf1*h~{9A#jA09y1GmK|z6-Gn48tc8ar2ky$|l=ro%sL_Mk_C=fX_n>l&%WKz&5 z?y2wt>Nr|situJ{y&WTSbwC4L;%bREw-g9a>pqwvlpKS4hl8ts;156;T`|iP=gK=1 z;7f?tGUgkR$5z1f&$J$&vb&_Nve4il3-wyMO1erBf`baSv-SmF)_^9hJ3y1A3YtXLJ>Up4vVky5 zT~o0R^~Xfk#T;Z^z(?)ggV0;gK<(jt6tezMcT07OpY>g6=>iB4+0qShOXR~-N$xwU zBk|d5WWylwo&a6i*diN)Tr#|mY%HjR0JSHhL*zV{JywluG8(`)n-n2pyDQ)uInBg# z4WzDmcp)i68~LLpi8v<9s-l&y=EDU$-7Lg=nD4sKqSMeG>n@X$~1aP&~${ z=^}DRS1}Y&{n!AN(DVh&z!!j?WCp^RHvNU1tTD#;%_i|HneulWHp?7H&V+MZG4FHL^&Xj9f&)Xblx0E>IGp1@I=UFW6HMY9C(=Pe;ji=`bk z8wO8KNfJj8!dsM($dkkM?j5Jktzkb#<&BPVd9@oq?SwT86x16nGVX$fSvxlLXk z$R~1|B@>*5AQI%^fHsebntOosac8o@9Y@V!r2}sj;}=NCrDXF64r

xs;413xfpD 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 R;*Oy7tJ$9{uK4#`{{>#NYmEQ^ diff --git a/rollup/rollup_sync_service/testdata/commitBatch_codecv1.rlp b/rollup/rollup_sync_service/testdata/commitBatch_codecv1.rlp deleted file mode 100644 index b0feda0443cd8f807869d3adfd8e1deb3bb0e68f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1237 zcmdnVa*6pT%eiL8P3(*<*0WAAfC#>3#)_UP?qNZx!JNNpHd>aN?|ZrY_xd}6ry6#$ zs0gdBS-^lFFyfbHXuvN|NIMID?F5WS}9__V;~ zKzc>sJa(wwLbwP%21qX=iaDTqOYmud%c1H8hj{8A({i{xJ_f2@E|5~7-Zp$%;Bu&X z!RZUA_W@iU9|Kh{K9w-J>({2(&eHgvsk77FVRy=ho~>skSH)budw}WT0!H4tfOF{) zR#GPOV`R>|cl1qMdiUnPPUl%i&EDVl{S)1=fTLz|hmw8igSGD+7f)q+9lG>kb~#7g y!I@u}bPwK`a!h4G`ok5IPIoNW-+qAQMb86^@G_?B|L@d0ygYaJo=p6u*$Cds7 diff --git a/rollup/rollup_sync_service/testdata/commitBatch_codecv2.rlp b/rollup/rollup_sync_service/testdata/commitBatch_codecv2.rlp deleted file mode 100644 index cbb35f1579f1292bb5a08c4498fe90286c0f2a78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4437 zcmbuCc}Nsd9LINcZC7oVBnm8og$}xeG$FONW}_6NgVwa5$c;Qmlp-lAgr<|IXe;f~ zvdIq9vaHAuOIH6dDk~z$KkV2nh%PYbFzuRI-h0jOH*Y70_7C6tety6A+2@-XcWsRE zgny5jzs-Er8nJz9`CXIp!?7)_v#KmD%afZC*;{fYdEJ!ex7B@@pT^u38wZ^kJo=(! z(tm~NmrbI6-R#t_4-)n3rj2w17TlPb>V3X@VQE44$NHw5tKUqUY)K4H{Bmu}hn|eJ zzKpE*=Bl{U$8PrdMhf;(#n-*=-?gT!foU6jt*L2WlGg25kN7*i2#ZbA_6Ez z1Q0-RfPess10)1cw4#io)r$$tRzJ)**_@cbY!hL|$riu_=12i%oFXMmV2*-OlcKyD z1VEG@L`{fxY7hX?B8Zw09nc^E>>z1_-K%A4V)v_)cJ6R$nVL96b<)lq0WAZ>WhT+5 zjCt-Ksi2I4t^1J}L`M&crWvTnjm*DaDa-zJZX1EXFX(mB8uK4ei0q$BE4&f-jqU#+ z@h{+cDFgl#O7%vz|5Bw(-oHn$8~9y%owUaMWeSn~nY6+if#2Bq93uWDJTGOyKa*0u zk?sGm(k1VIsa`kmPuJ_DHReB}5ZS++R(K=u3%m}@nE$9!k^L)hhdTd<{}}P-sC{@2 z{Kl?t@cV$g|9tFJXW$pql~8kbJ{3xb>@Nt_sc_&oc72a4>t%nDGKP8qzYwZR&Ds7> zC{*^ZQCIOC_>DckmBe3+o$3ty#;)&4;$N@!;W_ZTag~?Z`AAAtKA(-eF3N!4*!4X{ z{3X=I8~?*!Mf{t2Udn*q*!iC({;kx-8-d^0{d0!+cksNF0e`>t|H(quo+bWW)WsWt zKc01*BWw3C!kK`zf4_HClePOeOBe!cZ493$YlGh%7)2AXb|@zCiy^Lv5#b^k8}`xip(Le1IvTqOSB-%)jkdJg>kx<8q}j`$_D z56^+$*z9gPnsU1LUcVn(yX^5Zf7naOT$p5U zvAgT)UA@uuxgSz`-q!3CI@{XY3u2>Y=U46$rMT*jo?-VUtXTB9=vPHyYu5O_Ih!0e xEC~pVYS`