@@ -2,9 +2,11 @@ package relayer
22
33import (
44 "fmt"
5+ "math/big"
56
67 "github.com/scroll-tech/da-codec/encoding"
78 "github.com/scroll-tech/go-ethereum/common"
9+ "github.com/scroll-tech/go-ethereum/core/types"
810 "github.com/scroll-tech/go-ethereum/crypto/kzg4844"
911
1012 "scroll-tech/rollup/internal/orm"
@@ -14,36 +16,24 @@ import (
1416// transaction data (calldata and blobs) by parsing them and comparing against database records.
1517// This ensures the constructed transaction data is correct and consistent with the database state.
1618func (r * Layer2Relayer ) sanityChecksCommitBatchCodecV7CalldataAndBlobs (calldata []byte , blobs []* kzg4844.Blob ) error {
17- if len (blobs ) == 0 {
18- return fmt .Errorf ("no blobs provided" )
19- }
20-
2119 calldataInfo , err := r .parseCommitBatchesCalldata (calldata )
2220 if err != nil {
2321 return fmt .Errorf ("failed to parse calldata: %w" , err )
2422 }
2523
26- batchesToValidate , firstBatch , lastBatch , err := r .getBatchesFromCalldata (calldataInfo )
24+ batchesToValidate , err := r .getBatchesFromCalldata (calldataInfo )
2725 if err != nil {
2826 return fmt .Errorf ("failed to get batches from database: %w" , err )
2927 }
3028
31- if len (blobs ) != len (batchesToValidate ) {
32- return fmt .Errorf ("blob count mismatch: got %d blobs, expected %d batches" , len (blobs ), len (batchesToValidate ))
33- }
34-
35- if err := r .validateCalldataAgainstDatabase (calldataInfo , firstBatch , lastBatch ); err != nil {
36- return fmt .Errorf ("calldata validation failed: %w" , err )
29+ if err := r .validateCalldataAndBlobsAgainstDatabase (calldataInfo , blobs , batchesToValidate ); err != nil {
30+ return fmt .Errorf ("calldata and blobs validation failed: %w" , err )
3731 }
3832
3933 if err := r .validateDatabaseConsistency (batchesToValidate ); err != nil {
4034 return fmt .Errorf ("database consistency validation failed: %w" , err )
4135 }
4236
43- if err := r .validateBlobsAgainstDatabase (blobs , batchesToValidate ); err != nil {
44- return fmt .Errorf ("blob validation failed: %w" , err )
45- }
46-
4737 return nil
4838}
4939
@@ -91,17 +81,17 @@ func (r *Layer2Relayer) parseCommitBatchesCalldata(calldata []byte) (*CalldataIn
9181}
9282
9383// getBatchesFromCalldata retrieves the relevant batches from database based on calldata information
94- func (r * Layer2Relayer ) getBatchesFromCalldata (info * CalldataInfo ) ([]* dbBatchWithChunks , * orm. Batch , * orm. Batch , error ) {
84+ func (r * Layer2Relayer ) getBatchesFromCalldata (info * CalldataInfo ) ([]* dbBatchWithChunks , error ) {
9585 // Get the parent batch to determine the starting point
9686 parentBatch , err := r .batchOrm .GetBatchByHash (r .ctx , info .ParentBatchHash .Hex ())
9787 if err != nil {
98- return nil , nil , nil , fmt .Errorf ("failed to get parent batch by hash %s: %w" , info .ParentBatchHash .Hex (), err )
88+ return nil , fmt .Errorf ("failed to get parent batch by hash %s: %w" , info .ParentBatchHash .Hex (), err )
9989 }
10090
10191 // Get the last batch to determine the ending point
10292 lastBatch , err := r .batchOrm .GetBatchByHash (r .ctx , info .LastBatchHash .Hex ())
10393 if err != nil {
104- return nil , nil , nil , fmt .Errorf ("failed to get last batch by hash %s: %w" , info .LastBatchHash .Hex (), err )
94+ return nil , fmt .Errorf ("failed to get last batch by hash %s: %w" , info .LastBatchHash .Hex (), err )
10595 }
10696
10797 // Get all batches in the range (parent+1 to last)
@@ -110,20 +100,20 @@ func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWi
110100
111101 // Check if the range is valid
112102 if firstBatchIndex > lastBatchIndex {
113- return nil , nil , nil , fmt .Errorf ("no batches found in range: first index %d, last index %d" , firstBatchIndex , lastBatchIndex )
103+ return nil , fmt .Errorf ("no batches found in range: first index %d, last index %d" , firstBatchIndex , lastBatchIndex )
114104 }
115105
116106 var batchesToValidate []* dbBatchWithChunks
117107 for batchIndex := firstBatchIndex ; batchIndex <= lastBatchIndex ; batchIndex ++ {
118108 dbBatch , err := r .batchOrm .GetBatchByIndex (r .ctx , batchIndex )
119109 if err != nil {
120- return nil , nil , nil , fmt .Errorf ("failed to get batch by index %d: %w" , batchIndex , err )
110+ return nil , fmt .Errorf ("failed to get batch by index %d: %w" , batchIndex , err )
121111 }
122112
123113 // Get chunks for this batch
124114 dbChunks , err := r .chunkOrm .GetChunksInRange (r .ctx , dbBatch .StartChunkIndex , dbBatch .EndChunkIndex )
125115 if err != nil {
126- return nil , nil , nil , fmt .Errorf ("failed to get chunks for batch %d: %w" , batchIndex , err )
116+ return nil , fmt .Errorf ("failed to get chunks for batch %d: %w" , batchIndex , err )
127117 }
128118
129119 batchesToValidate = append (batchesToValidate , & dbBatchWithChunks {
@@ -132,30 +122,7 @@ func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWi
132122 })
133123 }
134124
135- // Get first batch for return
136- firstBatch := batchesToValidate [0 ].Batch
137-
138- return batchesToValidate , firstBatch , lastBatch , nil
139- }
140-
141- // validateCalldataAgainstDatabase validates calldata parameters against database records
142- func (r * Layer2Relayer ) validateCalldataAgainstDatabase (info * CalldataInfo , firstBatch , lastBatch * orm.Batch ) error {
143- // Validate codec version
144- if info .Version != uint8 (firstBatch .CodecVersion ) {
145- return fmt .Errorf ("version mismatch: calldata=%d, db=%d" , info .Version , firstBatch .CodecVersion )
146- }
147-
148- // Validate parent batch hash
149- if info .ParentBatchHash != common .HexToHash (firstBatch .ParentBatchHash ) {
150- return fmt .Errorf ("parentBatchHash mismatch: calldata=%s, db=%s" , info .ParentBatchHash .Hex (), firstBatch .ParentBatchHash )
151- }
152-
153- // Validate last batch hash
154- if info .LastBatchHash != common .HexToHash (lastBatch .Hash ) {
155- return fmt .Errorf ("lastBatchHash mismatch: calldata=%s, db=%s" , info .LastBatchHash .Hex (), lastBatch .Hash )
156- }
157-
158- return nil
125+ return batchesToValidate , nil
159126}
160127
161128// validateDatabaseConsistency performs comprehensive validation of database records
@@ -328,10 +295,38 @@ func (r *Layer2Relayer) validateSingleChunkConsistency(chunk *orm.Chunk, prevChu
328295 return nil
329296}
330297
331- // validateBlobsAgainstDatabase validates blobs against database records
332- func (r * Layer2Relayer ) validateBlobsAgainstDatabase (blobs []* kzg4844.Blob , batchesToValidate []* dbBatchWithChunks ) error {
333- // Get codec for blob decoding
298+ // validateCalldataAndBlobsAgainstDatabase validates calldata and blobs against database records
299+ func (r * Layer2Relayer ) validateCalldataAndBlobsAgainstDatabase (calldataInfo * CalldataInfo , blobs []* kzg4844.Blob , batchesToValidate []* dbBatchWithChunks ) error {
300+ // Validate blobs
301+ if len (blobs ) == 0 {
302+ return fmt .Errorf ("no blobs provided" )
303+ }
304+
305+ // Validate blob count
306+ if len (blobs ) != len (batchesToValidate ) {
307+ return fmt .Errorf ("blob count mismatch: got %d blobs, expected %d batches" , len (blobs ), len (batchesToValidate ))
308+ }
309+
310+ // Get first and last batches for validation, length check is already done above
334311 firstBatch := batchesToValidate [0 ].Batch
312+ lastBatch := batchesToValidate [len (batchesToValidate )- 1 ].Batch
313+
314+ // Validate codec version
315+ if calldataInfo .Version != uint8 (firstBatch .CodecVersion ) {
316+ return fmt .Errorf ("version mismatch: calldata=%d, db=%d" , calldataInfo .Version , firstBatch .CodecVersion )
317+ }
318+
319+ // Validate parent batch hash
320+ if calldataInfo .ParentBatchHash != common .HexToHash (firstBatch .ParentBatchHash ) {
321+ return fmt .Errorf ("parentBatchHash mismatch: calldata=%s, db=%s" , calldataInfo .ParentBatchHash .Hex (), firstBatch .ParentBatchHash )
322+ }
323+
324+ // Validate last batch hash
325+ if calldataInfo .LastBatchHash != common .HexToHash (lastBatch .Hash ) {
326+ return fmt .Errorf ("lastBatchHash mismatch: calldata=%s, db=%s" , calldataInfo .LastBatchHash .Hex (), lastBatch .Hash )
327+ }
328+
329+ // Get codec for blob decoding
335330 codec , err := encoding .CodecFromVersion (encoding .CodecVersion (firstBatch .CodecVersion ))
336331 if err != nil {
337332 return fmt .Errorf ("failed to get codec: %w" , err )
@@ -340,9 +335,7 @@ func (r *Layer2Relayer) validateBlobsAgainstDatabase(blobs []*kzg4844.Blob, batc
340335 // Validate each blob against its corresponding batch
341336 for i , blob := range blobs {
342337 dbBatch := batchesToValidate [i ].Batch
343- dbChunks := batchesToValidate [i ].Chunks
344-
345- if err := r .validateSingleBlobAgainstBatch (blob , dbBatch , dbChunks , codec ); err != nil {
338+ if err := r .validateSingleBlobAgainstBatch (calldataInfo , blob , dbBatch , codec ); err != nil {
346339 return fmt .Errorf ("blob validation failed for batch %d: %w" , dbBatch .Index , err )
347340 }
348341 }
@@ -351,53 +344,21 @@ func (r *Layer2Relayer) validateBlobsAgainstDatabase(blobs []*kzg4844.Blob, batc
351344}
352345
353346// validateSingleBlobAgainstBatch validates a single blob against its batch data
354- func (r * Layer2Relayer ) validateSingleBlobAgainstBatch (blob * kzg4844.Blob , dbBatch * orm.Batch , dbChunks []* orm.Chunk , codec encoding.Codec ) error {
355- // Collect all blocks for the batch
356- var batchBlocks []* encoding.Block
357- for _ , c := range dbChunks {
358- blocks , err := r .l2BlockOrm .GetL2BlocksInRange (r .ctx , c .StartBlockNumber , c .EndBlockNumber )
359- if err != nil {
360- return fmt .Errorf ("failed to get blocks for chunk %d: %w" , c .Index , err )
361- }
362-
363- if len (blocks ) == 0 {
364- return fmt .Errorf ("chunk %d has no blocks in range [%d, %d]" , c .Index , c .StartBlockNumber , c .EndBlockNumber )
365- }
366-
367- // Verify block count matches expected range
368- expectedBlockCount := c .EndBlockNumber - c .StartBlockNumber + 1
369- if len (blocks ) != int (expectedBlockCount ) {
370- return fmt .Errorf ("chunk %d expected %d blocks but got %d" , c .Index , expectedBlockCount , len (blocks ))
371- }
372-
373- batchBlocks = append (batchBlocks , blocks ... )
374- }
375-
347+ func (r * Layer2Relayer ) validateSingleBlobAgainstBatch (calldataInfo * CalldataInfo , blob * kzg4844.Blob , dbBatch * orm.Batch , codec encoding.Codec ) error {
376348 // Decode blob payload
377349 payload , err := codec .DecodeBlob (blob )
378350 if err != nil {
379351 return fmt .Errorf ("failed to decode blob: %w" , err )
380352 }
381353
382- // Validate L1 message queue hashes
383- if payload .PrevL1MessageQueueHash () != common .HexToHash (dbBatch .PrevL1MessageQueueHash ) {
384- return fmt .Errorf ("prevL1MessageQueueHash mismatch: decoded=%s, db=%s" , payload .PrevL1MessageQueueHash ().Hex (), dbBatch .PrevL1MessageQueueHash )
385- }
386-
387- if payload .PostL1MessageQueueHash () != common .HexToHash (dbBatch .PostL1MessageQueueHash ) {
388- return fmt .Errorf ("postL1MessageQueueHash mismatch: decoded=%s, db=%s" , payload .PostL1MessageQueueHash ().Hex (), dbBatch .PostL1MessageQueueHash )
389- }
390-
391- // Validate block data
392- decodedBlocks := payload .Blocks ()
393- if len (decodedBlocks ) != len (batchBlocks ) {
394- return fmt .Errorf ("block count mismatch: decoded=%d, db=%d" , len (decodedBlocks ), len (batchBlocks ))
354+ // Validate batch hash
355+ daBatch , err := assembleDABatchFromPayload (calldataInfo , payload , dbBatch , codec )
356+ if err != nil {
357+ return fmt .Errorf ("failed to assemble batch from payload: %w" , err )
395358 }
396359
397- for j , dbBlock := range batchBlocks {
398- if decodedBlocks [j ].Number () != dbBlock .Header .Number .Uint64 () {
399- return fmt .Errorf ("block number mismatch at index %d: decoded=%d, db=%d" , j , decodedBlocks [j ].Number (), dbBlock .Header .Number .Uint64 ())
400- }
360+ if daBatch .Hash () != common .HexToHash (dbBatch .Hash ) {
361+ return fmt .Errorf ("batch hash mismatch: decoded from blob=%s, db=%s" , daBatch .Hash ().Hex (), dbBatch .Hash )
401362 }
402363
403364 return nil
@@ -436,3 +397,51 @@ func (r *Layer2Relayer) validateMessageQueueConsistency(batchIndex uint64, chunk
436397
437398 return nil
438399}
400+
401+ func assembleDABatchFromPayload (calldataInfo * CalldataInfo , payload encoding.DABlobPayload , dbBatch * orm.Batch , codec encoding.Codec ) (encoding.DABatch , error ) {
402+ blocks , err := assembleBlocksFromPayload (payload )
403+ if err != nil {
404+ 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 , calldataInfo .ParentBatchHash .Hex (), err )
405+ }
406+ parentBatchHash := calldataInfo .ParentBatchHash
407+ batch := & encoding.Batch {
408+ Index : dbBatch .Index , // The database provides only batch index, other fields are derived from blob payload
409+ ParentBatchHash : parentBatchHash ,
410+ PrevL1MessageQueueHash : payload .PrevL1MessageQueueHash (),
411+ PostL1MessageQueueHash : payload .PostL1MessageQueueHash (),
412+ Blocks : blocks ,
413+ Chunks : []* encoding.Chunk { // One chunk for this batch to pass sanity checks when building DABatch
414+ {
415+ Blocks : blocks ,
416+ PrevL1MessageQueueHash : payload .PrevL1MessageQueueHash (),
417+ PostL1MessageQueueHash : payload .PostL1MessageQueueHash (),
418+ },
419+ },
420+ }
421+ daBatch , err := codec .NewDABatch (batch )
422+ if err != nil {
423+ return nil , fmt .Errorf ("failed to build DABatch batch_index=%d codec_version=%d parent_batch_hash=%s: %w" , dbBatch .Index , dbBatch .CodecVersion , calldataInfo .ParentBatchHash .Hex (), err )
424+ }
425+ return daBatch , nil
426+ }
427+
428+ func assembleBlocksFromPayload (payload encoding.DABlobPayload ) ([]* encoding.Block , error ) {
429+ daBlocks := payload .Blocks ()
430+ txss := payload .Transactions ()
431+ if len (daBlocks ) != len (txss ) {
432+ return nil , fmt .Errorf ("mismatched number of blocks and transactions: %d blocks, %d transactions" , len (daBlocks ), len (txss ))
433+ }
434+ blocks := make ([]* encoding.Block , len (daBlocks ))
435+ for i := range daBlocks {
436+ blocks [i ] = & encoding.Block {
437+ Header : & types.Header {
438+ Number : new (big.Int ).SetUint64 (daBlocks [i ].Number ()),
439+ Time : daBlocks [i ].Timestamp (),
440+ BaseFee : daBlocks [i ].BaseFee (),
441+ GasLimit : daBlocks [i ].GasLimit (),
442+ },
443+ Transactions : encoding .TxsToTxsData (txss [i ]),
444+ }
445+ }
446+ return blocks , nil
447+ }
0 commit comments