From ef980a2059f141f940007effbab93209a061df31 Mon Sep 17 00:00:00 2001 From: Mengran Lan Date: Mon, 1 Jul 2024 22:24:32 +0800 Subject: [PATCH] feat(rollup_sync_service): handle multiple batches in `FinalizeBatch` event (#865) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: now FinalizeBatch event will influence one or more batches * fix bug && add unit test * fix typo * Update core/rawdb/accessors_rollup_event_test.go Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> * trivial fix * chore: auto version bump [bot] * trivial change: move write finalized things logic out of loop * utilize code && comment * remove redundant check * two log level changing case --------- Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: Thegaram --- core/rawdb/accessors_rollup_event.go | 27 +++++++++++ core/rawdb/accessors_rollup_event_test.go | 28 +++++++++++- core/rawdb/schema.go | 1 + params/version.go | 2 +- .../rollup_sync_service.go | 45 ++++++++++++++----- 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/core/rawdb/accessors_rollup_event.go b/core/rawdb/accessors_rollup_event.go index 796e60628a4d..e04c3f0c7028 100644 --- a/core/rawdb/accessors_rollup_event.go +++ b/core/rawdb/accessors_rollup_event.go @@ -144,3 +144,30 @@ func ReadFinalizedL2BlockNumber(db ethdb.Reader) *uint64 { finalizedL2BlockNumber := number.Uint64() return &finalizedL2BlockNumber } + +// WriteLastFinalizedBatchIndex stores the last finalized batch index in the database. +func WriteLastFinalizedBatchIndex(db ethdb.KeyValueWriter, lastFinalizedBatchIndex uint64) { + value := big.NewInt(0).SetUint64(lastFinalizedBatchIndex).Bytes() + if err := db.Put(lastFinalizedBatchIndexKey, value); err != nil { + log.Crit("failed to store last finalized batch index for rollup event", "batch index", lastFinalizedBatchIndex, "value", value, "err", err) + } +} + +// ReadLastFinalizedBatchIndex fetches the last finalized batch index from the database. +func ReadLastFinalizedBatchIndex(db ethdb.Reader) *uint64 { + data, err := db.Get(lastFinalizedBatchIndexKey) + if err != nil && isNotFoundErr(err) { + return nil + } + if err != nil { + log.Crit("failed to read last finalized batch index from database", "key", lastFinalizedBatchIndexKey, "err", err) + } + + number := new(big.Int).SetBytes(data) + if !number.IsUint64() { + log.Crit("unexpected finalized batch index in database", "data", data, "number", number) + } + + lastFinalizedBatchIndex := number.Uint64() + return &lastFinalizedBatchIndex +} diff --git a/core/rawdb/accessors_rollup_event_test.go b/core/rawdb/accessors_rollup_event_test.go index 861b4523522a..3c34e4829b51 100644 --- a/core/rawdb/accessors_rollup_event_test.go +++ b/core/rawdb/accessors_rollup_event_test.go @@ -45,7 +45,7 @@ func TestFinalizedL2BlockNumber(t *testing.T) { // read non-existing value if got := ReadFinalizedL2BlockNumber(db); got != nil { - t.Fatal("Expected 0 for non-existing value", "got", *got) + t.Fatal("Expected nil for non-existing value", "got", *got) } for _, num := range blockNumbers { @@ -58,6 +58,32 @@ func TestFinalizedL2BlockNumber(t *testing.T) { } } +func TestLastFinalizedBatchIndex(t *testing.T) { + batchIndxes := []uint64{ + 1, + 1 << 2, + 1 << 8, + 1 << 16, + 1 << 32, + } + + db := NewMemoryDatabase() + + // read non-existing value + if got := ReadLastFinalizedBatchIndex(db); got != nil { + t.Fatal("Expected nil for non-existing value", "got", *got) + } + + for _, num := range batchIndxes { + WriteLastFinalizedBatchIndex(db, num) + got := ReadLastFinalizedBatchIndex(db) + + if *got != num { + t.Fatal("Batch index mismatch", "expected", num, "got", got) + } + } +} + func TestFinalizedBatchMeta(t *testing.T) { batches := []*FinalizedBatchMeta{ { diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index f5f82cb2cafb..5d144df2bef3 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -115,6 +115,7 @@ var ( batchChunkRangesPrefix = []byte("R-bcr") batchMetaPrefix = []byte("R-bm") finalizedL2BlockNumberKey = []byte("R-finalized") + lastFinalizedBatchIndexKey = []byte("R-finalizedBatchIndex") // Row consumption rowConsumptionPrefix = []byte("rc") // rowConsumptionPrefix + hash -> row consumption by block diff --git a/params/version.go b/params/version.go index 100539112d5a..cb4ccd46c0bb 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 = 5 // Minor version component of the current release - VersionPatch = 1 // Patch version component of the current release + VersionPatch = 2 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/rollup_sync_service/rollup_sync_service.go b/rollup/rollup_sync_service/rollup_sync_service.go index e74e41e9bc73..2aff697a9462 100644 --- a/rollup/rollup_sync_service/rollup_sync_service.go +++ b/rollup/rollup_sync_service/rollup_sync_service.go @@ -225,23 +225,46 @@ func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endB batchIndex := event.BatchIndex.Uint64() log.Trace("found new FinalizeBatch event", "batch index", batchIndex) - parentBatchMeta, chunks, err := s.getLocalInfoForBatch(batchIndex) - if err != nil { - return fmt.Errorf("failed to get local node info, batch index: %v, err: %w", batchIndex, err) + lastFinalizedBatchIndex := rawdb.ReadLastFinalizedBatchIndex(s.db) + + // After darwin, FinalizeBatch event emitted every bundle, which contains multiple batches. + // Therefore there are a range of finalized batches need to be saved into db. + // + // The range logic also applies to the batches before darwin when FinalizeBatch event emitted + // per single batch. In this situation, `batchIndex` just equals to `*lastFinalizedBatchIndex + 1` + // and only one batch is processed through the for loop. + startBatchIndex := batchIndex + if lastFinalizedBatchIndex != nil { + startBatchIndex = *lastFinalizedBatchIndex + 1 + } else { + log.Warn("got nil when reading last finalized batch index. This should happen only once.") } - endBlock, finalizedBatchMeta, err := validateBatch(event, parentBatchMeta, chunks, s.bc.Config(), s.stack) - if err != nil { - return fmt.Errorf("fatal: validateBatch failed: finalize event: %v, err: %w", event, err) - } + var highestFinalizedBlockNumber uint64 + for index := startBatchIndex; index <= batchIndex; index++ { + parentBatchMeta, chunks, err := s.getLocalInfoForBatch(index) + if err != nil { + return fmt.Errorf("failed to get local node info, batch index: %v, err: %w", index, err) + } - rawdb.WriteFinalizedL2BlockNumber(s.db, endBlock) - rawdb.WriteFinalizedBatchMeta(s.db, batchIndex, finalizedBatchMeta) + endBlock, finalizedBatchMeta, err := validateBatch(event, parentBatchMeta, chunks, s.bc.Config(), s.stack) + if err != nil { + return fmt.Errorf("fatal: validateBatch failed: finalize event: %v, err: %w", event, err) + } - if batchIndex%100 == 0 { - log.Info("finalized batch progress", "batch index", batchIndex, "finalized l2 block height", endBlock) + rawdb.WriteFinalizedBatchMeta(s.db, index, finalizedBatchMeta) + + if index%100 == 0 { + log.Info("finalized batch progress", "batch index", index, "finalized l2 block height", endBlock) + } + + highestFinalizedBlockNumber = endBlock } + rawdb.WriteFinalizedL2BlockNumber(s.db, highestFinalizedBlockNumber) + rawdb.WriteLastFinalizedBatchIndex(s.db, batchIndex) + log.Debug("write finalized l2 block number", "batch index", batchIndex, "finalized l2 block height", highestFinalizedBlockNumber) + default: return fmt.Errorf("unknown event, topic: %v, tx hash: %v", vLog.Topics[0].Hex(), vLog.TxHash.Hex()) }