go-ethereum/rollup/da_syncer/l1_message_queue_height_finder.go
Jonas Theis 40ebbd6491
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>
2025-03-03 13:14:32 +01:00

136 lines
5.1 KiB
Go

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
}