feat(L1 follower): adjust to recent CodecV7 and contract changes (#1120)

* port changes from #1013

* port changes from #1068

* go.mod tidy

* fix compile error

* fix goimports

* fix log

* address review comments

* upgrade golang.org/x/net to 0.23.0

* port changes from #1018

* fix tests and linter errors

* address review comments

* refactor rollup sync service / verifier to use CalldataBlobSource to retrieve data from L1

* add configuration and initialize blob clients

* fix unit tests

* remove unused code

* address review comments

* address more review comments

* implement first version of new da-codec and to handle multiple batches submitted in one transaction

* add CommitBatchDAV7 and handle multiple commit events submitted in a single transactions

* fix bug due to previous batch being empty when processing the first batch within a set of batches

* Allow using MPT

* update to latest da-codec

* add field to CommittedBatchMeta to store LastL1MessageQueueHash for CodecV7 batches

* adjust rollup verifier to support CodecV7 batches

* address review comments

* fix issues after merge

* go mod tidy

* fix unit tests

* update da-codec

* add test TestValidateBatchCodecV7

* go mod tidy

* do not log error on shutdown

* add sanity check for version to deserialization of committedBatchMetaV7

* port changes from #1073

* chore: auto version bump [bot]

* address review comments

* add more logs

* disable ENRUpdater if DA sync mode is enabled

* exit pipeline if context is cancelled

* correctly handle override by setting the head of the chain to the parent's height so that created blocks will always become part of canonical chain

* fix error with genesis event being nil

* chore: auto version bump [bot]

* chore: auto version bump [bot]

* adjust to renaming in CodecV7

* implement carrying forward of L1 MessageQueue index

* fix issue after upgrading from old storage to new format where batchIndex was 0 and all batches would be skipped

* add new RevertBatch event

* add commitBatches to be able to read calldata of CodecV7/EuclidV2 committed batches

* implement finding of L1 message queue height for initial batch in recovery mode

* add sanity checks for computed batches from events and batch hashes given via calldata from commit transaction

* update ScrollChain ABI

* chore: auto version bump [bot]

* remove initial batch form DAQueue

* go mod tidy

* fix underflow bug when l1DeploymentBlock==0

* fix bug with wrong parentBatchHash of first batch of batches submitted in a single tx

* update to latest da-codec

* address review comments

* address review comments

* fix bug when l1MessageV2StartIndex==0 serialized to [] (empty slice) which would always be decoded to non-existing instead of 0

* chore: auto version bump [bot]

* cache go dependencies in Dockerfile.mockccc

* chore: auto version bump [bot]

* add INFO log when reverting rollup events in L1 follower mode

* change LastProcessedMessageQueueIndex of finalize event to  totalL1MessagesPoppedOverall

* cleanup

* chore: auto version bump [bot]

---------

Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com>
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
Co-authored-by: jonastheis <jonastheis@users.noreply.github.com>
This commit is contained in:
Jonas Theis 2025-03-03 20:14:32 +08:00 committed by GitHub
parent 87e196052a
commit 40ebbd6491
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 756 additions and 231 deletions

View file

@ -3,3 +3,5 @@
build/_workspace build/_workspace
build/_bin build/_bin
tests/testdata tests/testdata
tmp/

View file

@ -7,14 +7,18 @@ ARG SCROLL_LIB_PATH=/scroll/lib
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM scrolltech/go-rust-builder:go-1.21-rust-nightly-2023-12-03 as builder FROM scrolltech/go-rust-builder:go-1.21-rust-nightly-2023-12-03 as builder
ADD . /go-ethereum WORKDIR /go-ethereum
RUN cd /go-ethereum && env GO111MODULE=on go run build/ci.go install ./cmd/geth 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 # Pull Geth into a second stage deploy alpine container
FROM ubuntu:20.04 FROM ubuntu:20.04
RUN apt-get -qq update \ #RUN apt-get -qq update \
&& apt-get -qq install -y --no-install-recommends ca-certificates # && apt-get -qq install -y --no-install-recommends ca-certificates
ENV CGO_LDFLAGS="-ldl" ENV CGO_LDFLAGS="-ldl"

View file

@ -1,23 +1,33 @@
package rawdb package rawdb
import ( import (
"bytes"
"math/big" "math/big"
"github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/log" "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. type DAProcessedBatchMeta struct {
func WriteDASyncedL1BlockNumber(db ethdb.KeyValueWriter, L1BlockNumber uint64) { BatchIndex uint64
value := big.NewInt(0).SetUint64(L1BlockNumber).Bytes() 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 { 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. // ReadDAProcessedBatchMeta retrieves the batch metadata of the latest processed DA batch.
func ReadDASyncedL1BlockNumber(db ethdb.Reader) *uint64 { func ReadDAProcessedBatchMeta(db ethdb.Reader) *DAProcessedBatchMeta {
data, err := db.Get(daSyncedL1BlockNumberKey) data, err := db.Get(daSyncedL1BlockNumberKey)
if err != nil && isNotFoundErr(err) { if err != nil && isNotFoundErr(err) {
return nil return nil
@ -29,11 +39,25 @@ func ReadDASyncedL1BlockNumber(db ethdb.Reader) *uint64 {
return nil return nil
} }
number := new(big.Int).SetBytes(data) // Try decoding from the newest format for future proofness, then the older one for old data.
if !number.IsUint64() { daProcessedBatchMeta := new(DAProcessedBatchMeta)
log.Crit("Unexpected DA synced L1 block number in database", "number", number) if err = rlp.Decode(bytes.NewReader(data), daProcessedBatchMeta); err == nil {
return daProcessedBatchMeta
} }
value := number.Uint64() // Before storing DAProcessedBatchMeta we used to store a single uint64 value for the L1 block number.
return &value 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,
}
} }

View file

@ -383,9 +383,13 @@ func ReadFirstQueueIndexNotInL2Block(db ethdb.Reader, l2BlockHash common.Hash) *
// WriteL1MessageV2StartIndex writes the start index of L1 messages that are from L1MessageQueueV2. // WriteL1MessageV2StartIndex writes the start index of L1 messages that are from L1MessageQueueV2.
func WriteL1MessageV2StartIndex(db ethdb.KeyValueWriter, queueIndex uint64) { 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) log.Crit("Failed to update L1MessageV2 start index", "err", err)
} }
} }
@ -402,13 +406,11 @@ func ReadL1MessageV2StartIndex(db ethdb.Reader) *uint64 {
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
if len(data) != 8 {
number := new(big.Int).SetBytes(data) return nil
if !number.IsUint64() {
log.Crit("Unexpected number for L1MessageV2 start index", "number", number)
} }
res := binary.BigEndian.Uint64(data)
res := number.Uint64()
return &res return &res
} }

View file

@ -25,7 +25,7 @@ type CommittedBatchMeta struct {
ChunkBlockRanges []*ChunkBlockRange ChunkBlockRanges []*ChunkBlockRange
// introduced with CodecV7 // introduced with CodecV7
LastL1MessageQueueHash common.Hash PostL1MessageQueueHash common.Hash
} }
type committedBatchMetaV0 struct { type committedBatchMetaV0 struct {
@ -170,7 +170,7 @@ func WriteCommittedBatchMeta(db ethdb.KeyValueWriter, batchIndex uint64, committ
committedBatchMetaToStore = &committedBatchMetaV7{ committedBatchMetaToStore = &committedBatchMetaV7{
Version: committedBatchMeta.Version, Version: committedBatchMeta.Version,
ChunkBlockRanges: committedBatchMeta.ChunkBlockRanges, ChunkBlockRanges: committedBatchMeta.ChunkBlockRanges,
LastL1MessageQueueHash: committedBatchMeta.LastL1MessageQueueHash, LastL1MessageQueueHash: committedBatchMeta.PostL1MessageQueueHash,
} }
} }
@ -202,7 +202,7 @@ func ReadCommittedBatchMeta(db ethdb.Reader, batchIndex uint64) (*CommittedBatch
return &CommittedBatchMeta{ return &CommittedBatchMeta{
Version: cbm7.Version, Version: cbm7.Version,
ChunkBlockRanges: cbm7.ChunkBlockRanges, ChunkBlockRanges: cbm7.ChunkBlockRanges,
LastL1MessageQueueHash: cbm7.LastL1MessageQueueHash, PostL1MessageQueueHash: cbm7.LastL1MessageQueueHash,
}, nil }, nil
} }
@ -214,7 +214,7 @@ func ReadCommittedBatchMeta(db ethdb.Reader, batchIndex uint64) (*CommittedBatch
return &CommittedBatchMeta{ return &CommittedBatchMeta{
Version: cbm0.Version, Version: cbm0.Version,
ChunkBlockRanges: cbm0.ChunkBlockRanges, ChunkBlockRanges: cbm0.ChunkBlockRanges,
LastL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
}, nil }, nil
} }

View file

@ -182,7 +182,7 @@ func TestWriteReadDeleteCommittedBatchMeta(t *testing.T) {
meta: &CommittedBatchMeta{ meta: &CommittedBatchMeta{
Version: 7, Version: 7,
ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 10}}, 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{ meta: &CommittedBatchMeta{
Version: 255, Version: 255,
ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 10}, {StartBlockNumber: 11, EndBlockNumber: 20}}, 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{ newMeta := &CommittedBatchMeta{
Version: 255, Version: 255,
ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 20}, {StartBlockNumber: 21, EndBlockNumber: 30}}, ChunkBlockRanges: []*ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 20}, {StartBlockNumber: 21, EndBlockNumber: 30}},
LastL1MessageQueueHash: common.Hash{255}, PostL1MessageQueueHash: common.Hash{255},
} }
// write initial meta // write initial meta
@ -282,5 +282,5 @@ func compareCommittedBatchMeta(a, b *CommittedBatchMeta) bool {
} }
} }
return a.LastL1MessageQueueHash == b.LastL1MessageQueueHash return a.PostL1MessageQueueHash == b.PostL1MessageQueueHash
} }

2
go.mod
View file

@ -51,7 +51,7 @@ require (
github.com/prometheus/tsdb v0.7.1 github.com/prometheus/tsdb v0.7.1
github.com/rjeczalik/notify v0.9.1 github.com/rjeczalik/notify v0.9.1
github.com/rs/cors v1.7.0 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/scroll-tech/zktrie v0.8.4
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sourcegraph/conc v0.3.0 github.com/sourcegraph/conc v0.3.0

4
go.sum
View file

@ -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 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 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/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.20250226072559-f8a8d3898f54 h1:qVpsVu1J91opTn6HYeuzWcBRVhQmPR8g05i+PlOjlI4=
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/go.mod h1:xECEHZLVzbdUn+tNbRJhRIjLGTOTmnFQuTgUTeVLX58=
github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= 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/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= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=

View file

@ -24,7 +24,7 @@ import (
const ( const (
VersionMajor = 5 // Major version component of the current release VersionMajor = 5 // Major version component of the current release
VersionMinor = 8 // Minor 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 VersionMeta = "mainnet" // Version metadata to append to the version string
) )

View file

@ -7,7 +7,9 @@ import (
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/ethdb" "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/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. // 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 lastFinalizedBatchIndex uint64
batches *common.Heap[da.Entry] batches *common.Heap[da.Entry]
batchesMap *common.ShrinkingMap[uint64, *common.HeapElement[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{ return &BatchQueue{
DAQueue: DAQueue, DAQueue: DAQueue,
db: db, db: db,
lastFinalizedBatchIndex: 0, lastFinalizedBatchIndex: lastProcessedBatch.BatchIndex,
batches: common.NewHeap[da.Entry](), batches: common.NewHeap[da.Entry](),
batchesMap: common.NewShrinkingMap[uint64, *common.HeapElement[da.Entry]](1000), 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 // NextBatch finds next finalized batch and returns data, that was committed in that batch
func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) { func (bq *BatchQueue) NextBatch(ctx context.Context) (da.EntryWithBlocks, error) {
if batch := bq.getFinalizedBatch(); batch != nil { if batch := bq.nextFinalizedBatch(); batch != nil {
return batch, nil return batch, nil
} }
@ -50,13 +55,15 @@ func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) {
case da.CommitBatchV0Type, da.CommitBatchWithBlobType: case da.CommitBatchV0Type, da.CommitBatchWithBlobType:
bq.addBatch(daEntry) bq.addBatch(daEntry)
case da.RevertBatchType: 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: case da.FinalizeBatchType:
if daEntry.BatchIndex() > bq.lastFinalizedBatchIndex { if daEntry.BatchIndex() > bq.lastFinalizedBatchIndex {
bq.lastFinalizedBatchIndex = daEntry.BatchIndex() bq.lastFinalizedBatchIndex = daEntry.BatchIndex()
} }
if batch := bq.getFinalizedBatch(); batch != nil { if batch := bq.nextFinalizedBatch(); batch != nil {
return batch, nil return batch, nil
} }
default: default:
@ -65,16 +72,17 @@ func (bq *BatchQueue) NextBatch(ctx context.Context) (da.Entry, error) {
} }
} }
// getFinalizedBatch returns next finalized batch if there is available // nextFinalizedBatch returns next finalized batch if there is available
func (bq *BatchQueue) getFinalizedBatch() da.Entry { func (bq *BatchQueue) nextFinalizedBatch() da.EntryWithBlocks {
if bq.batches.Len() == 0 { if bq.batches.Len() == 0 {
return nil return nil
} }
batch := bq.batches.Peek().Value() 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 { if batch.BatchIndex() <= bq.lastFinalizedBatchIndex {
bq.deleteBatch(batch) return bq.processAndDeleteBatch(batch)
return batch
} else { } else {
return nil return nil
} }
@ -85,25 +93,87 @@ func (bq *BatchQueue) addBatch(batch da.Entry) {
bq.batchesMap.Set(batch.BatchIndex(), heapElement) bq.batchesMap.Set(batch.BatchIndex(), heapElement)
} }
// deleteBatch deletes data committed in the batch from map, because this batch is reverted or finalized func (bq *BatchQueue) handleRevertEvent(event l1.RollupEvent) error {
// updates DASyncedL1BlockNumber switch event.Type() {
func (bq *BatchQueue) deleteBatch(batch da.Entry) { case l1.RevertEventV0Type:
batchHeapElement, exists := bq.batchesMap.Get(batch.BatchIndex()) revertBatch, ok := event.(*l1.RevertBatchEventV0)
if !exists { if !ok {
return 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) 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 return true
// TODO: we should store this information when the batch is done being processed to avoid inconsistencies
rawdb.WriteDASyncedL1BlockNumber(bq.db, batch.L1BlockNumber()-1)
} }
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.batches.Clear()
bq.batchesMap.Clear() bq.batchesMap.Clear()
bq.lastFinalizedBatchIndex = 0 bq.lastFinalizedBatchIndex = lastProcessedBatchMeta.BatchIndex
bq.DAQueue.Reset(height) bq.previousBatch = lastProcessedBatchMeta
bq.DAQueue.Reset(lastProcessedBatchMeta)
} }

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"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/da"
) )
@ -34,23 +35,20 @@ func (bq *BlockQueue) NextBlock(ctx context.Context) (*da.PartialBlock, error) {
} }
func (bq *BlockQueue) getBlocksFromBatch(ctx context.Context) error { func (bq *BlockQueue) getBlocksFromBatch(ctx context.Context) error {
daEntry, err := bq.batchQueue.NextBatch(ctx) entryWithBlocks, err := bq.batchQueue.NextBatch(ctx)
if err != nil { if err != nil {
return err return err
} }
entryWithBlocks, ok := daEntry.(da.EntryWithBlocks) bq.blocks, err = entryWithBlocks.Blocks()
// this should never happen because we only receive CommitBatch entries if err != nil {
if !ok { return fmt.Errorf("failed to get blocks from entry: %w", err)
return fmt.Errorf("unexpected type of daEntry: %T", daEntry)
} }
bq.blocks = entryWithBlocks.Blocks()
return nil return nil
} }
func (bq *BlockQueue) Reset(height uint64) { func (bq *BlockQueue) Reset(lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta) {
bq.blocks = make([]*da.PartialBlock, 0) bq.blocks = make([]*da.PartialBlock, 0)
bq.batchQueue.Reset(height) bq.batchQueue.Reset(lastProcessedBatchMeta)
} }

View file

@ -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 // add commit event to the list of previous commit events, so we can process events created in the same tx together
lastCommitTransactionHash = commitEvent.TxHash() lastCommitTransactionHash = commitEvent.TxHash()
lastCommitEvents = append(lastCommitEvents, commitEvent) 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 we have any previous commit events, we need to create a new DA before processing the revert event
if len(lastCommitEvents) > 0 { if len(lastCommitEvents) > 0 {
if err = getAndAppendCommitBatchDA(); err != nil { if err = getAndAppendCommitBatchDA(); err != nil {
@ -152,13 +152,7 @@ func (ds *CalldataBlobSource) processRollupEventsToDA(rollupEvents l1.RollupEven
} }
} }
revertEvent, ok := rollupEvent.(*l1.RevertBatchEvent) entry = NewRevertBatch(rollupEvent)
// 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)
entries = append(entries, entry) entries = append(entries, entry)
case l1.FinalizeEventType: case l1.FinalizeEventType:
// if we have any previous commit events, we need to create a new DA before processing the finalized event // 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() { switch codec.Version() {
case 0: case encoding.CodecV0:
if entry, err = NewCommitBatchDAV0(ds.db, codec, commitEvent, args.ParentBatchHeader, args.Chunks, args.SkippedL1MessageBitmap); err != nil { 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) 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 { 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) 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) { 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()) 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 var parentBatchHash common.Hash
if previousEvent == nil { if previousEvent == nil {
parentBatchHash = common.BytesToHash(args.ParentBatchHeader) parentBatchHash = args.ParentBatchHash
} else { } else {
parentBatchHash = previousEvent.BatchHash() parentBatchHash = previousEvent.BatchHash()
} }
@ -265,5 +259,14 @@ func (ds *CalldataBlobSource) getCommitBatchDA(commitEvents []*l1.CommitBatchEve
entries = append(entries, entry) 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 return entries, nil
} }

View file

@ -16,12 +16,14 @@ import (
) )
type CommitBatchDAV0 struct { type CommitBatchDAV0 struct {
db ethdb.Database
version encoding.CodecVersion version encoding.CodecVersion
batchIndex uint64 batchIndex uint64
parentTotalL1MessagePopped uint64 parentTotalL1MessagePopped uint64
l1MessagesPopped int
skippedL1MessageBitmap []byte skippedL1MessageBitmap []byte
chunks []*encoding.DAChunkRawTx chunks []*encoding.DAChunkRawTx
l1Txs []*types.L1MessageTx
event *l1.CommitBatchEvent event *l1.CommitBatchEvent
} }
@ -50,18 +52,15 @@ func NewCommitBatchDAV0WithChunks(db ethdb.Database,
event *l1.CommitBatchEvent, event *l1.CommitBatchEvent,
) (*CommitBatchDAV0, error) { ) (*CommitBatchDAV0, error) {
parentTotalL1MessagePopped := getBatchTotalL1MessagePopped(parentBatchHeader) 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{ return &CommitBatchDAV0{
db: db,
version: version, version: version,
batchIndex: batchIndex, batchIndex: batchIndex,
parentTotalL1MessagePopped: parentTotalL1MessagePopped, parentTotalL1MessagePopped: parentTotalL1MessagePopped,
l1MessagesPopped: getTotalMessagesPoppedFromChunks(decodedChunks),
skippedL1MessageBitmap: skippedL1MessageBitmap, skippedL1MessageBitmap: skippedL1MessageBitmap,
chunks: decodedChunks, chunks: decodedChunks,
l1Txs: l1Txs,
event: event, event: event,
}, nil }, nil
} }
@ -110,7 +109,12 @@ func (c *CommitBatchDAV0) CompareTo(other Entry) int {
return 0 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 var blocks []*PartialBlock
l1TxPointer := 0 l1TxPointer := 0
@ -120,8 +124,8 @@ func (c *CommitBatchDAV0) Blocks() []*PartialBlock {
// create txs // create txs
txs := make(types.Transactions, 0, daBlock.NumTransactions()) txs := make(types.Transactions, 0, daBlock.NumTransactions())
// insert l1 msgs // insert l1 msgs
for l1TxPointer < len(c.l1Txs) && c.l1Txs[l1TxPointer].QueueIndex < curL1TxIndex+uint64(daBlock.NumL1Messages()) { for l1TxPointer < len(l1Txs) && l1Txs[l1TxPointer].QueueIndex < curL1TxIndex+uint64(daBlock.NumL1Messages()) {
l1Tx := types.NewTx(c.l1Txs[l1TxPointer]) l1Tx := types.NewTx(l1Txs[l1TxPointer])
txs = append(txs, l1Tx) txs = append(txs, l1Tx)
l1TxPointer++ 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 { func getTotalMessagesPoppedFromChunks(decodedChunks []*encoding.DAChunkRawTx) int {

View file

@ -20,13 +20,15 @@ import (
) )
type CommitBatchDAV7 struct { type CommitBatchDAV7 struct {
version encoding.CodecVersion db ethdb.Database
batchIndex uint64
initialL1MessageIndex uint64 version encoding.CodecVersion
blocks []encoding.DABlock batchIndex uint64
transactions []types.Transactions versionedHashes []common.Hash
l1Txs []types.Transactions blobPayload encoding.DABlobPayload
versionedHashes []common.Hash
parentTotalL1MessagePopped uint64
l1MessagesPopped uint64
event *l1.CommitBatchEvent 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) 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{ return &CommitBatchDAV7{
version: codec.Version(), db: db,
batchIndex: commitEvent.BatchIndex().Uint64(), version: codec.Version(),
initialL1MessageIndex: blobPayload.InitialL1MessageIndex(), batchIndex: commitEvent.BatchIndex().Uint64(),
blocks: blobPayload.Blocks(), versionedHashes: []common.Hash{blobVersionedHash},
transactions: blobPayload.Transactions(), blobPayload: blobPayload,
l1Txs: l1Txs, l1MessagesPopped: getL1MessagesPoppedFromBlocks(blobPayload.Blocks()),
versionedHashes: []common.Hash{blobVersionedHash}, event: commitEvent,
event: commitEvent,
}, nil }, nil
} }
@ -117,18 +113,26 @@ func (c *CommitBatchDAV7) Event() l1.RollupEvent {
return c.event 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 var blocks []*PartialBlock
for i, daBlock := range c.blocks { for i, daBlock := range c.blobPayload.Blocks() {
// create txs // create txs
txs := make(types.Transactions, 0, daBlock.NumTransactions()) txs := make(types.Transactions, 0, daBlock.NumTransactions())
// insert L1 messages // 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 // insert L2 txs
txs = append(txs, c.transactions[i]...) txs = append(txs, c.blobPayload.Transactions()[i]...)
block := NewPartialBlock( block := NewPartialBlock(
&PartialHeader{ &PartialHeader{
@ -143,7 +147,19 @@ func (c *CommitBatchDAV7) Blocks() []*PartialBlock {
blocks = append(blocks, block) 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 { func (c *CommitBatchDAV7) Version() encoding.CodecVersion {
@ -153,8 +169,8 @@ func (c *CommitBatchDAV7) Version() encoding.CodecVersion {
func (c *CommitBatchDAV7) Chunks() []*encoding.DAChunkRawTx { func (c *CommitBatchDAV7) Chunks() []*encoding.DAChunkRawTx {
return []*encoding.DAChunkRawTx{ return []*encoding.DAChunkRawTx{
{ {
Blocks: c.blocks, Blocks: c.blobPayload.Blocks(),
Transactions: c.transactions, Transactions: c.blobPayload.Transactions(),
}, },
} }
} }
@ -189,3 +205,13 @@ func getL1MessagesV7(db ethdb.Database, blocks []encoding.DABlock, initialL1Mess
return allTxs, nil return allTxs, nil
} }
func getL1MessagesPoppedFromBlocks(blocks []encoding.DABlock) uint64 {
var totalL1MessagePopped uint64
for _, block := range blocks {
totalL1MessagePopped += uint64(block.NumL1Messages())
}
return totalL1MessagePopped
}

View file

@ -34,10 +34,13 @@ type Entry interface {
type EntryWithBlocks interface { type EntryWithBlocks interface {
Entry Entry
Blocks() []*PartialBlock Blocks() ([]*PartialBlock, error)
Version() encoding.CodecVersion Version() encoding.CodecVersion
Chunks() []*encoding.DAChunkRawTx Chunks() []*encoding.DAChunkRawTx
BlobVersionedHashes() []common.Hash BlobVersionedHashes() []common.Hash
SetParentTotalL1MessagePopped(uint64)
TotalL1MessagesPopped() uint64
L1MessagesPoppedInBatch() uint64
} }
type Entries []Entry type Entries []Entry

View file

@ -5,10 +5,10 @@ import (
) )
type RevertBatch struct { type RevertBatch struct {
event *l1.RevertBatchEvent event l1.RollupEvent
} }
func NewRevertBatch(event *l1.RevertBatchEvent) *RevertBatch { func NewRevertBatch(event l1.RollupEvent) *RevertBatch {
return &RevertBatch{ return &RevertBatch{
event: event, event: event,
} }
@ -21,6 +21,7 @@ func (r *RevertBatch) Type() Type {
func (r *RevertBatch) L1BlockNumber() uint64 { func (r *RevertBatch) L1BlockNumber() uint64 {
return r.event.BlockNumber() return r.event.BlockNumber()
} }
func (r *RevertBatch) BatchIndex() uint64 { func (r *RevertBatch) BatchIndex() uint64 {
return r.event.BatchIndex().Uint64() return r.event.BatchIndex().Uint64()
} }

View file

@ -4,25 +4,23 @@ import (
"context" "context"
"errors" "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/da"
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors" "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. // DAQueue is a pipeline stage that reads DA entries from a DataSource and provides them to the next stage.
type DAQueue struct { type DAQueue struct {
l1height uint64 l1height uint64
initialBatch uint64
dataSourceFactory *DataSourceFactory dataSourceFactory *DataSourceFactory
dataSource DataSource dataSource DataSource
da da.Entries da da.Entries
} }
func NewDAQueue(l1height uint64, initialBatch uint64, dataSourceFactory *DataSourceFactory) *DAQueue { func NewDAQueue(l1height uint64, dataSourceFactory *DataSourceFactory) *DAQueue {
return &DAQueue{ return &DAQueue{
l1height: l1height, l1height: l1height,
initialBatch: initialBatch,
dataSourceFactory: dataSourceFactory, dataSourceFactory: dataSourceFactory,
dataSource: nil, dataSource: nil,
da: make(da.Entries, 0), da: make(da.Entries, 0),
@ -47,11 +45,6 @@ func (dq *DAQueue) NextDA(ctx context.Context) (da.Entry, error) {
daEntry := dq.da[0] daEntry := dq.da[0]
dq.da = dq.da[1:] 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 return daEntry, nil
} }
} }
@ -86,8 +79,8 @@ func (dq *DAQueue) DataSource() DataSource {
return dq.dataSource return dq.dataSource
} }
func (dq *DAQueue) Reset(height uint64) { func (dq *DAQueue) Reset(lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta) {
dq.l1height = height dq.l1height = lastProcessedBatchMeta.L1BlockNumber
dq.dataSource = nil dq.dataSource = nil
dq.da = make(da.Entries, 0) dq.da = make(da.Entries, 0)
} }

View file

@ -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
}

View file

@ -79,29 +79,51 @@ func NewSyncingPipeline(ctx context.Context, blockchain *core.BlockChain, genesi
} }
dataSourceFactory := NewDataSourceFactory(blockchain, genesisConfig, config, l1Reader, blobClientList, db) dataSourceFactory := NewDataSourceFactory(blockchain, genesisConfig, config, l1Reader, blobClientList, db)
var initialL1Block uint64 var lastProcessedBatchMeta *rawdb.DAProcessedBatchMeta
if config.RecoveryMode { if config.RecoveryMode {
initialL1Block = config.InitialL1Block if config.InitialL1Block == 0 {
if initialL1Block == 0 {
return nil, errors.New("sync from DA: initial L1 block must be set in recovery mode") return nil, errors.New("sync from DA: initial L1 block must be set in recovery mode")
} }
if config.InitialBatch == 0 { if config.InitialBatch == 0 {
return nil, errors.New("sync from DA: initial batch must be set in recovery mode") 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) l1MessageQueueHeightFinder, err := NewL1MessageQueueHeightFinder(ctx, config.InitialL1Block, l1Reader, blobClientList, db)
} else { if err != nil {
initialL1Block = l1DeploymentBlock - 1 return nil, fmt.Errorf("failed to create L1MessageQueueHeightFinder: %w", err)
config.InitialL1Block = initialL1Block
from := rawdb.ReadDASyncedL1BlockNumber(db)
if from != nil {
initialL1Block = *from
} }
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) daQueue := NewDAQueue(lastProcessedBatchMeta.L1BlockNumber, dataSourceFactory)
batchQueue := NewBatchQueue(daQueue, db) batchQueue := NewBatchQueue(daQueue, db, lastProcessedBatchMeta)
blockQueue := NewBlockQueue(batchQueue) blockQueue := NewBlockQueue(batchQueue)
daSyncer := NewDASyncer(blockchain, config.L2EndBlock) daSyncer := NewDASyncer(blockchain, config.L2EndBlock)
@ -260,12 +282,21 @@ func (s *SyncingPipeline) Stop() {
func (s *SyncingPipeline) reset(resetCounter int) { func (s *SyncingPipeline) reset(resetCounter int) {
amount := 100 * uint64(resetCounter) amount := 100 * uint64(resetCounter)
syncedL1Height := s.config.InitialL1Block
from := rawdb.ReadDASyncedL1BlockNumber(s.db) lastProcessedBatchMeta := rawdb.ReadDAProcessedBatchMeta(s.db)
if from != nil && *from+amount > syncedL1Height { if lastProcessedBatchMeta == nil {
syncedL1Height = *from - amount lastProcessedBatchMeta = &rawdb.DAProcessedBatchMeta{
rawdb.WriteDASyncedL1BlockNumber(s.db, syncedL1Height) 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)
} }

File diff suppressed because one or more lines are too long

View file

@ -12,13 +12,15 @@ import (
) )
func TestEventSignatures(t *testing.T) { func TestEventSignatures(t *testing.T) {
assert.Equal(t, crypto.Keccak256Hash([]byte("CommitBatch(uint256,bytes32)")), ScrollChainABI.Events["CommitBatch"].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["RevertBatch"].ID) assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,bytes32)")), ScrollChainABI.Events[revertBatchV0EventName].ID)
assert.Equal(t, crypto.Keccak256Hash([]byte("FinalizeBatch(uint256,bytes32,bytes32,bytes32)")), ScrollChainABI.Events["FinalizeBatch"].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) { func TestUnpackLog(t *testing.T) {
mockBatchIndex := big.NewInt(123) mockBatchIndex := big.NewInt(123)
finishMockBatchIndex := big.NewInt(125)
mockBatchHash := crypto.Keccak256Hash([]byte("mockBatch")) mockBatchHash := crypto.Keccak256Hash([]byte("mockBatch"))
mockStateRoot := crypto.Keccak256Hash([]byte("mockStateRoot")) mockStateRoot := crypto.Keccak256Hash([]byte("mockStateRoot"))
mockWithdrawRoot := crypto.Keccak256Hash([]byte("mockWithdrawRoot")) mockWithdrawRoot := crypto.Keccak256Hash([]byte("mockWithdrawRoot"))
@ -42,16 +44,40 @@ func TestUnpackLog(t *testing.T) {
&CommitBatchEventUnpacked{}, &CommitBatchEventUnpacked{},
}, },
{ {
revertBatchEventName, revertBatchV0EventName,
types.Log{ types.Log{
Data: nil, 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, BatchIndex: mockBatchIndex,
BatchHash: mockBatchHash, 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, finalizeBatchEventName,

View file

@ -16,7 +16,8 @@ import (
const ( const (
commitBatchEventName = "CommitBatch" commitBatchEventName = "CommitBatch"
revertBatchEventName = "RevertBatch" revertBatchV0EventName = "RevertBatch"
revertBatchV7EventName = "RevertBatch0"
finalizeBatchEventName = "FinalizeBatch" finalizeBatchEventName = "FinalizeBatch"
nextUnfinalizedQueueIndex = "nextUnfinalizedQueueIndex" nextUnfinalizedQueueIndex = "nextUnfinalizedQueueIndex"
lastFinalizedBatchIndex = "lastFinalizedBatchIndex" lastFinalizedBatchIndex = "lastFinalizedBatchIndex"
@ -32,7 +33,8 @@ type Reader struct {
scrollChainABI *abi.ABI scrollChainABI *abi.ABI
l1MessageQueueABI *abi.ABI l1MessageQueueABI *abi.ABI
l1CommitBatchEventSignature common.Hash l1CommitBatchEventSignature common.Hash
l1RevertBatchEventSignature common.Hash l1RevertBatchEventV0Signature common.Hash
l1RevertBatchEventV7Signature common.Hash
l1FinalizeBatchEventSignature common.Hash l1FinalizeBatchEventSignature common.Hash
} }
@ -60,7 +62,8 @@ func NewReader(ctx context.Context, config Config, l1Client Client) (*Reader, er
scrollChainABI: ScrollChainABI, scrollChainABI: ScrollChainABI,
l1MessageQueueABI: L1MessageQueueABIManual, l1MessageQueueABI: L1MessageQueueABIManual,
l1CommitBatchEventSignature: ScrollChainABI.Events[commitBatchEventName].ID, 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, l1FinalizeBatchEventSignature: ScrollChainABI.Events[finalizeBatchEventName].ID,
} }
@ -172,10 +175,11 @@ func (r *Reader) FetchRollupEventsInRange(from, to uint64) (RollupEvents, error)
}, },
Topics: make([][]common.Hash, 1), 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][0] = r.l1CommitBatchEventSignature
query.Topics[0][1] = r.l1RevertBatchEventSignature query.Topics[0][1] = r.l1RevertBatchEventV0Signature
query.Topics[0][2] = r.l1FinalizeBatchEventSignature query.Topics[0][2] = r.l1RevertBatchEventV7Signature
query.Topics[0][3] = r.l1FinalizeBatchEventSignature
logsBatch, err := r.client.FilterLogs(r.ctx, query) logsBatch, err := r.client.FilterLogs(r.ctx, query)
if err != nil { if err != nil {
@ -203,10 +207,11 @@ func (r *Reader) FetchRollupEventsInRangeWithCallback(from, to uint64, callback
}, },
Topics: make([][]common.Hash, 1), 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][0] = r.l1CommitBatchEventSignature
query.Topics[0][1] = r.l1RevertBatchEventSignature query.Topics[0][1] = r.l1RevertBatchEventV0Signature
query.Topics[0][2] = r.l1FinalizeBatchEventSignature query.Topics[0][2] = r.l1RevertBatchEventV7Signature
query.Topics[0][3] = r.l1FinalizeBatchEventSignature
logsBatch, err := r.client.FilterLogs(r.ctx, query) logsBatch, err := r.client.FilterLogs(r.ctx, query)
if err != nil { 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 { if err = UnpackLog(r.scrollChainABI, event, commitBatchEventName, vLog); err != nil {
return nil, fmt.Errorf("failed to unpack commit rollup event log, err: %w", err) 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{ rollupEvent = &CommitBatchEvent{
batchIndex: event.BatchIndex, batchIndex: event.BatchIndex,
batchHash: event.BatchHash, batchHash: event.BatchHash,
@ -254,26 +259,39 @@ func (r *Reader) processLogsToRollupEvents(logs []types.Log) (RollupEvents, erro
blockNumber: vLog.BlockNumber, blockNumber: vLog.BlockNumber,
} }
case r.l1RevertBatchEventSignature: case r.l1RevertBatchEventV0Signature:
event := &RevertBatchEventUnpacked{} event := &RevertBatchEventV0Unpacked{}
if err = UnpackLog(r.scrollChainABI, event, revertBatchEventName, vLog); err != nil { if err = UnpackLog(r.scrollChainABI, event, revertBatchV0EventName, vLog); err != nil {
return nil, fmt.Errorf("failed to unpack revert rollup event log, err: %w", err) 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()) log.Trace("found new RevertBatchV0Type event", "batch index", event.BatchIndex.Uint64(), "batch hash", event.BatchHash.Hex())
rollupEvent = &RevertBatchEvent{ rollupEvent = &RevertBatchEventV0{
batchIndex: event.BatchIndex, batchIndex: event.BatchIndex,
batchHash: event.BatchHash, batchHash: event.BatchHash,
txHash: vLog.TxHash, txHash: vLog.TxHash,
blockHash: vLog.BlockHash, blockHash: vLog.BlockHash,
blockNumber: vLog.BlockNumber, 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: case r.l1FinalizeBatchEventSignature:
event := &FinalizeBatchEventUnpacked{} event := &FinalizeBatchEventUnpacked{}
if err = UnpackLog(r.scrollChainABI, event, finalizeBatchEventName, vLog); err != nil { if err = UnpackLog(r.scrollChainABI, event, finalizeBatchEventName, vLog); err != nil {
return nil, fmt.Errorf("failed to unpack finalized rollup event log, err: %w", err) 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{ rollupEvent = &FinalizeBatchEvent{
batchIndex: event.BatchIndex, batchIndex: event.BatchIndex,
batchHash: event.BatchHash, batchHash: event.BatchHash,
@ -375,6 +393,11 @@ func (r *Reader) FetchCommitTxData(commitEvent *CommitBatchEvent) (*CommitBatchA
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to decode calldata into commitBatch args %s, values: %+v, err: %w", commitBatchWithBlobProofMethodName, values, err) 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 { } else {
return nil, fmt.Errorf("unknown method name for commit transaction: %s", method.Name) 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 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
}

View file

@ -238,7 +238,9 @@ func (s *RollupSyncService) updateRollupEvents(daEntries da.Entries) error {
case da.RevertBatchType: case da.RevertBatchType:
log.Trace("found new RevertBatch event", "batch index", entry.BatchIndex()) 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: case da.FinalizeBatchType:
event, ok := entry.Event().(*l1.FinalizeBatchEvent) event, ok := entry.Event().(*l1.FinalizeBatchEvent)
@ -321,6 +323,33 @@ func (s *RollupSyncService) updateRollupEvents(daEntries da.Entries) error {
return nil 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) { func (s *RollupSyncService) getLocalChunksForBatch(chunkBlockRanges []*rawdb.ChunkBlockRange) ([]*encoding.Chunk, error) {
if len(chunkBlockRanges) == 0 { if len(chunkBlockRanges) == 0 {
return nil, fmt.Errorf("chunkBlockRanges is empty") return nil, fmt.Errorf("chunkBlockRanges is empty")
@ -377,7 +406,7 @@ func (s *RollupSyncService) getCommittedBatchMeta(commitedBatch da.EntryWithBloc
return &rawdb.CommittedBatchMeta{ return &rawdb.CommittedBatchMeta{
Version: 0, Version: 0,
ChunkBlockRanges: []*rawdb.ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 0}}, ChunkBlockRanges: []*rawdb.ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 0}},
LastL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
}, nil }, 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) 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. // With CodecV7 the batch creation changed. We need to compute and store PostL1MessageQueueHash.
// InitialL1MessageQueueHash of a batch == LastL1MessageQueueHash of the previous batch. // 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 // 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 // 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. // 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. // 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. // In this case we need to compute the prevL1MessageQueueHash from the empty hash.
var initialL1MessageQueueHash common.Hash var prevL1MessageQueueHash common.Hash
if encoding.CodecVersion(parentCommittedBatchMeta.Version) < commitedBatch.Version() { if encoding.CodecVersion(parentCommittedBatchMeta.Version) < commitedBatch.Version() {
initialL1MessageQueueHash = common.Hash{} prevL1MessageQueueHash = common.Hash{}
} else { } else {
initialL1MessageQueueHash = parentCommittedBatchMeta.LastL1MessageQueueHash prevL1MessageQueueHash = parentCommittedBatchMeta.PostL1MessageQueueHash
} }
chunks, err := s.getLocalChunksForBatch(chunkRanges) 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()) 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 { if err != nil {
return nil, fmt.Errorf("failed to apply L1 messages from blocks, batch index: %v, err: %w", chunks[0], err) 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{ return &rawdb.CommittedBatchMeta{
Version: uint8(commitedBatch.Version()), Version: uint8(commitedBatch.Version()),
ChunkBlockRanges: chunkRanges, ChunkBlockRanges: chunkRanges,
LastL1MessageQueueHash: lastL1MessageQueueHash, PostL1MessageQueueHash: lastL1MessageQueueHash,
}, nil }, nil
} }
@ -490,12 +519,11 @@ func validateBatch(batchIndex uint64, event *l1.FinalizeBatchEvent, parentFinali
} }
batch = &encoding.Batch{ batch = &encoding.Batch{
Index: batchIndex, Index: batchIndex,
ParentBatchHash: parentFinalizedBatchMeta.BatchHash, ParentBatchHash: parentFinalizedBatchMeta.BatchHash,
InitialL1MessageIndex: parentFinalizedBatchMeta.TotalL1MessagePopped, Blocks: startChunk.Blocks,
Blocks: startChunk.Blocks, PrevL1MessageQueueHash: parentCommittedBatchMeta.PostL1MessageQueueHash,
InitialL1MessageQueueHash: parentCommittedBatchMeta.LastL1MessageQueueHash, PostL1MessageQueueHash: committedBatchMeta.PostL1MessageQueueHash,
LastL1MessageQueueHash: committedBatchMeta.LastL1MessageQueueHash,
} }
} }

View file

@ -161,6 +161,10 @@ type mockEntryWithBlocks struct {
versionedHashes []common.Hash versionedHashes []common.Hash
} }
func (m mockEntryWithBlocks) L1MessagesPoppedInBatch() uint64 {
panic("implement me")
}
func (m mockEntryWithBlocks) Type() da.Type { func (m mockEntryWithBlocks) Type() da.Type {
panic("implement me") panic("implement me")
} }
@ -181,7 +185,7 @@ func (m mockEntryWithBlocks) Event() l1.RollupEvent {
panic("implement me") panic("implement me")
} }
func (m mockEntryWithBlocks) Blocks() []*da.PartialBlock { func (m mockEntryWithBlocks) Blocks() ([]*da.PartialBlock, error) {
panic("implement me") panic("implement me")
} }
@ -193,6 +197,14 @@ func (m mockEntryWithBlocks) Chunks() []*encoding.DAChunkRawTx {
return m.chunks 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 { func (m mockEntryWithBlocks) BlobVersionedHashes() []common.Hash {
return m.versionedHashes return m.versionedHashes
} }
@ -667,11 +679,10 @@ func TestValidateBatchCodecV7(t *testing.T) {
{ {
block1 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_02.json"), 1) block1 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_02.json"), 1)
batch1 := &encoding.Batch{ batch1 := &encoding.Batch{
Index: 1, Index: 1,
InitialL1MessageIndex: 0, PrevL1MessageQueueHash: common.Hash{},
InitialL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
LastL1MessageQueueHash: common.Hash{}, Blocks: []*encoding.Block{block1},
Blocks: []*encoding.Block{block1},
} }
batch1LastBlock := batch1.Blocks[len(batch1.Blocks)-1] batch1LastBlock := batch1.Blocks[len(batch1.Blocks)-1]
@ -690,7 +701,7 @@ func TestValidateBatchCodecV7(t *testing.T) {
committedBatchMeta1 = &rawdb.CommittedBatchMeta{ committedBatchMeta1 = &rawdb.CommittedBatchMeta{
Version: uint8(encoding.CodecV7), Version: uint8(encoding.CodecV7),
LastL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
} }
var endBlock1 uint64 var endBlock1 uint64
@ -708,12 +719,11 @@ func TestValidateBatchCodecV7(t *testing.T) {
// finalize 3 batches with CodecV7 at once // finalize 3 batches with CodecV7 at once
block2 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_03.json"), 2) block2 := replaceBlockNumber(readBlockFromJSON(t, "./testdata/blockTrace_03.json"), 2)
batch2 := &encoding.Batch{ batch2 := &encoding.Batch{
Index: 2, Index: 2,
ParentBatchHash: finalizedBatchMeta1.BatchHash, ParentBatchHash: finalizedBatchMeta1.BatchHash,
InitialL1MessageIndex: 0, PrevL1MessageQueueHash: common.Hash{},
InitialL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
LastL1MessageQueueHash: common.Hash{}, Blocks: []*encoding.Block{block2},
Blocks: []*encoding.Block{block2},
} }
batch2LastBlock := batch2.Blocks[len(batch2.Blocks)-1] 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}) LastL1MessageQueueHashBatch3, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(common.Hash{}, []*encoding.Block{block3})
require.NoError(t, err) require.NoError(t, err)
batch3 := &encoding.Batch{ batch3 := &encoding.Batch{
Index: 3, Index: 3,
ParentBatchHash: daBatch2.Hash(), ParentBatchHash: daBatch2.Hash(),
InitialL1MessageIndex: 0, PrevL1MessageQueueHash: common.Hash{},
InitialL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: LastL1MessageQueueHashBatch3,
LastL1MessageQueueHash: LastL1MessageQueueHashBatch3, Blocks: []*encoding.Block{block3},
Blocks: []*encoding.Block{block3},
} }
batch3LastBlock := batch3.Blocks[len(batch3.Blocks)-1] batch3LastBlock := batch3.Blocks[len(batch3.Blocks)-1]
@ -740,12 +749,11 @@ func TestValidateBatchCodecV7(t *testing.T) {
LastL1MessageQueueHashBatch4, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(LastL1MessageQueueHashBatch3, []*encoding.Block{block4}) LastL1MessageQueueHashBatch4, err := encoding.MessageQueueV2ApplyL1MessagesFromBlocks(LastL1MessageQueueHashBatch3, []*encoding.Block{block4})
require.NoError(t, err) require.NoError(t, err)
batch4 := &encoding.Batch{ batch4 := &encoding.Batch{
Index: 4, Index: 4,
ParentBatchHash: daBatch3.Hash(), ParentBatchHash: daBatch3.Hash(),
InitialL1MessageIndex: 1, PrevL1MessageQueueHash: LastL1MessageQueueHashBatch3,
InitialL1MessageQueueHash: LastL1MessageQueueHashBatch3, PostL1MessageQueueHash: LastL1MessageQueueHashBatch4,
LastL1MessageQueueHash: LastL1MessageQueueHashBatch4, Blocks: []*encoding.Block{block4},
Blocks: []*encoding.Block{block4},
} }
batch4LastBlock := batch4.Blocks[len(batch4.Blocks)-1] batch4LastBlock := batch4.Blocks[len(batch4.Blocks)-1]
@ -764,17 +772,17 @@ func TestValidateBatchCodecV7(t *testing.T) {
committedBatchMeta2 := &rawdb.CommittedBatchMeta{ committedBatchMeta2 := &rawdb.CommittedBatchMeta{
Version: uint8(encoding.CodecV7), Version: uint8(encoding.CodecV7),
LastL1MessageQueueHash: common.Hash{}, PostL1MessageQueueHash: common.Hash{},
} }
committedBatchMeta3 := &rawdb.CommittedBatchMeta{ committedBatchMeta3 := &rawdb.CommittedBatchMeta{
Version: uint8(encoding.CodecV7), Version: uint8(encoding.CodecV7),
LastL1MessageQueueHash: LastL1MessageQueueHashBatch3, PostL1MessageQueueHash: LastL1MessageQueueHashBatch3,
} }
committedBatchMeta4 := &rawdb.CommittedBatchMeta{ committedBatchMeta4 := &rawdb.CommittedBatchMeta{
Version: uint8(encoding.CodecV7), Version: uint8(encoding.CodecV7),
LastL1MessageQueueHash: LastL1MessageQueueHashBatch4, PostL1MessageQueueHash: LastL1MessageQueueHashBatch4,
} }
endBlock2, finalizedBatchMeta2, err := validateBatch(2, event2, finalizedBatchMeta1, committedBatchMeta1, committedBatchMeta2, []*encoding.Chunk{{Blocks: batch2.Blocks}}, nil) endBlock2, finalizedBatchMeta2, err := validateBatch(2, event2, finalizedBatchMeta1, committedBatchMeta1, committedBatchMeta2, []*encoding.Chunk{{Blocks: batch2.Blocks}}, nil)