55 "math/big"
66
77 "github.com/scroll-tech/da-codec/encoding"
8+ "github.com/scroll-tech/go-ethereum/accounts/abi"
89 "github.com/scroll-tech/go-ethereum/common"
910 "github.com/scroll-tech/go-ethereum/core/types"
1011 "github.com/scroll-tech/go-ethereum/crypto/kzg4844"
@@ -16,17 +17,20 @@ import (
1617// transaction data (calldata and blobs) by parsing them and comparing against database records.
1718// This ensures the constructed transaction data is correct and consistent with the database state.
1819func (r * Layer2Relayer ) sanityChecksCommitBatchCodecV7CalldataAndBlobs (calldata []byte , blobs []* kzg4844.Blob ) error {
19- calldataInfo , err := r .parseCommitBatchesCalldata (calldata )
20+ if r .l1RollupABI == nil {
21+ return fmt .Errorf ("l1RollupABI is nil: cannot parse commitBatches calldata" )
22+ }
23+ calldataInfo , err := parseCommitBatchesCalldata (r .l1RollupABI , calldata )
2024 if err != nil {
2125 return fmt .Errorf ("failed to parse calldata: %w" , err )
2226 }
2327
24- batchesToValidate , err := r .getBatchesFromCalldata (calldataInfo )
28+ batchesToValidate , l1MessagesWithBlockNumbers , err := r .getBatchesFromCalldata (calldataInfo )
2529 if err != nil {
2630 return fmt .Errorf ("failed to get batches from database: %w" , err )
2731 }
2832
29- if err := r .validateCalldataAndBlobsAgainstDatabase (calldataInfo , blobs , batchesToValidate ); err != nil {
33+ if err := r .validateCalldataAndBlobsAgainstDatabase (calldataInfo , blobs , batchesToValidate , l1MessagesWithBlockNumbers ); err != nil {
3034 return fmt .Errorf ("calldata and blobs validation failed: %w" , err )
3135 }
3236
@@ -45,8 +49,8 @@ type CalldataInfo struct {
4549}
4650
4751// parseCommitBatchesCalldata parses the commitBatches calldata and extracts key information
48- func ( r * Layer2Relayer ) parseCommitBatchesCalldata ( calldata []byte ) (* CalldataInfo , error ) {
49- method := r . l1RollupABI .Methods ["commitBatches" ]
52+ func parseCommitBatchesCalldata ( abi * abi. ABI , calldata []byte ) (* CalldataInfo , error ) {
53+ method := abi .Methods ["commitBatches" ]
5054 decoded , err := method .Inputs .Unpack (calldata [4 :])
5155 if err != nil {
5256 return nil , fmt .Errorf ("failed to unpack commitBatches calldata: %w" , err )
@@ -81,17 +85,17 @@ func (r *Layer2Relayer) parseCommitBatchesCalldata(calldata []byte) (*CalldataIn
8185}
8286
8387// getBatchesFromCalldata retrieves the relevant batches from database based on calldata information
84- func (r * Layer2Relayer ) getBatchesFromCalldata (info * CalldataInfo ) ([]* dbBatchWithChunks , error ) {
88+ func (r * Layer2Relayer ) getBatchesFromCalldata (info * CalldataInfo ) ([]* dbBatchWithChunks , map [ uint64 ][] * types. TransactionData , error ) {
8589 // Get the parent batch to determine the starting point
8690 parentBatch , err := r .batchOrm .GetBatchByHash (r .ctx , info .ParentBatchHash .Hex ())
8791 if err != nil {
88- return nil , fmt .Errorf ("failed to get parent batch by hash %s: %w" , info .ParentBatchHash .Hex (), err )
92+ return nil , nil , fmt .Errorf ("failed to get parent batch by hash %s: %w" , info .ParentBatchHash .Hex (), err )
8993 }
9094
9195 // Get the last batch to determine the ending point
9296 lastBatch , err := r .batchOrm .GetBatchByHash (r .ctx , info .LastBatchHash .Hex ())
9397 if err != nil {
94- return nil , fmt .Errorf ("failed to get last batch by hash %s: %w" , info .LastBatchHash .Hex (), err )
98+ return nil , nil , fmt .Errorf ("failed to get last batch by hash %s: %w" , info .LastBatchHash .Hex (), err )
9599 }
96100
97101 // Get all batches in the range (parent+1 to last)
@@ -100,29 +104,59 @@ func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWi
100104
101105 // Check if the range is valid
102106 if firstBatchIndex > lastBatchIndex {
103- return nil , fmt .Errorf ("no batches found in range: first index %d, last index %d" , firstBatchIndex , lastBatchIndex )
107+ return nil , nil , fmt .Errorf ("no batches found in range: first index %d, last index %d" , firstBatchIndex , lastBatchIndex )
104108 }
105109
106110 var batchesToValidate []* dbBatchWithChunks
111+ l1MessagesWithBlockNumbers := make (map [uint64 ][]* types.TransactionData )
107112 for batchIndex := firstBatchIndex ; batchIndex <= lastBatchIndex ; batchIndex ++ {
108113 dbBatch , err := r .batchOrm .GetBatchByIndex (r .ctx , batchIndex )
109114 if err != nil {
110- return nil , fmt .Errorf ("failed to get batch by index %d: %w" , batchIndex , err )
115+ return nil , nil , fmt .Errorf ("failed to get batch by index %d: %w" , batchIndex , err )
111116 }
112117
113118 // Get chunks for this batch
114119 dbChunks , err := r .chunkOrm .GetChunksInRange (r .ctx , dbBatch .StartChunkIndex , dbBatch .EndChunkIndex )
115120 if err != nil {
116- return nil , fmt .Errorf ("failed to get chunks for batch %d: %w" , batchIndex , err )
121+ return nil , nil , fmt .Errorf ("failed to get chunks for batch %d: %w" , batchIndex , err )
117122 }
118123
119124 batchesToValidate = append (batchesToValidate , & dbBatchWithChunks {
120125 Batch : dbBatch ,
121126 Chunks : dbChunks ,
122127 })
123- }
124128
125- return batchesToValidate , nil
129+ // If there are L1 messages in this batch, retrieve L1 messages with block numbers
130+ for _ , chunk := range dbChunks {
131+ if chunk .TotalL1MessagesPoppedInChunk > 0 {
132+ blockWithL1Messages , err := r .l2BlockOrm .GetL2BlocksInRange (r .ctx , chunk .StartBlockNumber , chunk .EndBlockNumber )
133+ if err != nil {
134+ return nil , nil , fmt .Errorf ("failed to get L2 blocks for chunk %d: %w" , chunk .Index , err )
135+ }
136+ var l1MessagesCount uint64
137+ for _ , block := range blockWithL1Messages {
138+ bn := block .Header .Number .Uint64 ()
139+ seenL2 := false
140+ for _ , tx := range block .Transactions {
141+ if tx .Type == types .L1MessageTxType {
142+ if seenL2 {
143+ // Invariant violated: found an L1 message after an L2 transaction in the same block.
144+ return nil , nil , fmt .Errorf ("L1 message after L2 transaction in block %d" , bn )
145+ }
146+ l1MessagesWithBlockNumbers [bn ] = append (l1MessagesWithBlockNumbers [bn ], tx )
147+ l1MessagesCount ++
148+ } else {
149+ seenL2 = true
150+ }
151+ }
152+ }
153+ if chunk .TotalL1MessagesPoppedInChunk != l1MessagesCount {
154+ return nil , nil , fmt .Errorf ("chunk %d has inconsistent L1 messages count: expected %d, got %d" , chunk .Index , chunk .TotalL1MessagesPoppedInChunk , l1MessagesCount )
155+ }
156+ }
157+ }
158+ }
159+ return batchesToValidate , l1MessagesWithBlockNumbers , nil
126160}
127161
128162// validateDatabaseConsistency performs comprehensive validation of database records
@@ -299,7 +333,7 @@ func (r *Layer2Relayer) validateSingleChunkConsistency(chunk *orm.Chunk, prevChu
299333}
300334
301335// validateCalldataAndBlobsAgainstDatabase validates calldata and blobs against database records
302- func (r * Layer2Relayer ) validateCalldataAndBlobsAgainstDatabase (calldataInfo * CalldataInfo , blobs []* kzg4844.Blob , batchesToValidate []* dbBatchWithChunks ) error {
336+ func (r * Layer2Relayer ) validateCalldataAndBlobsAgainstDatabase (calldataInfo * CalldataInfo , blobs []* kzg4844.Blob , batchesToValidate []* dbBatchWithChunks , l1MessagesWithBlockNumbers map [ uint64 ][] * types. TransactionData ) error {
303337 // Validate blobs
304338 if len (blobs ) == 0 {
305339 return fmt .Errorf ("no blobs provided" )
@@ -338,7 +372,7 @@ func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *Ca
338372 // Validate each blob against its corresponding batch
339373 for i , blob := range blobs {
340374 dbBatch := batchesToValidate [i ].Batch
341- if err := r .validateSingleBlobAgainstBatch (blob , dbBatch , codec ); err != nil {
375+ if err := r .validateSingleBlobAgainstBatch (blob , dbBatch , codec , l1MessagesWithBlockNumbers ); err != nil {
342376 return fmt .Errorf ("blob validation failed for batch %d: %w" , dbBatch .Index , err )
343377 }
344378 }
@@ -347,15 +381,15 @@ func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *Ca
347381}
348382
349383// validateSingleBlobAgainstBatch validates a single blob against its batch data
350- func (r * Layer2Relayer ) validateSingleBlobAgainstBatch (blob * kzg4844.Blob , dbBatch * orm.Batch , codec encoding.Codec ) error {
384+ func (r * Layer2Relayer ) validateSingleBlobAgainstBatch (blob * kzg4844.Blob , dbBatch * orm.Batch , codec encoding.Codec , l1MessagesWithBlockNumbers map [ uint64 ][] * types. TransactionData ) error {
351385 // Decode blob payload
352386 payload , err := codec .DecodeBlob (blob )
353387 if err != nil {
354388 return fmt .Errorf ("failed to decode blob: %w" , err )
355389 }
356390
357391 // Validate batch hash
358- daBatch , err := assembleDABatchFromPayload (payload , dbBatch , codec )
392+ daBatch , err := assembleDABatchFromPayload (payload , dbBatch , codec , l1MessagesWithBlockNumbers )
359393 if err != nil {
360394 return fmt .Errorf ("failed to assemble batch from payload: %w" , err )
361395 }
@@ -401,8 +435,8 @@ func (r *Layer2Relayer) validateMessageQueueConsistency(batchIndex uint64, chunk
401435 return nil
402436}
403437
404- func assembleDABatchFromPayload (payload encoding.DABlobPayload , dbBatch * orm.Batch , codec encoding.Codec ) (encoding.DABatch , error ) {
405- blocks , err := assembleBlocksFromPayload (payload )
438+ func assembleDABatchFromPayload (payload encoding.DABlobPayload , dbBatch * orm.Batch , codec encoding.Codec , l1MessagesWithBlockNumbers map [ uint64 ][] * types. TransactionData ) (encoding.DABatch , error ) {
439+ blocks , err := assembleBlocksFromPayload (payload , l1MessagesWithBlockNumbers )
406440 if err != nil {
407441 return nil , fmt .Errorf ("failed to assemble blocks from payload batch_index=%d codec_version=%d parent_batch_hash=%s: %w" , dbBatch .Index , dbBatch .CodecVersion , dbBatch .ParentBatchHash , err )
408442 }
@@ -427,7 +461,7 @@ func assembleDABatchFromPayload(payload encoding.DABlobPayload, dbBatch *orm.Bat
427461 return daBatch , nil
428462}
429463
430- func assembleBlocksFromPayload (payload encoding.DABlobPayload ) ([]* encoding.Block , error ) {
464+ func assembleBlocksFromPayload (payload encoding.DABlobPayload , l1MessagesWithBlockNumbers map [ uint64 ][] * types. TransactionData ) ([]* encoding.Block , error ) {
431465 daBlocks := payload .Blocks ()
432466 txns := payload .Transactions ()
433467 if len (daBlocks ) != len (txns ) {
@@ -442,8 +476,12 @@ func assembleBlocksFromPayload(payload encoding.DABlobPayload) ([]*encoding.Bloc
442476 BaseFee : daBlocks [i ].BaseFee (),
443477 GasLimit : daBlocks [i ].GasLimit (),
444478 },
445- Transactions : encoding .TxsToTxsData (txns [i ]),
446479 }
480+ // Ensure per-block ordering: [L1 messages][L2 transactions]. Prepend L1 messages (if any), then append L2 transactions.
481+ if l1Messages , ok := l1MessagesWithBlockNumbers [daBlocks [i ].Number ()]; ok {
482+ blocks [i ].Transactions = l1Messages
483+ }
484+ blocks [i ].Transactions = append (blocks [i ].Transactions , encoding .TxsToTxsData (txns [i ])... )
447485 }
448486 return blocks , nil
449487}
0 commit comments