Skip to content

Commit 0360eba

Browse files
authored
fix(l2geth-verifier): only check last batch of the bundle (#904)
* fix(l2geth-verifier): only check the last batch of each bundle * refactor function comments * add more logs to debug * fix the bug * Revert "add more logs to debug" This reverts commit 4befe9f. * tweak * TestValidateBatchInFinalizeByBundle * update da-codec dependency * move rawdb.WriteFinalizedBatchMeta outside the loop * use batch writer * change log.Crit to log.Error
1 parent 0cecc19 commit 0360eba

File tree

5 files changed

+133
-53
lines changed

5 files changed

+133
-53
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ require (
5050
github.com/prometheus/tsdb v0.7.1
5151
github.com/rjeczalik/notify v0.9.1
5252
github.com/rs/cors v1.7.0
53-
github.com/scroll-tech/da-codec v0.1.1-0.20240703091800-5b6cded48ab7
53+
github.com/scroll-tech/da-codec v0.1.1-0.20240716101216-c55ed9455cf4
5454
github.com/scroll-tech/zktrie v0.8.4
5555
github.com/shirou/gopsutil v3.21.11+incompatible
5656
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
392392
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
393393
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
394394
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
395-
github.com/scroll-tech/da-codec v0.1.1-0.20240703091800-5b6cded48ab7 h1:UfiLBLCAMBk9bsTP3fc1fETpNVFSQapQVdLcZveyV0M=
396-
github.com/scroll-tech/da-codec v0.1.1-0.20240703091800-5b6cded48ab7/go.mod h1:D6XEESeNVJkQJlv3eK+FyR+ufPkgVQbJzERylQi53Bs=
395+
github.com/scroll-tech/da-codec v0.1.1-0.20240716101216-c55ed9455cf4 h1:40Lby3huKNFZ2EXzxqVpADB+caepDRrNRoUgTsCKN88=
396+
github.com/scroll-tech/da-codec v0.1.1-0.20240716101216-c55ed9455cf4/go.mod h1:D6XEESeNVJkQJlv3eK+FyR+ufPkgVQbJzERylQi53Bs=
397397
github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE=
398398
github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
399399
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=

params/version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 5 // Major version component of the current release
2626
VersionMinor = 5 // Minor version component of the current release
27-
VersionPatch = 12 // Patch version component of the current release
27+
VersionPatch = 13 // Patch version component of the current release
2828
VersionMeta = "mainnet" // Version metadata to append to the version string
2929
)
3030

rollup/rollup_sync_service/rollup_sync_service.go

+72-37
Original file line numberDiff line numberDiff line change
@@ -242,26 +242,31 @@ func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endB
242242
}
243243

244244
var highestFinalizedBlockNumber uint64
245+
batchWriter := s.db.NewBatch()
245246
for index := startBatchIndex; index <= batchIndex; index++ {
246247
parentBatchMeta, chunks, err := s.getLocalInfoForBatch(index)
247248
if err != nil {
248249
return fmt.Errorf("failed to get local node info, batch index: %v, err: %w", index, err)
249250
}
250251

251-
endBlock, finalizedBatchMeta, err := validateBatch(event, parentBatchMeta, chunks, s.bc.Config(), s.stack)
252+
endBlock, finalizedBatchMeta, err := validateBatch(index, event, parentBatchMeta, chunks, s.bc.Config(), s.stack)
252253
if err != nil {
253254
return fmt.Errorf("fatal: validateBatch failed: finalize event: %v, err: %w", event, err)
254255
}
255256

256-
rawdb.WriteFinalizedBatchMeta(s.db, index, finalizedBatchMeta)
257+
rawdb.WriteFinalizedBatchMeta(batchWriter, index, finalizedBatchMeta)
258+
highestFinalizedBlockNumber = endBlock
257259

258260
if index%100 == 0 {
259261
log.Info("finalized batch progress", "batch index", index, "finalized l2 block height", endBlock)
260262
}
261-
262-
highestFinalizedBlockNumber = endBlock
263263
}
264264

265+
if err := batchWriter.Write(); err != nil {
266+
log.Error("fatal: failed to batch write finalized batch meta to database", "startBatchIndex", startBatchIndex, "endBatchIndex", batchIndex,
267+
"batchCount", batchIndex-startBatchIndex+1, "highestFinalizedBlockNumber", highestFinalizedBlockNumber, "err", err)
268+
return fmt.Errorf("failed to batch write finalized batch meta to database: %w", err)
269+
}
265270
rawdb.WriteFinalizedL2BlockNumber(s.db, highestFinalizedBlockNumber)
266271
rawdb.WriteLastFinalizedBatchIndex(s.db, batchIndex)
267272
log.Debug("write finalized l2 block number", "batch index", batchIndex, "finalized l2 block height", highestFinalizedBlockNumber)
@@ -423,42 +428,48 @@ func (s *RollupSyncService) decodeChunkBlockRanges(txData []byte) ([]*rawdb.Chun
423428
}
424429

425430
// validateBatch verifies the consistency between the L1 contract and L2 node data.
431+
// It performs the following checks:
432+
// 1. Recalculates the batch hash locally
433+
// 2. Compares local state root, local withdraw root, and locally calculated batch hash with L1 data (for the last batch only when "finalize by bundle")
434+
//
426435
// The function will terminate the node and exit if any consistency check fails.
427-
// It returns the number of the end block, a finalized batch meta data, and an error if any.
428-
func validateBatch(event *L1FinalizeBatchEvent, parentBatchMeta *rawdb.FinalizedBatchMeta, chunks []*encoding.Chunk, chainCfg *params.ChainConfig, stack *node.Node) (uint64, *rawdb.FinalizedBatchMeta, error) {
436+
//
437+
// Parameters:
438+
// - batchIndex: batch index of the validated batch
439+
// - event: L1 finalize batch event data
440+
// - parentBatchMeta: metadata of the parent batch
441+
// - chunks: slice of chunk data for the current batch
442+
// - chainCfg: chain configuration to identify the codec version
443+
// - stack: node stack to terminate the node in case of inconsistency
444+
//
445+
// Returns:
446+
// - uint64: the end block height of the batch
447+
// - *rawdb.FinalizedBatchMeta: finalized batch metadata
448+
// - error: any error encountered during validation
449+
//
450+
// Note: This function is compatible with both "finalize by batch" and "finalize by bundle" methods.
451+
// In "finalize by bundle", only the last batch of each bundle is fully verified.
452+
// This check still ensures the correctness of all batch hashes in the bundle due to the parent-child relationship between batch hashes.
453+
func validateBatch(batchIndex uint64, event *L1FinalizeBatchEvent, parentBatchMeta *rawdb.FinalizedBatchMeta, chunks []*encoding.Chunk, chainCfg *params.ChainConfig, stack *node.Node) (uint64, *rawdb.FinalizedBatchMeta, error) {
429454
if len(chunks) == 0 {
430-
return 0, nil, fmt.Errorf("invalid argument: length of chunks is 0, batch index: %v", event.BatchIndex.Uint64())
455+
return 0, nil, fmt.Errorf("invalid argument: length of chunks is 0, batch index: %v", batchIndex)
431456
}
432457

433458
startChunk := chunks[0]
434459
if len(startChunk.Blocks) == 0 {
435-
return 0, nil, fmt.Errorf("invalid argument: block count of start chunk is 0, batch index: %v", event.BatchIndex.Uint64())
460+
return 0, nil, fmt.Errorf("invalid argument: block count of start chunk is 0, batch index: %v", batchIndex)
436461
}
437462
startBlock := startChunk.Blocks[0]
438463

439464
endChunk := chunks[len(chunks)-1]
440465
if len(endChunk.Blocks) == 0 {
441-
return 0, nil, fmt.Errorf("invalid argument: block count of end chunk is 0, batch index: %v", event.BatchIndex.Uint64())
466+
return 0, nil, fmt.Errorf("invalid argument: block count of end chunk is 0, batch index: %v", batchIndex)
442467
}
443468
endBlock := endChunk.Blocks[len(endChunk.Blocks)-1]
444469

445-
localStateRoot := endBlock.Header.Root
446-
if localStateRoot != event.StateRoot {
447-
log.Error("State root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized state root", event.StateRoot.Hex(), "l2 state root", localStateRoot.Hex())
448-
stack.Close()
449-
os.Exit(1)
450-
}
451-
452-
localWithdrawRoot := endBlock.WithdrawRoot
453-
if localWithdrawRoot != event.WithdrawRoot {
454-
log.Error("Withdraw root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized withdraw root", event.WithdrawRoot.Hex(), "l2 withdraw root", localWithdrawRoot.Hex())
455-
stack.Close()
456-
os.Exit(1)
457-
}
458-
459470
// Note: All params of batch are calculated locally based on the block data.
460471
batch := &encoding.Batch{
461-
Index: event.BatchIndex.Uint64(),
472+
Index: batchIndex,
462473
TotalL1MessagePoppedBefore: parentBatchMeta.TotalL1MessagePopped,
463474
ParentBatchHash: parentBatchMeta.BatchHash,
464475
Chunks: chunks,
@@ -468,40 +479,64 @@ func validateBatch(event *L1FinalizeBatchEvent, parentBatchMeta *rawdb.Finalized
468479
if startBlock.Header.Number.Uint64() == 0 || !chainCfg.IsBernoulli(startBlock.Header.Number) { // codecv0: genesis batch or batches before Bernoulli
469480
daBatch, err := codecv0.NewDABatch(batch)
470481
if err != nil {
471-
return 0, nil, fmt.Errorf("failed to create codecv0 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
482+
return 0, nil, fmt.Errorf("failed to create codecv0 DA batch, batch index: %v, err: %w", batchIndex, err)
472483
}
473484
localBatchHash = daBatch.Hash()
474485
} else if !chainCfg.IsCurie(startBlock.Header.Number) { // codecv1: batches after Bernoulli and before Curie
475486
daBatch, err := codecv1.NewDABatch(batch)
476487
if err != nil {
477-
return 0, nil, fmt.Errorf("failed to create codecv1 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
488+
return 0, nil, fmt.Errorf("failed to create codecv1 DA batch, batch index: %v, err: %w", batchIndex, err)
478489
}
479490
localBatchHash = daBatch.Hash()
480491
} else if !chainCfg.IsDarwin(startBlock.Header.Time) { // codecv2: batches after Curie and before Darwin
481492
daBatch, err := codecv2.NewDABatch(batch)
482493
if err != nil {
483-
return 0, nil, fmt.Errorf("failed to create codecv2 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
494+
return 0, nil, fmt.Errorf("failed to create codecv2 DA batch, batch index: %v, err: %w", batchIndex, err)
484495
}
485496
localBatchHash = daBatch.Hash()
486497
} else { // codecv3: batches after Darwin
487498
daBatch, err := codecv3.NewDABatch(batch)
488499
if err != nil {
489-
return 0, nil, fmt.Errorf("failed to create codecv3 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
500+
return 0, nil, fmt.Errorf("failed to create codecv3 DA batch, batch index: %v, err: %w", batchIndex, err)
490501
}
491502
localBatchHash = daBatch.Hash()
492503
}
493504

494-
// Note: If the batch headers match, this ensures the consistency of blocks and transactions
505+
localStateRoot := endBlock.Header.Root
506+
localWithdrawRoot := endBlock.WithdrawRoot
507+
508+
// Note: If the state root, withdraw root, and batch headers match, this ensures the consistency of blocks and transactions
495509
// (including skipped transactions) between L1 and L2.
496-
if localBatchHash != event.BatchHash {
497-
log.Error("Batch hash mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "parent TotalL1MessagePopped", parentBatchMeta.TotalL1MessagePopped, "l1 finalized batch hash", event.BatchHash.Hex(), "l2 batch hash", localBatchHash.Hex())
498-
chunksJson, err := json.Marshal(chunks)
499-
if err != nil {
500-
log.Error("marshal chunks failed", "err", err)
510+
//
511+
// Only check when batch index matches the index of the event. This is compatible with both "finalize by batch" and "finalize by bundle":
512+
// - finalize by batch: check all batches
513+
// - finalize by bundle: check the last batch, because only one event (containing the info of the last batch) is emitted per bundle
514+
if batchIndex == event.BatchIndex.Uint64() {
515+
if localStateRoot != event.StateRoot {
516+
log.Error("State root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized state root", event.StateRoot.Hex(), "l2 state root", localStateRoot.Hex())
517+
stack.Close()
518+
os.Exit(1)
519+
}
520+
521+
if localWithdrawRoot != event.WithdrawRoot {
522+
log.Error("Withdraw root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized withdraw root", event.WithdrawRoot.Hex(), "l2 withdraw root", localWithdrawRoot.Hex())
523+
stack.Close()
524+
os.Exit(1)
525+
}
526+
527+
// Verify batch hash
528+
// This check ensures the correctness of all batch hashes in the bundle
529+
// due to the parent-child relationship between batch hashes
530+
if localBatchHash != event.BatchHash {
531+
log.Error("Batch hash mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "parent TotalL1MessagePopped", parentBatchMeta.TotalL1MessagePopped, "l1 finalized batch hash", event.BatchHash.Hex(), "l2 batch hash", localBatchHash.Hex())
532+
chunksJson, err := json.Marshal(chunks)
533+
if err != nil {
534+
log.Error("marshal chunks failed", "err", err)
535+
}
536+
log.Error("Chunks", "chunks", string(chunksJson))
537+
stack.Close()
538+
os.Exit(1)
501539
}
502-
log.Error("Chunks", "chunks", string(chunksJson))
503-
stack.Close()
504-
os.Exit(1)
505540
}
506541

507542
totalL1MessagePopped := parentBatchMeta.TotalL1MessagePopped

0 commit comments

Comments
 (0)