diff --git a/.dockerignore b/.dockerignore index 0c013d18b1..f09367249f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,5 @@ build/_workspace build/_bin tests/testdata + +tmp/ \ No newline at end of file diff --git a/Dockerfile.mockccc b/Dockerfile.mockccc index 2586dbf0cb..cb67c9b4b8 100644 --- a/Dockerfile.mockccc +++ b/Dockerfile.mockccc @@ -7,14 +7,18 @@ ARG SCROLL_LIB_PATH=/scroll/lib # Build Geth in a stock Go builder container FROM scrolltech/go-rust-builder:go-1.21-rust-nightly-2023-12-03 as builder -ADD . /go-ethereum -RUN cd /go-ethereum && env GO111MODULE=on go run build/ci.go install ./cmd/geth +WORKDIR /go-ethereum +COPY go.mod go.sum ./ +RUN go mod download -x + +ADD . ./ +RUN env GO111MODULE=on go run build/ci.go install ./cmd/geth # Pull Geth into a second stage deploy alpine container FROM ubuntu:20.04 -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates +#RUN apt-get -qq update \ +# && apt-get -qq install -y --no-install-recommends ca-certificates ENV CGO_LDFLAGS="-ldl" diff --git a/core/rawdb/accessors_da_syncer.go b/core/rawdb/accessors_da_syncer.go index 96f8166856..ed8cb1a7d0 100644 --- a/core/rawdb/accessors_da_syncer.go +++ b/core/rawdb/accessors_da_syncer.go @@ -1,23 +1,33 @@ package rawdb import ( + "bytes" "math/big" "github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rlp" ) -// WriteDASyncedL1BlockNumber writes the highest synced L1 block number to the database. -func WriteDASyncedL1BlockNumber(db ethdb.KeyValueWriter, L1BlockNumber uint64) { - value := big.NewInt(0).SetUint64(L1BlockNumber).Bytes() +type DAProcessedBatchMeta struct { + BatchIndex uint64 + L1BlockNumber uint64 + TotalL1MessagesPopped uint64 +} +// WriteDAProcessedBatchMeta writes the batch metadata of the latest processed DA batch. +func WriteDAProcessedBatchMeta(db ethdb.KeyValueWriter, daProcessedBatchMeta *DAProcessedBatchMeta) { + value, err := rlp.EncodeToBytes(daProcessedBatchMeta) + if err != nil { + log.Crit("failed to RLP encode committed batch metadata", "batch index", daProcessedBatchMeta.BatchIndex, "committed batch meta", daProcessedBatchMeta, "err", err) + } if err := db.Put(daSyncedL1BlockNumberKey, value); err != nil { - log.Crit("Failed to update DA synced L1 block number", "err", err) + log.Crit("Failed to update DAProcessedBatchMeta", "err", err) } } -// ReadDASyncedL1BlockNumber retrieves the highest synced L1 block number. -func ReadDASyncedL1BlockNumber(db ethdb.Reader) *uint64 { +// ReadDAProcessedBatchMeta retrieves the batch metadata of the latest processed DA batch. +func ReadDAProcessedBatchMeta(db ethdb.Reader) *DAProcessedBatchMeta { data, err := db.Get(daSyncedL1BlockNumberKey) if err != nil && isNotFoundErr(err) { return nil @@ -29,11 +39,25 @@ func ReadDASyncedL1BlockNumber(db ethdb.Reader) *uint64 { return nil } - number := new(big.Int).SetBytes(data) - if !number.IsUint64() { - log.Crit("Unexpected DA synced L1 block number in database", "number", number) + // Try decoding from the newest format for future proofness, then the older one for old data. + daProcessedBatchMeta := new(DAProcessedBatchMeta) + if err = rlp.Decode(bytes.NewReader(data), daProcessedBatchMeta); err == nil { + return daProcessedBatchMeta } - value := number.Uint64() - return &value + // Before storing DAProcessedBatchMeta we used to store a single uint64 value for the L1 block number. + l1BlockNumber := new(big.Int).SetBytes(data) + if !l1BlockNumber.IsUint64() { + log.Crit("Unexpected DA synced L1 block number in database", "number", l1BlockNumber) + } + + // We can simply set only the L1BlockNumber because carrying forward the totalL1MessagesPopped is not required before EuclidV2 (CodecV7) + // (the parentTotalL1MessagePopped is given via the parentBatchHeader). + // Nodes need to update to the new version to be able to continue syncing after EuclidV2 (CodecV7). Therefore, + // the only nodes that might read a uint64 value are nodes that were running L1 follower before the EuclidV2. + return &DAProcessedBatchMeta{ + BatchIndex: 0, + L1BlockNumber: l1BlockNumber.Uint64(), + TotalL1MessagesPopped: 0, + } } diff --git a/core/rawdb/accessors_l1_message.go b/core/rawdb/accessors_l1_message.go index a447160f12..535b6c600f 100644 --- a/core/rawdb/accessors_l1_message.go +++ b/core/rawdb/accessors_l1_message.go @@ -383,9 +383,13 @@ func ReadFirstQueueIndexNotInL2Block(db ethdb.Reader, l2BlockHash common.Hash) * // WriteL1MessageV2StartIndex writes the start index of L1 messages that are from L1MessageQueueV2. func WriteL1MessageV2StartIndex(db ethdb.KeyValueWriter, queueIndex uint64) { - value := big.NewInt(0).SetUint64(queueIndex).Bytes() + // Write with binary.BigEndian.PutUint64 to ensure that 0 values are written as 8 bytes. + // big.NewInt(0).SetUint64(l1BlockNumber).Bytes() would write 0 as empty slice which leads to problems when reading + // the value as non-existent and 0 are not distinguishable. + var buf [8]byte + binary.BigEndian.PutUint64(buf[:], queueIndex) - if err := db.Put(l1MessageV2StartIndexKey, value); err != nil { + if err := db.Put(l1MessageV2StartIndexKey, buf[:]); err != nil { log.Crit("Failed to update L1MessageV2 start index", "err", err) } } @@ -402,13 +406,11 @@ func ReadL1MessageV2StartIndex(db ethdb.Reader) *uint64 { if len(data) == 0 { return nil } - - number := new(big.Int).SetBytes(data) - if !number.IsUint64() { - log.Crit("Unexpected number for L1MessageV2 start index", "number", number) + if len(data) != 8 { + return nil } + res := binary.BigEndian.Uint64(data) - res := number.Uint64() return &res } diff --git a/core/rawdb/accessors_rollup_event.go b/core/rawdb/accessors_rollup_event.go index c48c9c0278..7545664a7b 100644 --- a/core/rawdb/accessors_rollup_event.go +++ b/core/rawdb/accessors_rollup_event.go @@ -25,7 +25,7 @@ type CommittedBatchMeta struct { ChunkBlockRanges []*ChunkBlockRange // introduced with CodecV7 - LastL1MessageQueueHash common.Hash + PostL1MessageQueueHash common.Hash } type committedBatchMetaV0 struct { @@ -170,7 +170,7 @@ func WriteCommittedBatchMeta(db ethdb.KeyValueWriter, batchIndex uint64, committ committedBatchMetaToStore = &committedBatchMetaV7{ Version: committedBatchMeta.Version, ChunkBlockRanges: committedBatchMeta.ChunkBlockRanges, - LastL1MessageQueueHash: committedBatchMeta.LastL1MessageQueueHash, + LastL1MessageQueueHash: committedBatchMeta.PostL1MessageQueueHash, } } @@ -202,7 +202,7 @@ func ReadCommittedBatchMeta(db ethdb.Reader, batchIndex uint64) (*CommittedBatch return &CommittedBatchMeta{ Version: cbm7.Version, ChunkBlockRanges: cbm7.ChunkBlockRanges, - LastL1MessageQueueHash: cbm7.LastL1MessageQueueHash, + PostL1MessageQueueHash: cbm7.LastL1MessageQueueHash, }, nil } @@ -214,7 +214,7 @@ func ReadCommittedBatchMeta(db ethdb.Reader, batchIndex uint64) (*CommittedBatch return &CommittedBatchMeta{ Version: cbm0.Version, ChunkBlockRanges: cbm0.ChunkBlockRanges, - LastL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, }, nil } diff --git a/core/rawdb/accessors_rollup_event_test.go b/core/rawdb/accessors_rollup_event_test.go index 554b07d0b8..442a075866 100644 --- a/core/rawdb/accessors_rollup_event_test.go +++ b/core/rawdb/accessors_rollup_event_test.go @@ -182,7 +182,7 @@ func TestWriteReadDeleteCommittedBatchMeta(t *testing.T) { meta: &CommittedBatchMeta{ Version: 7, ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 10}}, - LastL1MessageQueueHash: common.Hash{1, 2, 3, 4, 5, 6, 7}, + PostL1MessageQueueHash: common.Hash{1, 2, 3, 4, 5, 6, 7}, }, }, { @@ -190,7 +190,7 @@ func TestWriteReadDeleteCommittedBatchMeta(t *testing.T) { meta: &CommittedBatchMeta{ Version: 255, ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 10}, {StartBlockNumber: 11, EndBlockNumber: 20}}, - LastL1MessageQueueHash: common.Hash{255}, + PostL1MessageQueueHash: common.Hash{255}, }, }, } @@ -237,7 +237,7 @@ func TestOverwriteCommittedBatchMeta(t *testing.T) { newMeta := &CommittedBatchMeta{ Version: 255, ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 20}, {StartBlockNumber: 21, EndBlockNumber: 30}}, - LastL1MessageQueueHash: common.Hash{255}, + PostL1MessageQueueHash: common.Hash{255}, } // write initial meta @@ -282,5 +282,5 @@ func compareCommittedBatchMeta(a, b *CommittedBatchMeta) bool { } } - return a.LastL1MessageQueueHash == b.LastL1MessageQueueHash + return a.PostL1MessageQueueHash == b.PostL1MessageQueueHash } diff --git a/go.mod b/go.mod index e36cb8b5f4..ea4204d370 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/scroll-tech/da-codec v0.1.3-0.20250210041951-d028c537b995 + github.com/scroll-tech/da-codec v0.1.3-0.20250226072559-f8a8d3898f54 github.com/scroll-tech/zktrie v0.8.4 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sourcegraph/conc v0.3.0 diff --git a/go.sum b/go.sum index 6e8606ed54..6e028c2e64 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scroll-tech/da-codec v0.1.3-0.20250210041951-d028c537b995 h1:Zo1p42CUS9pADSKoDD0ZoDxf4dQ3gttqWZlV+RSeImk= -github.com/scroll-tech/da-codec v0.1.3-0.20250210041951-d028c537b995/go.mod h1:UZhhjzqYsyEhcvY0Y+SP+oMdeOUqFn/UXpbAYuPGzg0= +github.com/scroll-tech/da-codec v0.1.3-0.20250226072559-f8a8d3898f54 h1:qVpsVu1J91opTn6HYeuzWcBRVhQmPR8g05i+PlOjlI4= +github.com/scroll-tech/da-codec v0.1.3-0.20250226072559-f8a8d3898f54/go.mod h1:xECEHZLVzbdUn+tNbRJhRIjLGTOTmnFQuTgUTeVLX58= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= diff --git a/params/version.go b/params/version.go index 650b578480..6ca0a4f92b 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 = 17 // Patch version component of the current release + VersionPatch = 18 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/da_syncer/batch_queue.go b/rollup/da_syncer/batch_queue.go index 5c1b75c0da..1850f53c19 100644 --- a/rollup/da_syncer/batch_queue.go +++ b/rollup/da_syncer/batch_queue.go @@ -7,7 +7,9 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/ethdb" + "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" + "github.com/scroll-tech/go-ethereum/rollup/l1" ) // BatchQueue is a pipeline stage that reads all batch events from DAQueue and provides only finalized batches to the next stage. @@ -17,21 +19,24 @@ type BatchQueue struct { lastFinalizedBatchIndex uint64 batches *common.Heap[da.Entry] batchesMap *common.ShrinkingMap[uint64, *common.HeapElement[da.Entry]] + + previousBatch *rawdb.DAProcessedBatchMeta } -func NewBatchQueue(DAQueue *DAQueue, db ethdb.Database) *BatchQueue { +func NewBatchQueue(DAQueue *DAQueue, db ethdb.Database, lastProcessedBatch *rawdb.DAProcessedBatchMeta) *BatchQueue { return &BatchQueue{ DAQueue: DAQueue, db: db, - lastFinalizedBatchIndex: 0, + lastFinalizedBatchIndex: lastProcessedBatch.BatchIndex, batches: common.NewHeap[da.Entry](), batchesMap: common.NewShrinkingMap[uint64, *common.HeapElement[da.Entry]](1000), + previousBatch: lastProcessedBatch, } } // NextBatch finds next finalized batch and returns data, that was committed in that batch -func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) { - if batch := bq.getFinalizedBatch(); batch != nil { +func (bq *BatchQueue) NextBatch(ctx context.Context) (da.EntryWithBlocks, error) { + if batch := bq.nextFinalizedBatch(); batch != nil { return batch, nil } @@ -50,13 +55,15 @@ func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) { case da.CommitBatchV0Type, da.CommitBatchWithBlobType: bq.addBatch(daEntry) case da.RevertBatchType: - bq.deleteBatch(daEntry) + if err = bq.handleRevertEvent(daEntry.Event()); err != nil { + return nil, fmt.Errorf("failed to handle revert event: %w", err) + } case da.FinalizeBatchType: if daEntry.BatchIndex() > bq.lastFinalizedBatchIndex { bq.lastFinalizedBatchIndex = daEntry.BatchIndex() } - if batch := bq.getFinalizedBatch(); batch != nil { + if batch := bq.nextFinalizedBatch(); batch != nil { return batch, nil } default: @@ -65,16 +72,17 @@ func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) { } } -// getFinalizedBatch returns next finalized batch if there is available -func (bq *BatchQueue) getFinalizedBatch() da.Entry { +// nextFinalizedBatch returns next finalized batch if there is available +func (bq *BatchQueue) nextFinalizedBatch() da.EntryWithBlocks { if bq.batches.Len() == 0 { return nil } batch := bq.batches.Peek().Value() + // we process all batches smaller or equal to the last finalized batch index -> this reflects bundles of multiple batches + // where we only receive the finalize event for the last batch of the bundle. if batch.BatchIndex() <= bq.lastFinalizedBatchIndex { - bq.deleteBatch(batch) - return batch + return bq.processAndDeleteBatch(batch) } else { return nil } @@ -85,25 +93,87 @@ func (bq *BatchQueue) addBatch(batch da.Entry) { bq.batchesMap.Set(batch.BatchIndex(), heapElement) } -// deleteBatch deletes data committed in the batch from map, because this batch is reverted or finalized -// updates DASyncedL1BlockNumber -func (bq *BatchQueue) deleteBatch(batch da.Entry) { - batchHeapElement, exists := bq.batchesMap.Get(batch.BatchIndex()) - if !exists { - return +func (bq *BatchQueue) handleRevertEvent(event l1.RollupEvent) error { + switch event.Type() { + case l1.RevertEventV0Type: + revertBatch, ok := event.(*l1.RevertBatchEventV0) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV0Type", event) + } + + log.Info("reverting batch due to RevertEventV0Type", "batchIndex", revertBatch.BatchIndex()) + + bq.deleteBatch(revertBatch.BatchIndex().Uint64()) + case l1.RevertEventV7Type: + revertBatch, ok := event.(*l1.RevertBatchEventV7) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV7Type", event) + } + + // delete all batches from revertBatch.StartBatchIndex (inclusive) to revertBatch.FinishBatchIndex (inclusive) + for i := revertBatch.StartBatchIndex().Uint64(); i <= revertBatch.FinishBatchIndex().Uint64(); i++ { + log.Info("reverting batch due to RevertEventV7Type", "batchIndex", i) + bq.deleteBatch(i) + } + default: + return fmt.Errorf("unexpected type of revert event: %T", event) } - bq.batchesMap.Delete(batch.BatchIndex()) + return nil +} + +func (bq *BatchQueue) deleteBatch(batchIndex uint64) (deleted bool) { + batchHeapElement, exists := bq.batchesMap.Get(batchIndex) + if !exists { + return false + } + + bq.batchesMap.Delete(batchIndex) bq.batches.Remove(batchHeapElement) - // we store here min height of currently loaded batches to be able to start syncing from the same place in case of restart - // TODO: we should store this information when the batch is done being processed to avoid inconsistencies - rawdb.WriteDASyncedL1BlockNumber(bq.db, batch.L1BlockNumber()-1) + return true } -func (bq *BatchQueue) Reset(height uint64) { +// processAndDeleteBatch processes a batch and deletes the batch from map. Stores the syncing progress on disk. +func (bq *BatchQueue) processAndDeleteBatch(batch da.Entry) da.EntryWithBlocks { + if !bq.deleteBatch(batch.BatchIndex()) { + return nil + } + + entryWithBlocks, ok := batch.(da.EntryWithBlocks) + if !ok { + // this should only happen if we delete a reverted batch + return nil + } + + // sanity check that the next batch is the one we expect. If not, we skip the batch. + if bq.previousBatch.BatchIndex > 0 && bq.previousBatch.BatchIndex+1 != entryWithBlocks.BatchIndex() { + log.Info("BatchQueue: skipping batch ", "currentBatch", entryWithBlocks.BatchIndex(), "previousBatch", bq.previousBatch.BatchIndex) + return nil + } + + // carry forward the total L1 messages popped from the previous batch + entryWithBlocks.SetParentTotalL1MessagePopped(bq.previousBatch.TotalL1MessagesPopped) + + // we store the previous batch as it has been completely processed which we know because the next batch is requested within the pipeline. + // In case of a restart or crash we can continue from the last processed batch (and its metadata). + rawdb.WriteDAProcessedBatchMeta(bq.db, bq.previousBatch) + + log.Info("processing batch", "batchIndex", entryWithBlocks.BatchIndex(), "L1BlockNumber", entryWithBlocks.L1BlockNumber(), "totalL1MessagesPopped", entryWithBlocks.TotalL1MessagesPopped(), "previousBatch", bq.previousBatch.BatchIndex, "previousL1BlockNumber", bq.previousBatch.L1BlockNumber, "previous TotalL1MessagesPopped", bq.previousBatch.TotalL1MessagesPopped) + + bq.previousBatch = &rawdb.DAProcessedBatchMeta{ + L1BlockNumber: entryWithBlocks.L1BlockNumber(), + BatchIndex: entryWithBlocks.BatchIndex(), + TotalL1MessagesPopped: entryWithBlocks.TotalL1MessagesPopped(), + } + + return entryWithBlocks +} + +func (bq *BatchQueue) Reset(lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta) { bq.batches.Clear() bq.batchesMap.Clear() - bq.lastFinalizedBatchIndex = 0 - bq.DAQueue.Reset(height) + bq.lastFinalizedBatchIndex = lastProcessedBatchMeta.BatchIndex + bq.previousBatch = lastProcessedBatchMeta + bq.DAQueue.Reset(lastProcessedBatchMeta) } diff --git a/rollup/da_syncer/block_queue.go b/rollup/da_syncer/block_queue.go index a122d41ab3..630382f001 100644 --- a/rollup/da_syncer/block_queue.go +++ b/rollup/da_syncer/block_queue.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" ) @@ -34,23 +35,20 @@ func (bq *BlockQueue) NextBlock(ctx context.Context) (*da.PartialBlock, error) { } func (bq *BlockQueue) getBlocksFromBatch(ctx context.Context) error { - daEntry, err := bq.batchQueue.NextBatch(ctx) + entryWithBlocks, err := bq.batchQueue.NextBatch(ctx) if err != nil { return err } - entryWithBlocks, ok := daEntry.(da.EntryWithBlocks) - // this should never happen because we only receive CommitBatch entries - if !ok { - return fmt.Errorf("unexpected type of daEntry: %T", daEntry) + bq.blocks, err = entryWithBlocks.Blocks() + if err != nil { + return fmt.Errorf("failed to get blocks from entry: %w", err) } - bq.blocks = entryWithBlocks.Blocks() - return nil } -func (bq *BlockQueue) Reset(height uint64) { +func (bq *BlockQueue) Reset(lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta) { bq.blocks = make([]*da.PartialBlock, 0) - bq.batchQueue.Reset(height) + bq.batchQueue.Reset(lastProcessedBatchMeta) } diff --git a/rollup/da_syncer/da/calldata_blob_source.go b/rollup/da_syncer/da/calldata_blob_source.go index aa9d32c1b9..1ab0762f02 100644 --- a/rollup/da_syncer/da/calldata_blob_source.go +++ b/rollup/da_syncer/da/calldata_blob_source.go @@ -144,7 +144,7 @@ func (ds *CalldataBlobSource) processRollupEventsToDA(rollupEvents l1.RollupEven // add commit event to the list of previous commit events, so we can process events created in the same tx together lastCommitTransactionHash = commitEvent.TxHash() lastCommitEvents = append(lastCommitEvents, commitEvent) - case l1.RevertEventType: + case l1.RevertEventV0Type, l1.RevertEventV7Type: // if we have any previous commit events, we need to create a new DA before processing the revert event if len(lastCommitEvents) > 0 { if err = getAndAppendCommitBatchDA(); err != nil { @@ -152,13 +152,7 @@ func (ds *CalldataBlobSource) processRollupEventsToDA(rollupEvents l1.RollupEven } } - 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) + entry = NewRevertBatch(rollupEvent) entries = append(entries, entry) case l1.FinalizeEventType: // if we have any previous commit events, we need to create a new DA before processing the finalized event @@ -235,15 +229,15 @@ func (ds *CalldataBlobSource) getCommitBatchDA(commitEvents []*l1.CommitBatchEve } switch codec.Version() { - case 0: + case encoding.CodecV0: if entry, err = NewCommitBatchDAV0(ds.db, codec, commitEvent, args.ParentBatchHeader, args.Chunks, args.SkippedL1MessageBitmap); err != nil { return nil, fmt.Errorf("failed to decode DA, batch index: %d, err: %w", commitEvent.BatchIndex().Uint64(), err) } - case 1, 2, 3, 4, 5, 6: + case encoding.CodecV1, encoding.CodecV2, encoding.CodecV3, encoding.CodecV4, encoding.CodecV5, encoding.CodecV6: if entry, err = NewCommitBatchDAV1(ds.ctx, ds.db, ds.blobClient, codec, commitEvent, args.ParentBatchHeader, args.Chunks, args.SkippedL1MessageBitmap, args.BlobHashes, blockHeader.Time); err != nil { return nil, fmt.Errorf("failed to decode DA, batch index: %d, err: %w", commitEvent.BatchIndex().Uint64(), err) } - default: // CodecVersion 7 and above + default: // CodecV7 and above if i >= len(args.BlobHashes) { return nil, fmt.Errorf("not enough blob hashes for commit transaction: %s, index in tx: %d, batch index: %d, hash: %s", firstCommitEvent.TxHash(), i, commitEvent.BatchIndex().Uint64(), commitEvent.BatchHash().Hex()) } @@ -251,7 +245,7 @@ func (ds *CalldataBlobSource) getCommitBatchDA(commitEvents []*l1.CommitBatchEve var parentBatchHash common.Hash if previousEvent == nil { - parentBatchHash = common.BytesToHash(args.ParentBatchHeader) + parentBatchHash = args.ParentBatchHash } else { parentBatchHash = previousEvent.BatchHash() } @@ -265,5 +259,14 @@ func (ds *CalldataBlobSource) getCommitBatchDA(commitEvents []*l1.CommitBatchEve entries = append(entries, entry) } + if codec.Version() >= encoding.CodecV7 { + // sanity check that the last batch hash from the tx is equal to the last batch hash from the commit events. + // this means that all batches in the transaction have been successfully processed. + lastBatch := entries[len(entries)-1] + if args.LastBatchHash != lastBatch.Event().BatchHash() { + return nil, fmt.Errorf("last batch hash from tx is not equal to the one from commit events: LastBatchHash in calldata of tx: %s, last batch hash from events: %s", args.LastBatchHash.Hex(), lastBatch.Event().BatchHash().Hex()) + } + } + return entries, nil } diff --git a/rollup/da_syncer/da/commitV0.go b/rollup/da_syncer/da/commitV0.go index 659d9a57f7..c99a474b25 100644 --- a/rollup/da_syncer/da/commitV0.go +++ b/rollup/da_syncer/da/commitV0.go @@ -16,12 +16,14 @@ import ( ) type CommitBatchDAV0 struct { + db ethdb.Database + version encoding.CodecVersion batchIndex uint64 parentTotalL1MessagePopped uint64 + l1MessagesPopped int skippedL1MessageBitmap []byte chunks []*encoding.DAChunkRawTx - l1Txs []*types.L1MessageTx event *l1.CommitBatchEvent } @@ -50,18 +52,15 @@ func NewCommitBatchDAV0WithChunks(db ethdb.Database, event *l1.CommitBatchEvent, ) (*CommitBatchDAV0, error) { parentTotalL1MessagePopped := getBatchTotalL1MessagePopped(parentBatchHeader) - l1Txs, err := getL1Messages(db, parentTotalL1MessagePopped, skippedL1MessageBitmap, getTotalMessagesPoppedFromChunks(decodedChunks)) - if err != nil { - return nil, fmt.Errorf("failed to get L1 messages for v0 batch %d: %w", batchIndex, err) - } return &CommitBatchDAV0{ + db: db, version: version, batchIndex: batchIndex, parentTotalL1MessagePopped: parentTotalL1MessagePopped, + l1MessagesPopped: getTotalMessagesPoppedFromChunks(decodedChunks), skippedL1MessageBitmap: skippedL1MessageBitmap, chunks: decodedChunks, - l1Txs: l1Txs, event: event, }, nil } @@ -110,7 +109,12 @@ func (c *CommitBatchDAV0) CompareTo(other Entry) int { return 0 } -func (c *CommitBatchDAV0) Blocks() []*PartialBlock { +func (c *CommitBatchDAV0) Blocks() ([]*PartialBlock, error) { + l1Txs, err := getL1Messages(c.db, c.parentTotalL1MessagePopped, c.skippedL1MessageBitmap, c.l1MessagesPopped) + if err != nil { + return nil, fmt.Errorf("failed to get L1 messages for v0 batch %d: %w", c.batchIndex, err) + } + var blocks []*PartialBlock l1TxPointer := 0 @@ -120,8 +124,8 @@ func (c *CommitBatchDAV0) Blocks() []*PartialBlock { // create txs txs := make(types.Transactions, 0, daBlock.NumTransactions()) // insert l1 msgs - for l1TxPointer < len(c.l1Txs) && c.l1Txs[l1TxPointer].QueueIndex < curL1TxIndex+uint64(daBlock.NumL1Messages()) { - l1Tx := types.NewTx(c.l1Txs[l1TxPointer]) + for l1TxPointer < len(l1Txs) && l1Txs[l1TxPointer].QueueIndex < curL1TxIndex+uint64(daBlock.NumL1Messages()) { + l1Tx := types.NewTx(l1Txs[l1TxPointer]) txs = append(txs, l1Tx) l1TxPointer++ } @@ -144,7 +148,19 @@ func (c *CommitBatchDAV0) Blocks() []*PartialBlock { } } - return blocks + return blocks, nil +} + +func (c *CommitBatchDAV0) SetParentTotalL1MessagePopped(totalL1MessagePopped uint64) { + // we ignore setting parentTotalL1MessagePopped from outside as it is calculated from parent batch header for V0 batches +} + +func (c *CommitBatchDAV0) TotalL1MessagesPopped() uint64 { + return c.parentTotalL1MessagePopped + uint64(c.l1MessagesPopped) +} + +func (c *CommitBatchDAV0) L1MessagesPoppedInBatch() uint64 { + return uint64(c.l1MessagesPopped) } func getTotalMessagesPoppedFromChunks(decodedChunks []*encoding.DAChunkRawTx) int { diff --git a/rollup/da_syncer/da/commitV7.go b/rollup/da_syncer/da/commitV7.go index 6a865ea1cd..6048b853ae 100644 --- a/rollup/da_syncer/da/commitV7.go +++ b/rollup/da_syncer/da/commitV7.go @@ -20,13 +20,15 @@ import ( ) type CommitBatchDAV7 struct { - version encoding.CodecVersion - batchIndex uint64 - initialL1MessageIndex uint64 - blocks []encoding.DABlock - transactions []types.Transactions - l1Txs []types.Transactions - versionedHashes []common.Hash + db ethdb.Database + + version encoding.CodecVersion + batchIndex uint64 + versionedHashes []common.Hash + blobPayload encoding.DABlobPayload + + parentTotalL1MessagePopped uint64 + l1MessagesPopped uint64 event *l1.CommitBatchEvent } @@ -71,20 +73,14 @@ func NewCommitBatchDAV7(ctx context.Context, db ethdb.Database, return nil, fmt.Errorf("failed to decode blob: %w", err) } - l1Txs, err := getL1MessagesV7(db, blobPayload.Blocks(), blobPayload.InitialL1MessageIndex()) - if err != nil { - return nil, fmt.Errorf("failed to get L1 messages for v7 batch %d: %w", commitEvent.BatchIndex().Uint64(), err) - } - return &CommitBatchDAV7{ - version: codec.Version(), - batchIndex: commitEvent.BatchIndex().Uint64(), - initialL1MessageIndex: blobPayload.InitialL1MessageIndex(), - blocks: blobPayload.Blocks(), - transactions: blobPayload.Transactions(), - l1Txs: l1Txs, - versionedHashes: []common.Hash{blobVersionedHash}, - event: commitEvent, + db: db, + version: codec.Version(), + batchIndex: commitEvent.BatchIndex().Uint64(), + versionedHashes: []common.Hash{blobVersionedHash}, + blobPayload: blobPayload, + l1MessagesPopped: getL1MessagesPoppedFromBlocks(blobPayload.Blocks()), + event: commitEvent, }, nil } @@ -117,18 +113,26 @@ func (c *CommitBatchDAV7) Event() l1.RollupEvent { return c.event } -func (c *CommitBatchDAV7) Blocks() []*PartialBlock { +func (c *CommitBatchDAV7) Blocks() ([]*PartialBlock, error) { + initialL1MessageIndex := c.parentTotalL1MessagePopped + + l1Txs, err := getL1MessagesV7(c.db, c.blobPayload.Blocks(), initialL1MessageIndex) + if err != nil { + return nil, fmt.Errorf("failed to get L1 messages for v7 batch %d: %w", c.event.BatchIndex().Uint64(), err) + } + var blocks []*PartialBlock - for i, daBlock := range c.blocks { + for i, daBlock := range c.blobPayload.Blocks() { // create txs txs := make(types.Transactions, 0, daBlock.NumTransactions()) // insert L1 messages - txs = append(txs, c.l1Txs[i]...) + txs = append(txs, l1Txs[i]...) + // TODO: sanity check L1 messages with prev and post hashes // insert L2 txs - txs = append(txs, c.transactions[i]...) + txs = append(txs, c.blobPayload.Transactions()[i]...) block := NewPartialBlock( &PartialHeader{ @@ -143,7 +147,19 @@ func (c *CommitBatchDAV7) Blocks() []*PartialBlock { blocks = append(blocks, block) } - return blocks + return blocks, nil +} + +func (c *CommitBatchDAV7) SetParentTotalL1MessagePopped(totalL1MessagePopped uint64) { + c.parentTotalL1MessagePopped = totalL1MessagePopped +} + +func (c *CommitBatchDAV7) TotalL1MessagesPopped() uint64 { + return c.parentTotalL1MessagePopped + c.l1MessagesPopped +} + +func (c *CommitBatchDAV7) L1MessagesPoppedInBatch() uint64 { + return c.l1MessagesPopped } func (c *CommitBatchDAV7) Version() encoding.CodecVersion { @@ -153,8 +169,8 @@ func (c *CommitBatchDAV7) Version() encoding.CodecVersion { func (c *CommitBatchDAV7) Chunks() []*encoding.DAChunkRawTx { return []*encoding.DAChunkRawTx{ { - Blocks: c.blocks, - Transactions: c.transactions, + Blocks: c.blobPayload.Blocks(), + Transactions: c.blobPayload.Transactions(), }, } } @@ -189,3 +205,13 @@ func getL1MessagesV7(db ethdb.Database, blocks []encoding.DABlock, initialL1Mess return allTxs, nil } + +func getL1MessagesPoppedFromBlocks(blocks []encoding.DABlock) uint64 { + var totalL1MessagePopped uint64 + + for _, block := range blocks { + totalL1MessagePopped += uint64(block.NumL1Messages()) + } + + return totalL1MessagePopped +} diff --git a/rollup/da_syncer/da/da.go b/rollup/da_syncer/da/da.go index cd7320f1c0..fe72473451 100644 --- a/rollup/da_syncer/da/da.go +++ b/rollup/da_syncer/da/da.go @@ -34,10 +34,13 @@ type Entry interface { type EntryWithBlocks interface { Entry - Blocks() []*PartialBlock + Blocks() ([]*PartialBlock, error) Version() encoding.CodecVersion Chunks() []*encoding.DAChunkRawTx BlobVersionedHashes() []common.Hash + SetParentTotalL1MessagePopped(uint64) + TotalL1MessagesPopped() uint64 + L1MessagesPoppedInBatch() uint64 } type Entries []Entry diff --git a/rollup/da_syncer/da/revert.go b/rollup/da_syncer/da/revert.go index f8120fd3f1..37b3ef11d4 100644 --- a/rollup/da_syncer/da/revert.go +++ b/rollup/da_syncer/da/revert.go @@ -5,10 +5,10 @@ import ( ) type RevertBatch struct { - event *l1.RevertBatchEvent + event l1.RollupEvent } -func NewRevertBatch(event *l1.RevertBatchEvent) *RevertBatch { +func NewRevertBatch(event l1.RollupEvent) *RevertBatch { return &RevertBatch{ event: event, } @@ -21,6 +21,7 @@ func (r *RevertBatch) Type() Type { func (r *RevertBatch) L1BlockNumber() uint64 { return r.event.BlockNumber() } + func (r *RevertBatch) BatchIndex() uint64 { return r.event.BatchIndex().Uint64() } diff --git a/rollup/da_syncer/da_queue.go b/rollup/da_syncer/da_queue.go index a394357228..65a0acd181 100644 --- a/rollup/da_syncer/da_queue.go +++ b/rollup/da_syncer/da_queue.go @@ -4,25 +4,23 @@ import ( "context" "errors" - "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/rollup/da_syncer/da" "github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors" ) // DAQueue is a pipeline stage that reads DA entries from a DataSource and provides them to the next stage. type DAQueue struct { - l1height uint64 - initialBatch uint64 + l1height uint64 dataSourceFactory *DataSourceFactory dataSource DataSource da da.Entries } -func NewDAQueue(l1height uint64, initialBatch uint64, dataSourceFactory *DataSourceFactory) *DAQueue { +func NewDAQueue(l1height uint64, dataSourceFactory *DataSourceFactory) *DAQueue { return &DAQueue{ l1height: l1height, - initialBatch: initialBatch, dataSourceFactory: dataSourceFactory, dataSource: nil, da: make(da.Entries, 0), @@ -47,11 +45,6 @@ func (dq *DAQueue) NextDA(ctx context.Context) (da.Entry, error) { daEntry := dq.da[0] dq.da = dq.da[1:] - if daEntry.BatchIndex() < dq.initialBatch { - log.Debug("Skipping DA entry due to initial batch requirement", "batchIndex", daEntry.BatchIndex(), "initialBatch", dq.initialBatch) - continue - } - return daEntry, nil } } @@ -86,8 +79,8 @@ func (dq *DAQueue) DataSource() DataSource { return dq.dataSource } -func (dq *DAQueue) Reset(height uint64) { - dq.l1height = height +func (dq *DAQueue) Reset(lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta) { + dq.l1height = lastProcessedBatchMeta.L1BlockNumber dq.dataSource = nil dq.da = make(da.Entries, 0) } diff --git a/rollup/da_syncer/l1_message_queue_height_finder.go b/rollup/da_syncer/l1_message_queue_height_finder.go new file mode 100644 index 0000000000..c6789c1589 --- /dev/null +++ b/rollup/da_syncer/l1_message_queue_height_finder.go @@ -0,0 +1,136 @@ +package da_syncer + +import ( + "context" + "fmt" + + "github.com/scroll-tech/go-ethereum/ethdb" + "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" +) + +type L1MessageQueueHeightFinder struct { + ctx context.Context + calldataBlobSource *da.CalldataBlobSource + l1Reader *l1.Reader +} + +func NewL1MessageQueueHeightFinder(ctx context.Context, l1height uint64, l1Reader *l1.Reader, blobClient blob_client.BlobClient, db ethdb.Database) (*L1MessageQueueHeightFinder, error) { + calldataBlobSource, err := da.NewCalldataBlobSource(ctx, l1height, l1Reader, blobClient, db) + if err != nil { + return nil, fmt.Errorf("failed to create calldata blob source: %w", err) + } + + return &L1MessageQueueHeightFinder{ + ctx: ctx, + calldataBlobSource: calldataBlobSource, + l1Reader: l1Reader, + }, nil +} + +// TotalL1MessagesPoppedBefore finds the total L1 messages popped (L1 message queue height) before target batch. +// It does so by: +// 1. find bundle in which target batch was finalized +// 2. fetch the tx of the bundle to get the height of the L1 message queue after the bundle +// 3. with this information we can calculate the L1 message count for each batch from last finalized bundle to the target batch. +func (f *L1MessageQueueHeightFinder) TotalL1MessagesPoppedBefore(targetBatch uint64) (uint64, error) { + batches := make(map[uint64]da.EntryWithBlocks) + + finalizedBundle, err := f.findFinalizedBundle(targetBatch, batches) + if err != nil { + return 0, fmt.Errorf("failed to find the bundle in which the target batch was finalized") + } + + // 2. fetch tx of the bundle to get the TotalL1MessagesPoppedOverall after the bundle and the first batch within the bundle. + args, err := f.l1Reader.FetchFinalizeTxDataPostEuclidV2(finalizedBundle.Event().(*l1.FinalizeBatchEvent)) + if err != nil { + return 0, fmt.Errorf("failed to fetch finalize tx data: %w", err) + } + + // 3. with this information we can calculate the L1 message queue height for target batch: for each batch from last finalized batch to the target batch subtract L1 messages popped in the batch from L1 message queue height + lastBatchInBundle := finalizedBundle.BatchIndex() + + var l1MessageQueueHeight uint64 + // totalL1MessagesPoppedOverall is the number of messages processed after the bundle -> subtract 1 to get the last message in the bundle + if args.TotalL1MessagesPoppedOverall.Uint64() > 0 { + l1MessageQueueHeight = args.TotalL1MessagesPoppedOverall.Uint64() - 1 + } + + for i := lastBatchInBundle; i >= targetBatch; i-- { + batch, ok := batches[i] + if !ok { + return 0, fmt.Errorf("batch %d not found", i) + } + + if batch.L1MessagesPoppedInBatch() > l1MessageQueueHeight { + return 0, fmt.Errorf("L1 message queue height is less than L1 messages popped in batch %d (%d < %d)", i, l1MessageQueueHeight, batch.L1MessagesPoppedInBatch()) + } + l1MessageQueueHeight -= batch.L1MessagesPoppedInBatch() + } + + return l1MessageQueueHeight, nil +} + +func (f *L1MessageQueueHeightFinder) findFinalizedBundle(targetBatch uint64, batches map[uint64]da.EntryWithBlocks) (*da.FinalizeBatch, error) { + for { + // 1. find bundle in which target batch was finalized + daEntries, err := f.calldataBlobSource.NextData() + if err != nil { + return nil, fmt.Errorf("failed to get next data: %w", err) + } + + for _, daEntry := range daEntries { + switch daEntry.Type() { + case da.CommitBatchV0Type, da.CommitBatchWithBlobType: + daEntryWithBlocks, ok := daEntry.(da.EntryWithBlocks) + if !ok { + return nil, fmt.Errorf("unexpected type of daEntry: %T, expected EntryWithBlocks", daEntry) + } + + // save the batch for later use + batches[daEntry.BatchIndex()] = daEntryWithBlocks + case da.RevertBatchType: + if err = f.handleRevertEvent(batches, daEntry.Event()); err != nil { + return nil, fmt.Errorf("failed to handle revert event: %w", err) + } + case da.FinalizeBatchType: + // the finalized event is triggered only for the last batch in the bundle: + // we found the bundle in which the target batch was finalized + if daEntry.BatchIndex() >= targetBatch { + return daEntry.(*da.FinalizeBatch), nil + } + + default: + return nil, fmt.Errorf("unexpected type of daEntry: %T", daEntry) + } + } + } + +} + +func (f *L1MessageQueueHeightFinder) handleRevertEvent(batches map[uint64]da.EntryWithBlocks, event l1.RollupEvent) error { + switch event.Type() { + case l1.RevertEventV0Type: + revertBatch, ok := event.(*l1.RevertBatchEventV0) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV0Type", event) + } + + delete(batches, revertBatch.BatchIndex().Uint64()) + case l1.RevertEventV7Type: + revertBatch, ok := event.(*l1.RevertBatchEventV7) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV7Type", event) + } + + // delete all batches from revertBatch.StartBatchIndex (inclusive) to revertBatch.FinishBatchIndex (inclusive) + for i := revertBatch.StartBatchIndex().Uint64(); i <= revertBatch.FinishBatchIndex().Uint64(); i++ { + delete(batches, i) + } + default: + return fmt.Errorf("unexpected type of revert event: %T", event) + } + + return nil +} diff --git a/rollup/da_syncer/syncing_pipeline.go b/rollup/da_syncer/syncing_pipeline.go index 844f23e72c..080179107f 100644 --- a/rollup/da_syncer/syncing_pipeline.go +++ b/rollup/da_syncer/syncing_pipeline.go @@ -79,29 +79,51 @@ func NewSyncingPipeline(ctx context.Context, blockchain *core.BlockChain, genesi } dataSourceFactory := NewDataSourceFactory(blockchain, genesisConfig, config, l1Reader, blobClientList, db) - var initialL1Block uint64 + var lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta if config.RecoveryMode { - initialL1Block = config.InitialL1Block - if initialL1Block == 0 { + if config.InitialL1Block == 0 { return nil, errors.New("sync from DA: initial L1 block must be set in recovery mode") } if config.InitialBatch == 0 { return nil, errors.New("sync from DA: initial batch must be set in recovery mode") } - log.Info("sync from DA: initializing pipeline in recovery mode", "initialL1Block", initialL1Block, "initialBatch", config.InitialBatch) - } else { - initialL1Block = l1DeploymentBlock - 1 - config.InitialL1Block = initialL1Block - from := rawdb.ReadDASyncedL1BlockNumber(db) - if from != nil { - initialL1Block = *from + l1MessageQueueHeightFinder, err := NewL1MessageQueueHeightFinder(ctx, config.InitialL1Block, l1Reader, blobClientList, db) + if err != nil { + return nil, fmt.Errorf("failed to create L1MessageQueueHeightFinder: %w", err) } - log.Info("sync from DA: initializing pipeline", "initialL1Block", initialL1Block) + + l1MessageQueueHeightBeforeInitialBatch, err := l1MessageQueueHeightFinder.TotalL1MessagesPoppedBefore(config.InitialBatch) + if err != nil { + return nil, fmt.Errorf("failed to find L1 message queue height before initial batch: %w", err) + } + + lastProcessedBatchMeta = &rawdb.DAProcessedBatchMeta{ + BatchIndex: config.InitialBatch, + L1BlockNumber: config.InitialL1Block, + TotalL1MessagesPopped: l1MessageQueueHeightBeforeInitialBatch, + } + + log.Info("sync from DA: initializing pipeline in recovery mode", "initialL1Block", config.InitialL1Block, "initialBatch", config.InitialBatch, "L1BlockNumber", lastProcessedBatchMeta.L1BlockNumber, "TotalL1MessagesPopped", lastProcessedBatchMeta.TotalL1MessagesPopped) + } else { + lastProcessedBatchMeta = rawdb.ReadDAProcessedBatchMeta(db) + if lastProcessedBatchMeta == nil { + var l1BlockNumber uint64 + if l1DeploymentBlock > 0 { + l1BlockNumber = l1DeploymentBlock - 1 + } + lastProcessedBatchMeta = &rawdb.DAProcessedBatchMeta{ + BatchIndex: 0, + L1BlockNumber: l1BlockNumber, + TotalL1MessagesPopped: 0, + } + rawdb.WriteDAProcessedBatchMeta(db, lastProcessedBatchMeta) + } + log.Info("sync from DA: initializing pipeline", "BatchIndex", lastProcessedBatchMeta.BatchIndex, "L1BlockNumber", lastProcessedBatchMeta.L1BlockNumber, "TotalL1MessagesPopped", lastProcessedBatchMeta.TotalL1MessagesPopped) } - daQueue := NewDAQueue(initialL1Block, config.InitialBatch, dataSourceFactory) - batchQueue := NewBatchQueue(daQueue, db) + daQueue := NewDAQueue(lastProcessedBatchMeta.L1BlockNumber, dataSourceFactory) + batchQueue := NewBatchQueue(daQueue, db, lastProcessedBatchMeta) blockQueue := NewBlockQueue(batchQueue) daSyncer := NewDASyncer(blockchain, config.L2EndBlock) @@ -260,12 +282,21 @@ func (s *SyncingPipeline) Stop() { func (s *SyncingPipeline) reset(resetCounter int) { amount := 100 * uint64(resetCounter) - syncedL1Height := s.config.InitialL1Block - from := rawdb.ReadDASyncedL1BlockNumber(s.db) - if from != nil && *from+amount > syncedL1Height { - syncedL1Height = *from - amount - rawdb.WriteDASyncedL1BlockNumber(s.db, syncedL1Height) + + lastProcessedBatchMeta := rawdb.ReadDAProcessedBatchMeta(s.db) + if lastProcessedBatchMeta == nil { + lastProcessedBatchMeta = &rawdb.DAProcessedBatchMeta{ + BatchIndex: 0, + L1BlockNumber: s.config.InitialL1Block - amount, + TotalL1MessagesPopped: 0, + } } - log.Info("resetting syncing pipeline", "syncedL1Height", syncedL1Height) - s.blockQueue.Reset(syncedL1Height) + + if lastProcessedBatchMeta.L1BlockNumber > amount { + lastProcessedBatchMeta.L1BlockNumber -= amount + rawdb.WriteDAProcessedBatchMeta(s.db, lastProcessedBatchMeta) + } + + log.Info("resetting syncing pipeline", "batch index", lastProcessedBatchMeta.BatchIndex, "L1BlockNumber", lastProcessedBatchMeta.L1BlockNumber, "TotalL1MessagesPopped", lastProcessedBatchMeta.TotalL1MessagesPopped) + s.blockQueue.Reset(lastProcessedBatchMeta) } diff --git a/rollup/l1/abi.go b/rollup/l1/abi.go index de23204536..bcc35b5a75 100644 --- a/rollup/l1/abi.go +++ b/rollup/l1/abi.go @@ -24,7 +24,7 @@ func init() { // 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\"}]", + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_chainId\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_messageQueueV1\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_messageQueueV2\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_verifier\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_systemConfig\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addProver\",\"inputs\":[{\"name\":\"_account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addSequencer\",\"inputs\":[{\"name\":\"_account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"commitAndFinalizeBatch\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentBatchHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"finalizeStruct\",\"type\":\"tuple\",\"internalType\":\"struct IScrollChain.FinalizeStruct\",\"components\":[{\"name\":\"batchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"lastProcessedQueueIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"postStateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"withdrawRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"zkProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"commitBatchWithBlobProof\",\"inputs\":[{\"name\":\"_version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"_parentBatchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_chunks\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"_skippedL1MessageBitmap\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_blobDataProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"commitBatches\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentBatchHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"lastBatchHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"committedBatches\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disableEnforcedBatchMode\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"finalizeBundlePostEuclidV2\",\"inputs\":[{\"name\":\"batchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"lastProcessedQueueIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"postStateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"withdrawRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"aggrProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"finalizeBundleWithProof\",\"inputs\":[{\"name\":\"batchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"postStateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"withdrawRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"aggrProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"finalizedStateRoots\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"importGenesisBatch\",\"inputs\":[{\"name\":\"_batchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_stateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"initialEuclidBatchIndex\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_messageQueue\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_verifier\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_maxNumTxInChunk\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"initializeV2\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isBatchFinalized\",\"inputs\":[{\"name\":\"_batchIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isEnforcedModeEnabled\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isProver\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isSequencer\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"lastFinalizedBatchIndex\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"layer2ChainId\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maxNumTxInChunk\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"messageQueueV1\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"messageQueueV2\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"miscData\",\"inputs\":[],\"outputs\":[{\"name\":\"lastCommittedBatchIndex\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"lastFinalizedBatchIndex\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"lastFinalizeTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"flags\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"reserved\",\"type\":\"uint88\",\"internalType\":\"uint88\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"removeProver\",\"inputs\":[{\"name\":\"_account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"removeSequencer\",\"inputs\":[{\"name\":\"_account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"revertBatch\",\"inputs\":[{\"name\":\"batchHeader\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setPause\",\"inputs\":[{\"name\":\"_status\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"systemConfig\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"updateMaxNumTxInChunk\",\"inputs\":[{\"name\":\"_maxNumTxInChunk\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"verifier\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdrawRoots\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"CommitBatch\",\"inputs\":[{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"batchHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FinalizeBatch\",\"inputs\":[{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"batchHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"stateRoot\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"withdrawRoot\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RevertBatch\",\"inputs\":[{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"batchHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RevertBatch\",\"inputs\":[{\"name\":\"startBatchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"finishBatchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UpdateEnforcedBatchMode\",\"inputs\":[{\"name\":\"enabled\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"},{\"name\":\"lastCommittedBatchIndex\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UpdateMaxNumTxInChunk\",\"inputs\":[{\"name\":\"oldMaxNumTxInChunk\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newMaxNumTxInChunk\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UpdateProver\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"status\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UpdateSequencer\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"status\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ErrorAccountIsNotEOA\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchHeaderV0LengthTooSmall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchHeaderV1LengthTooSmall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchHeaderV3LengthMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchHeaderV7LengthMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchIsAlreadyCommitted\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchIsAlreadyVerified\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchIsEmpty\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchNotCommitted\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorCallPointEvaluationPrecompileFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorCallerIsNotProver\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorCallerIsNotSequencer\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorCannotDowngradeVersion\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorEuclidForkEnabled\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorFinalizePreAndPostEuclidBatchInOneBundle\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorFoundMultipleBlobs\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorGenesisBatchHasNonZeroField\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorGenesisBatchImported\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorGenesisDataHashIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorGenesisParentBatchHashIsNonZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorInEnforcedBatchMode\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectBatchHash\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectBatchVersion\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectBitmapLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectBitmapLengthV0\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectBitmapLengthV1\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectChunkLengthV1\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorLastL1MessageSkipped\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNoBlobFound\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNoBlockInChunkV1\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNotAllV1MessagesAreFinalized\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNotInEnforcedBatchMode\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNumTxsLessThanNumL1Msgs\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorRevertFinalizedBatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorStateRootIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorTooManyTxsInOneChunk\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorUnexpectedPointEvaluationPrecompileOutput\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorV5BatchContainsTransactions\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorV5BatchNotContainsOnlyOneBlock\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorV5BatchNotContainsOnlyOneChunk\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorZeroAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InconsistentBatchHash\",\"inputs\":[{\"name\":\"batchIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expected\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"actual\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]}]", } // L1MessageQueueMetaDataManual contains all meta data concerning the L1MessageQueue contract. @@ -35,13 +35,18 @@ var L1MessageQueueMetaDataManual = &bind.MetaData{ const ( // CommitEventType contains data of event of commit batch CommitEventType int = iota - // RevertEventType contains data of event of revert batch - RevertEventType + // RevertEventV0Type contains data of event of revert batch from V0 to V6 + RevertEventV0Type + // RevertEventV7Type contains data of event of revert batch after V7 (EuclidV2) + RevertEventV7Type // FinalizeEventType contains data of event of finalize batch FinalizeEventType commitBatchMethodName = "commitBatch" commitBatchWithBlobProofMethodName = "commitBatchWithBlobProof" + commitBatchesV7MethodName = "commitBatches" + + finalizeBundlePostEuclidV2MethodName = "finalizeBundlePostEuclidV2" // the length of method ID at the beginning of transaction data methodIDLength = 4 @@ -102,13 +107,13 @@ func (c *CommitBatchEvent) CompareTo(other *CommitBatchEvent) int { return c.batchIndex.Cmp(other.batchIndex) } -type RevertBatchEventUnpacked struct { +type RevertBatchEventV0Unpacked struct { BatchIndex *big.Int BatchHash common.Hash } -// RevertBatchEvent represents a RevertBatch event raised by the ScrollChain contract. -type RevertBatchEvent struct { +// RevertBatchEventV0 represents a RevertBatch event raised by the ScrollChain contract from V0 to V6. +type RevertBatchEventV0 struct { batchIndex *big.Int batchHash common.Hash txHash common.Hash @@ -116,30 +121,82 @@ type RevertBatchEvent struct { blockNumber uint64 } -func (r *RevertBatchEvent) BlockNumber() uint64 { +func (r *RevertBatchEventV0) BlockNumber() uint64 { return r.blockNumber } -func (r *RevertBatchEvent) BlockHash() common.Hash { +func (r *RevertBatchEventV0) BlockHash() common.Hash { return r.blockHash } -func (r *RevertBatchEvent) TxHash() common.Hash { +func (r *RevertBatchEventV0) TxHash() common.Hash { return r.txHash } -func (r *RevertBatchEvent) Type() int { - return RevertEventType +func (r *RevertBatchEventV0) Type() int { + return RevertEventV0Type } -func (r *RevertBatchEvent) BatchIndex() *big.Int { +func (r *RevertBatchEventV0) BatchIndex() *big.Int { return r.batchIndex } -func (r *RevertBatchEvent) BatchHash() common.Hash { +func (r *RevertBatchEventV0) BatchHash() common.Hash { return r.batchHash } +type RevertBatchEventV7Unpacked struct { + StartBatchIndex *big.Int + FinishBatchIndex *big.Int +} + +// RevertBatchEventV7 represents a RevertBatch event raised by the ScrollChain contract after V7 (EuclidV2). +// It reverts a range of batches from startBatchIndex (inclusive) to finishBatchIndex (inclusive). +type RevertBatchEventV7 struct { + startBatchIndex *big.Int + finishBatchIndex *big.Int + + txHash common.Hash + blockHash common.Hash + blockNumber uint64 +} + +func (r *RevertBatchEventV7) BlockNumber() uint64 { + return r.blockNumber +} + +func (r *RevertBatchEventV7) BlockHash() common.Hash { + return r.blockHash +} + +func (r *RevertBatchEventV7) TxHash() common.Hash { + return r.txHash +} + +func (r *RevertBatchEventV7) Type() int { + return RevertEventV7Type +} + +// BatchIndex returns the start batch index of the reverted range. +func (r *RevertBatchEventV7) BatchIndex() *big.Int { + return r.startBatchIndex +} + +// BatchHash is not applicable for RevertBatchEventV7. +func (r *RevertBatchEventV7) BatchHash() common.Hash { + return common.Hash{} +} + +// StartBatchIndex returns the start batch index of the reverted range. +func (r *RevertBatchEventV7) StartBatchIndex() *big.Int { + return r.startBatchIndex +} + +// FinishBatchIndex returns the finish batch index of the reverted range. +func (r *RevertBatchEventV7) FinishBatchIndex() *big.Int { + return r.finishBatchIndex +} + type FinalizeBatchEventUnpacked struct { BatchIndex *big.Int BatchHash common.Hash @@ -235,6 +292,10 @@ type CommitBatchArgs struct { Chunks [][]byte SkippedL1MessageBitmap []byte BlobHashes []common.Hash + + // added in CodecV7 + ParentBatchHash common.Hash + LastBatchHash common.Hash } func newCommitBatchArgs(method *abi.Method, values []interface{}) (*CommitBatchArgs, error) { @@ -257,6 +318,20 @@ func newCommitBatchArgsFromCommitBatchWithProof(method *abi.Method, values []int }, nil } +func newCommitBatchArgsFromCommitBatchesV7(method *abi.Method, values []any) (*CommitBatchArgs, error) { + var args commitBatchesV7Args + err := method.Inputs.Copy(&args, values) + if err != nil { + return nil, err + } + + return &CommitBatchArgs{ + Version: args.Version, + ParentBatchHash: args.ParentBatchHash, + LastBatchHash: args.LastBatchHash, + }, nil +} + type commitBatchWithBlobProofArgs struct { Version uint8 ParentBatchHeader []byte @@ -264,3 +339,26 @@ type commitBatchWithBlobProofArgs struct { SkippedL1MessageBitmap []byte BlobDataProof []byte } + +type commitBatchesV7Args struct { + Version uint8 + ParentBatchHash common.Hash + LastBatchHash common.Hash +} + +type FinalizeBatchArgs struct { + BatchHeader []byte + TotalL1MessagesPoppedOverall *big.Int + PostStateRoot common.Hash + WithdrawRoot common.Hash + AggrProof []byte +} + +func newFinalizeBatchArgs(method *abi.Method, values []any) (*FinalizeBatchArgs, error) { + var args FinalizeBatchArgs + err := method.Inputs.Copy(&args, values) + if err != nil { + return nil, err + } + return &args, nil +} diff --git a/rollup/l1/abi_test.go b/rollup/l1/abi_test.go index e50e8ccaa2..59fcb21ae5 100644 --- a/rollup/l1/abi_test.go +++ b/rollup/l1/abi_test.go @@ -12,13 +12,15 @@ import ( ) func TestEventSignatures(t *testing.T) { - 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) + assert.Equal(t, crypto.Keccak256Hash([]byte("CommitBatch(uint256,bytes32)")), ScrollChainABI.Events[commitBatchEventName].ID) + assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,bytes32)")), ScrollChainABI.Events[revertBatchV0EventName].ID) + assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,uint256)")), ScrollChainABI.Events[revertBatchV7EventName].ID) + assert.Equal(t, crypto.Keccak256Hash([]byte("FinalizeBatch(uint256,bytes32,bytes32,bytes32)")), ScrollChainABI.Events[finalizeBatchEventName].ID) } func TestUnpackLog(t *testing.T) { mockBatchIndex := big.NewInt(123) + finishMockBatchIndex := big.NewInt(125) mockBatchHash := crypto.Keccak256Hash([]byte("mockBatch")) mockStateRoot := crypto.Keccak256Hash([]byte("mockStateRoot")) mockWithdrawRoot := crypto.Keccak256Hash([]byte("mockWithdrawRoot")) @@ -42,16 +44,40 @@ func TestUnpackLog(t *testing.T) { &CommitBatchEventUnpacked{}, }, { - revertBatchEventName, + revertBatchV0EventName, types.Log{ Data: nil, - Topics: []common.Hash{ScrollChainABI.Events[revertBatchEventName].ID, common.BigToHash(mockBatchIndex), mockBatchHash}, + Topics: []common.Hash{ScrollChainABI.Events[revertBatchV0EventName].ID, common.BigToHash(mockBatchIndex), mockBatchHash}, }, - &RevertBatchEventUnpacked{ + &RevertBatchEventV0Unpacked{ BatchIndex: mockBatchIndex, BatchHash: mockBatchHash, }, - &RevertBatchEventUnpacked{}, + &RevertBatchEventV0Unpacked{}, + }, + { + revertBatchV7EventName, + types.Log{ + Data: nil, + Topics: []common.Hash{ScrollChainABI.Events[revertBatchV7EventName].ID, common.BigToHash(mockBatchIndex), common.BigToHash(mockBatchIndex)}, + }, + &RevertBatchEventV7Unpacked{ + StartBatchIndex: mockBatchIndex, + FinishBatchIndex: mockBatchIndex, + }, + &RevertBatchEventV7Unpacked{}, + }, + { + revertBatchV7EventName, + types.Log{ + Data: nil, + Topics: []common.Hash{ScrollChainABI.Events[revertBatchV7EventName].ID, common.BigToHash(mockBatchIndex), common.BigToHash(finishMockBatchIndex)}, + }, + &RevertBatchEventV7Unpacked{ + StartBatchIndex: mockBatchIndex, + FinishBatchIndex: finishMockBatchIndex, + }, + &RevertBatchEventV7Unpacked{}, }, { finalizeBatchEventName, diff --git a/rollup/l1/reader.go b/rollup/l1/reader.go index 82905f9511..6c39621d91 100644 --- a/rollup/l1/reader.go +++ b/rollup/l1/reader.go @@ -16,7 +16,8 @@ import ( const ( commitBatchEventName = "CommitBatch" - revertBatchEventName = "RevertBatch" + revertBatchV0EventName = "RevertBatch" + revertBatchV7EventName = "RevertBatch0" finalizeBatchEventName = "FinalizeBatch" nextUnfinalizedQueueIndex = "nextUnfinalizedQueueIndex" lastFinalizedBatchIndex = "lastFinalizedBatchIndex" @@ -32,7 +33,8 @@ type Reader struct { scrollChainABI *abi.ABI l1MessageQueueABI *abi.ABI l1CommitBatchEventSignature common.Hash - l1RevertBatchEventSignature common.Hash + l1RevertBatchEventV0Signature common.Hash + l1RevertBatchEventV7Signature common.Hash l1FinalizeBatchEventSignature common.Hash } @@ -60,7 +62,8 @@ func NewReader(ctx context.Context, config Config, l1Client Client) (*Reader, er scrollChainABI: ScrollChainABI, l1MessageQueueABI: L1MessageQueueABIManual, l1CommitBatchEventSignature: ScrollChainABI.Events[commitBatchEventName].ID, - l1RevertBatchEventSignature: ScrollChainABI.Events[revertBatchEventName].ID, + l1RevertBatchEventV0Signature: ScrollChainABI.Events[revertBatchV0EventName].ID, + l1RevertBatchEventV7Signature: ScrollChainABI.Events[revertBatchV7EventName].ID, l1FinalizeBatchEventSignature: ScrollChainABI.Events[finalizeBatchEventName].ID, } @@ -172,10 +175,11 @@ func (r *Reader) FetchRollupEventsInRange(from, to uint64) (RollupEvents, error) }, Topics: make([][]common.Hash, 1), } - query.Topics[0] = make([]common.Hash, 3) + query.Topics[0] = make([]common.Hash, 4) query.Topics[0][0] = r.l1CommitBatchEventSignature - query.Topics[0][1] = r.l1RevertBatchEventSignature - query.Topics[0][2] = r.l1FinalizeBatchEventSignature + query.Topics[0][1] = r.l1RevertBatchEventV0Signature + query.Topics[0][2] = r.l1RevertBatchEventV7Signature + query.Topics[0][3] = r.l1FinalizeBatchEventSignature logsBatch, err := r.client.FilterLogs(r.ctx, query) if err != nil { @@ -203,10 +207,11 @@ func (r *Reader) FetchRollupEventsInRangeWithCallback(from, to uint64, callback }, Topics: make([][]common.Hash, 1), } - query.Topics[0] = make([]common.Hash, 3) + query.Topics[0] = make([]common.Hash, 4) query.Topics[0][0] = r.l1CommitBatchEventSignature - query.Topics[0][1] = r.l1RevertBatchEventSignature - query.Topics[0][2] = r.l1FinalizeBatchEventSignature + query.Topics[0][1] = r.l1RevertBatchEventV0Signature + query.Topics[0][2] = r.l1RevertBatchEventV7Signature + query.Topics[0][3] = r.l1FinalizeBatchEventSignature logsBatch, err := r.client.FilterLogs(r.ctx, query) if err != nil { @@ -245,7 +250,7 @@ func (r *Reader) processLogsToRollupEvents(logs []types.Log) (RollupEvents, erro if err = UnpackLog(r.scrollChainABI, event, commitBatchEventName, vLog); err != nil { return nil, fmt.Errorf("failed to unpack commit rollup event log, err: %w", err) } - log.Trace("found new CommitBatch event", "batch index", event.BatchIndex.Uint64()) + log.Trace("found new CommitBatch event", "batch index", event.BatchIndex.Uint64(), "batch hash", event.BatchHash.Hex()) rollupEvent = &CommitBatchEvent{ batchIndex: event.BatchIndex, batchHash: event.BatchHash, @@ -254,26 +259,39 @@ func (r *Reader) processLogsToRollupEvents(logs []types.Log) (RollupEvents, erro blockNumber: vLog.BlockNumber, } - case r.l1RevertBatchEventSignature: - event := &RevertBatchEventUnpacked{} - if err = UnpackLog(r.scrollChainABI, event, revertBatchEventName, vLog); err != nil { - return nil, fmt.Errorf("failed to unpack revert rollup event log, err: %w", err) + case r.l1RevertBatchEventV0Signature: + event := &RevertBatchEventV0Unpacked{} + if err = UnpackLog(r.scrollChainABI, event, revertBatchV0EventName, vLog); err != nil { + return nil, fmt.Errorf("failed to unpack revert V0 rollup event log, err: %w", err) } - log.Trace("found new RevertBatchType event", "batch index", event.BatchIndex.Uint64()) - rollupEvent = &RevertBatchEvent{ + log.Trace("found new RevertBatchV0Type event", "batch index", event.BatchIndex.Uint64(), "batch hash", event.BatchHash.Hex()) + rollupEvent = &RevertBatchEventV0{ batchIndex: event.BatchIndex, batchHash: event.BatchHash, txHash: vLog.TxHash, blockHash: vLog.BlockHash, blockNumber: vLog.BlockNumber, } + case r.l1RevertBatchEventV7Signature: + event := &RevertBatchEventV7Unpacked{} + if err = UnpackLog(r.scrollChainABI, event, revertBatchV7EventName, vLog); err != nil { + return nil, fmt.Errorf("failed to unpack revert V7 rollup event log, err: %w", err) + } + log.Trace("found new RevertBatchV7Type event", "start batch index", event.StartBatchIndex.Uint64(), "finish batch index", event.FinishBatchIndex.Uint64()) + rollupEvent = &RevertBatchEventV7{ + startBatchIndex: event.StartBatchIndex, + finishBatchIndex: event.FinishBatchIndex, + txHash: vLog.TxHash, + blockHash: vLog.BlockHash, + blockNumber: vLog.BlockNumber, + } case r.l1FinalizeBatchEventSignature: event := &FinalizeBatchEventUnpacked{} if err = UnpackLog(r.scrollChainABI, event, finalizeBatchEventName, vLog); err != nil { return nil, fmt.Errorf("failed to unpack finalized rollup event log, err: %w", err) } - log.Trace("found new FinalizeBatchType event", "batch index", event.BatchIndex.Uint64()) + log.Trace("found new FinalizeBatchType event", "batch index", event.BatchIndex.Uint64(), "batch hash", event.BatchHash.Hex()) rollupEvent = &FinalizeBatchEvent{ batchIndex: event.BatchIndex, batchHash: event.BatchHash, @@ -375,6 +393,11 @@ func (r *Reader) FetchCommitTxData(commitEvent *CommitBatchEvent) (*CommitBatchA if err != nil { return nil, fmt.Errorf("failed to decode calldata into commitBatch args %s, values: %+v, err: %w", commitBatchWithBlobProofMethodName, values, err) } + } else if method.Name == commitBatchesV7MethodName { + args, err = newCommitBatchArgsFromCommitBatchesV7(method, values) + if err != nil { + return nil, fmt.Errorf("failed to decode calldata into commitBatch args %s, values: %+v, err: %w", commitBatchesV7MethodName, values, err) + } } else { return nil, fmt.Errorf("unknown method name for commit transaction: %s", method.Name) } @@ -383,3 +406,36 @@ func (r *Reader) FetchCommitTxData(commitEvent *CommitBatchEvent) (*CommitBatchA return args, nil } + +func (r *Reader) FetchFinalizeTxDataPostEuclidV2(event *FinalizeBatchEvent) (*FinalizeBatchArgs, error) { + tx, err := r.fetchTx(event.TxHash(), event.BlockHash()) + if err != nil { + return nil, err + } + txData := tx.Data() + + if len(txData) < methodIDLength { + return nil, fmt.Errorf("transaction data is too short, length of tx data: %v, minimum length required: %v", len(txData), methodIDLength) + } + + method, err := r.scrollChainABI.MethodById(txData[:methodIDLength]) + if err != nil { + return 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 nil, fmt.Errorf("failed to unpack transaction data using ABI, tx data: %v, err: %w", txData, err) + } + + var args *FinalizeBatchArgs + if method.Name == finalizeBundlePostEuclidV2MethodName { + args, err = newFinalizeBatchArgs(method, values) + if err != nil { + return nil, fmt.Errorf("failed to decode calldata into finalizeBatch args %s, values: %+v, err: %w", finalizeBundlePostEuclidV2MethodName, values, err) + } + } else { + return nil, fmt.Errorf("unknown method name for finalize transaction: %s", method.Name) + } + + return args, nil +} diff --git a/rollup/rollup_sync_service/rollup_sync_service.go b/rollup/rollup_sync_service/rollup_sync_service.go index d498288079..c0d79d8339 100644 --- a/rollup/rollup_sync_service/rollup_sync_service.go +++ b/rollup/rollup_sync_service/rollup_sync_service.go @@ -238,7 +238,9 @@ func (s *RollupSyncService) updateRollupEvents(daEntries da.Entries) error { case da.RevertBatchType: log.Trace("found new RevertBatch event", "batch index", entry.BatchIndex()) - rawdb.DeleteCommittedBatchMeta(s.db, entry.BatchIndex()) + if err := s.handleRevertEvent(entry.Event()); err != nil { + return fmt.Errorf("failed to handle revert event, batch index: %v, err: %w", entry.BatchIndex(), err) + } case da.FinalizeBatchType: event, ok := entry.Event().(*l1.FinalizeBatchEvent) @@ -321,6 +323,33 @@ func (s *RollupSyncService) updateRollupEvents(daEntries da.Entries) error { return nil } +func (s *RollupSyncService) handleRevertEvent(event l1.RollupEvent) error { + switch event.Type() { + case l1.RevertEventV0Type: + revertBatch, ok := event.(*l1.RevertBatchEventV0) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV0Type", event) + } + + rawdb.DeleteCommittedBatchMeta(s.db, revertBatch.BatchIndex().Uint64()) + + case l1.RevertEventV7Type: + revertBatch, ok := event.(*l1.RevertBatchEventV7) + if !ok { + return fmt.Errorf("unexpected type of revert event: %T, expected RevertEventV7Type", event) + } + + // delete all batches from revertBatch.StartBatchIndex (inclusive) to revertBatch.FinishBatchIndex (inclusive) + for i := revertBatch.StartBatchIndex().Uint64(); i <= revertBatch.FinishBatchIndex().Uint64(); i++ { + rawdb.DeleteCommittedBatchMeta(s.db, i) + } + default: + return fmt.Errorf("unexpected type of revert event: %T", event) + } + + return nil +} + func (s *RollupSyncService) getLocalChunksForBatch(chunkBlockRanges []*rawdb.ChunkBlockRange) ([]*encoding.Chunk, error) { if len(chunkBlockRanges) == 0 { return nil, fmt.Errorf("chunkBlockRanges is empty") @@ -377,7 +406,7 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc return &rawdb.CommittedBatchMeta{ Version: 0, ChunkBlockRanges: []*rawdb.ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 0}}, - LastL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, }, nil } @@ -386,8 +415,8 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc return nil, fmt.Errorf("failed to decode block ranges from chunks, batch index: %v, err: %w", commitedBatch.BatchIndex(), err) } - // With CodecV7 the batch creation changed. We need to compute and store LastL1MessageQueueHash. - // InitialL1MessageQueueHash of a batch == LastL1MessageQueueHash of the previous batch. + // With CodecV7 the batch creation changed. We need to compute and store PostL1MessageQueueHash. + // PrevL1MessageQueueHash of a batch == PostL1MessageQueueHash of the previous batch. // We need to do this for every committed batch (instead of finalized batch) because the L1MessageQueueHash // is a continuous hash of all L1 messages over all batches. With bundles we only receive the finalize event // for the last batch of the bundle. @@ -399,12 +428,12 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc } // If parent batch has a lower version this means this is the first batch of CodecV7. - // In this case we need to compute the InitialL1MessageQueueHash from the empty hash. - var initialL1MessageQueueHash common.Hash + // In this case we need to compute the prevL1MessageQueueHash from the empty hash. + var prevL1MessageQueueHash common.Hash if encoding.CodecVersion(parentCommittedBatchMeta.Version) < commitedBatch.Version() { - initialL1MessageQueueHash = common.Hash{} + prevL1MessageQueueHash = common.Hash{} } else { - initialL1MessageQueueHash = parentCommittedBatchMeta.LastL1MessageQueueHash + prevL1MessageQueueHash = parentCommittedBatchMeta.PostL1MessageQueueHash } chunks, err := s.getLocalChunksForBatch(chunkRanges) @@ -419,7 +448,7 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc return nil, fmt.Errorf("invalid argument: chunk count is not 1 for CodecV7, batch index: %v", commitedBatch.BatchIndex()) } - lastL1MessageQueueHash, err = encoding.MessageQueueV2ApplyL1MessagesFromBlocks(initialL1MessageQueueHash, chunks[0].Blocks) + lastL1MessageQueueHash, err = encoding.MessageQueueV2ApplyL1MessagesFromBlocks(prevL1MessageQueueHash, chunks[0].Blocks) if err != nil { return nil, fmt.Errorf("failed to apply L1 messages from blocks, batch index: %v, err: %w", chunks[0], err) } @@ -428,7 +457,7 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc return &rawdb.CommittedBatchMeta{ Version: uint8(commitedBatch.Version()), ChunkBlockRanges: chunkRanges, - LastL1MessageQueueHash: lastL1MessageQueueHash, + PostL1MessageQueueHash: lastL1MessageQueueHash, }, nil } @@ -490,12 +519,11 @@ func validateBatch(batchIndex uint64, event *l1.FinalizeBatchEvent, parentFinali } batch = &encoding.Batch{ - Index: batchIndex, - ParentBatchHash: parentFinalizedBatchMeta.BatchHash, - InitialL1MessageIndex: parentFinalizedBatchMeta.TotalL1MessagePopped, - Blocks: startChunk.Blocks, - InitialL1MessageQueueHash: parentCommittedBatchMeta.LastL1MessageQueueHash, - LastL1MessageQueueHash: committedBatchMeta.LastL1MessageQueueHash, + Index: batchIndex, + ParentBatchHash: parentFinalizedBatchMeta.BatchHash, + Blocks: startChunk.Blocks, + PrevL1MessageQueueHash: parentCommittedBatchMeta.PostL1MessageQueueHash, + PostL1MessageQueueHash: committedBatchMeta.PostL1MessageQueueHash, } } diff --git a/rollup/rollup_sync_service/rollup_sync_service_test.go b/rollup/rollup_sync_service/rollup_sync_service_test.go index 027f7134c6..e6f6d73396 100644 --- a/rollup/rollup_sync_service/rollup_sync_service_test.go +++ b/rollup/rollup_sync_service/rollup_sync_service_test.go @@ -161,6 +161,10 @@ type mockEntryWithBlocks struct { versionedHashes []common.Hash } +func (m mockEntryWithBlocks) L1MessagesPoppedInBatch() uint64 { + panic("implement me") +} + func (m mockEntryWithBlocks) Type() da.Type { panic("implement me") } @@ -181,7 +185,7 @@ func (m mockEntryWithBlocks) Event() l1.RollupEvent { panic("implement me") } -func (m mockEntryWithBlocks) Blocks() []*da.PartialBlock { +func (m mockEntryWithBlocks) Blocks() ([]*da.PartialBlock, error) { panic("implement me") } @@ -193,6 +197,14 @@ func (m mockEntryWithBlocks) Chunks() []*encoding.DAChunkRawTx { return m.chunks } +func (m mockEntryWithBlocks) SetParentTotalL1MessagePopped(totalL1MessagePopped uint64) { + panic("implement me") +} + +func (m mockEntryWithBlocks) TotalL1MessagesPopped() uint64 { + panic("implement me") +} + func (m mockEntryWithBlocks) BlobVersionedHashes() []common.Hash { return m.versionedHashes } @@ -667,11 +679,10 @@ func TestValidateBatchCodecV7(t *testing.T) { { block1 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_02.json"), 1) batch1 := &encoding.Batch{ - Index: 1, - InitialL1MessageIndex: 0, - InitialL1MessageQueueHash: common.Hash{}, - LastL1MessageQueueHash: common.Hash{}, - Blocks: []*encoding.Block{block1}, + Index: 1, + PrevL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, + Blocks: []*encoding.Block{block1}, } batch1LastBlock := batch1.Blocks[len(batch1.Blocks)-1] @@ -690,7 +701,7 @@ func TestValidateBatchCodecV7(t *testing.T) { committedBatchMeta1 = &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV7), - LastL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, } var endBlock1 uint64 @@ -708,12 +719,11 @@ func TestValidateBatchCodecV7(t *testing.T) { // finalize 3 batches with CodecV7 at once block2 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_03.json"), 2) batch2 := &encoding.Batch{ - Index: 2, - ParentBatchHash: finalizedBatchMeta1.BatchHash, - InitialL1MessageIndex: 0, - InitialL1MessageQueueHash: common.Hash{}, - LastL1MessageQueueHash: common.Hash{}, - Blocks: []*encoding.Block{block2}, + Index: 2, + ParentBatchHash: finalizedBatchMeta1.BatchHash, + PrevL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, + Blocks: []*encoding.Block{block2}, } batch2LastBlock := batch2.Blocks[len(batch2.Blocks)-1] @@ -724,12 +734,11 @@ func TestValidateBatchCodecV7(t *testing.T) { LastL1MessageQueueHashBatch3, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(common.Hash{}, []*encoding.Block{block3}) require.NoError(t, err) batch3 := &encoding.Batch{ - Index: 3, - ParentBatchHash: daBatch2.Hash(), - InitialL1MessageIndex: 0, - InitialL1MessageQueueHash: common.Hash{}, - LastL1MessageQueueHash: LastL1MessageQueueHashBatch3, - Blocks: []*encoding.Block{block3}, + Index: 3, + ParentBatchHash: daBatch2.Hash(), + PrevL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: LastL1MessageQueueHashBatch3, + Blocks: []*encoding.Block{block3}, } batch3LastBlock := batch3.Blocks[len(batch3.Blocks)-1] @@ -740,12 +749,11 @@ func TestValidateBatchCodecV7(t *testing.T) { LastL1MessageQueueHashBatch4, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(LastL1MessageQueueHashBatch3, []*encoding.Block{block4}) require.NoError(t, err) batch4 := &encoding.Batch{ - Index: 4, - ParentBatchHash: daBatch3.Hash(), - InitialL1MessageIndex: 1, - InitialL1MessageQueueHash: LastL1MessageQueueHashBatch3, - LastL1MessageQueueHash: LastL1MessageQueueHashBatch4, - Blocks: []*encoding.Block{block4}, + Index: 4, + ParentBatchHash: daBatch3.Hash(), + PrevL1MessageQueueHash: LastL1MessageQueueHashBatch3, + PostL1MessageQueueHash: LastL1MessageQueueHashBatch4, + Blocks: []*encoding.Block{block4}, } batch4LastBlock := batch4.Blocks[len(batch4.Blocks)-1] @@ -764,17 +772,17 @@ func TestValidateBatchCodecV7(t *testing.T) { committedBatchMeta2 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV7), - LastL1MessageQueueHash: common.Hash{}, + PostL1MessageQueueHash: common.Hash{}, } committedBatchMeta3 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV7), - LastL1MessageQueueHash: LastL1MessageQueueHashBatch3, + PostL1MessageQueueHash: LastL1MessageQueueHashBatch3, } committedBatchMeta4 := &rawdb.CommittedBatchMeta{ Version: uint8(encoding.CodecV7), - LastL1MessageQueueHash: LastL1MessageQueueHashBatch4, + PostL1MessageQueueHash: LastL1MessageQueueHashBatch4, } endBlock2, finalizedBatchMeta2, err := validateBatch(2, event2, finalizedBatchMeta1, committedBatchMeta1, committedBatchMeta2, []*encoding.Chunk{{Blocks: batch2.Blocks}}, nil)