Skip to content

Commit f4bc536

Browse files
author
colinlyguo
committed
compare the batch hashes of the calldata + blob hash vs database
1 parent be22866 commit f4bc536

File tree

1 file changed

+82
-75
lines changed

1 file changed

+82
-75
lines changed

rollup/internal/controller/relayer/l2_relayer_sanity.go

Lines changed: 82 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package relayer
22

33
import (
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"
@@ -28,22 +30,14 @@ func (r *Layer2Relayer) sanityChecksCommitBatchCodecV7CalldataAndBlobs(calldata
2830
return fmt.Errorf("failed to get batches from database: %w", err)
2931
}
3032

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)
33+
if err := r.validateCalldataAndBlobsAgainstDatabase(calldataInfo, blobs, batchesToValidate, firstBatch, lastBatch); err != nil {
34+
return fmt.Errorf("calldata and blobs validation failed: %w", err)
3735
}
3836

3937
if err := r.validateDatabaseConsistency(batchesToValidate); err != nil {
4038
return fmt.Errorf("database consistency validation failed: %w", err)
4139
}
4240

43-
if err := r.validateBlobsAgainstDatabase(blobs, batchesToValidate); err != nil {
44-
return fmt.Errorf("blob validation failed: %w", err)
45-
}
46-
4741
return nil
4842
}
4943

@@ -138,26 +132,6 @@ func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWi
138132
return batchesToValidate, firstBatch, lastBatch, nil
139133
}
140134

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
159-
}
160-
161135
// validateDatabaseConsistency performs comprehensive validation of database records
162136
func (r *Layer2Relayer) validateDatabaseConsistency(batchesToValidate []*dbBatchWithChunks) error {
163137
if len(batchesToValidate) == 0 {
@@ -328,10 +302,29 @@ func (r *Layer2Relayer) validateSingleChunkConsistency(chunk *orm.Chunk, prevChu
328302
return nil
329303
}
330304

331-
// validateBlobsAgainstDatabase validates blobs against database records
332-
func (r *Layer2Relayer) validateBlobsAgainstDatabase(blobs []*kzg4844.Blob, batchesToValidate []*dbBatchWithChunks) error {
305+
// validateCalldataAndBlobsAgainstDatabase validates calldata and blobs against database records
306+
func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *CalldataInfo, blobs []*kzg4844.Blob, batchesToValidate []*dbBatchWithChunks, firstBatch, lastBatch *orm.Batch) error {
307+
// Validate blob count
308+
if len(blobs) != len(batchesToValidate) {
309+
return fmt.Errorf("blob count mismatch: got %d blobs, expected %d batches", len(blobs), len(batchesToValidate))
310+
}
311+
312+
// Validate codec version
313+
if calldataInfo.Version != uint8(firstBatch.CodecVersion) {
314+
return fmt.Errorf("version mismatch: calldata=%d, db=%d", calldataInfo.Version, firstBatch.CodecVersion)
315+
}
316+
317+
// Validate parent batch hash
318+
if calldataInfo.ParentBatchHash != common.HexToHash(firstBatch.ParentBatchHash) {
319+
return fmt.Errorf("parentBatchHash mismatch: calldata=%s, db=%s", calldataInfo.ParentBatchHash.Hex(), firstBatch.ParentBatchHash)
320+
}
321+
322+
// Validate last batch hash
323+
if calldataInfo.LastBatchHash != common.HexToHash(lastBatch.Hash) {
324+
return fmt.Errorf("lastBatchHash mismatch: calldata=%s, db=%s", calldataInfo.LastBatchHash.Hex(), lastBatch.Hash)
325+
}
326+
333327
// Get codec for blob decoding
334-
firstBatch := batchesToValidate[0].Batch
335328
codec, err := encoding.CodecFromVersion(encoding.CodecVersion(firstBatch.CodecVersion))
336329
if err != nil {
337330
return fmt.Errorf("failed to get codec: %w", err)
@@ -340,9 +333,7 @@ func (r *Layer2Relayer) validateBlobsAgainstDatabase(blobs []*kzg4844.Blob, batc
340333
// Validate each blob against its corresponding batch
341334
for i, blob := range blobs {
342335
dbBatch := batchesToValidate[i].Batch
343-
dbChunks := batchesToValidate[i].Chunks
344-
345-
if err := r.validateSingleBlobAgainstBatch(blob, dbBatch, dbChunks, codec); err != nil {
336+
if err := r.validateSingleBlobAgainstBatch(calldataInfo, blob, dbBatch, codec); err != nil {
346337
return fmt.Errorf("blob validation failed for batch %d: %w", dbBatch.Index, err)
347338
}
348339
}
@@ -351,53 +342,21 @@ func (r *Layer2Relayer) validateBlobsAgainstDatabase(blobs []*kzg4844.Blob, batc
351342
}
352343

353344
// 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-
345+
func (r *Layer2Relayer) validateSingleBlobAgainstBatch(calldataInfo *CalldataInfo, blob *kzg4844.Blob, dbBatch *orm.Batch, codec encoding.Codec) error {
376346
// Decode blob payload
377347
payload, err := codec.DecodeBlob(blob)
378348
if err != nil {
379349
return fmt.Errorf("failed to decode blob: %w", err)
380350
}
381351

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)
352+
// Validate batch hash
353+
daBatch, err := assembleDABatchFromPayload(calldataInfo, payload, dbBatch, codec)
354+
if err != nil {
355+
return fmt.Errorf("failed to assemble batch from payload: %w", err)
389356
}
390357

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))
395-
}
396-
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-
}
358+
if daBatch.Hash() != common.HexToHash(dbBatch.Hash) {
359+
return fmt.Errorf("batch hash mismatch: decoded from blob=%s, db=%s", daBatch.Hash().Hex(), dbBatch.Hash)
401360
}
402361

403362
return nil
@@ -436,3 +395,51 @@ func (r *Layer2Relayer) validateMessageQueueConsistency(batchIndex uint64, chunk
436395

437396
return nil
438397
}
398+
399+
func assembleDABatchFromPayload(calldataInfo *CalldataInfo, payload encoding.DABlobPayload, dbBatch *orm.Batch, codec encoding.Codec) (encoding.DABatch, error) {
400+
blocks, err := assembleBlocksFromPayload(payload)
401+
if err != nil {
402+
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)
403+
}
404+
parentBatchHash := calldataInfo.ParentBatchHash
405+
batch := &encoding.Batch{
406+
Index: dbBatch.Index, // The database provides only batch index, other fields are derived from blob payload
407+
ParentBatchHash: parentBatchHash,
408+
PrevL1MessageQueueHash: payload.PrevL1MessageQueueHash(),
409+
PostL1MessageQueueHash: payload.PostL1MessageQueueHash(),
410+
Blocks: blocks,
411+
Chunks: []*encoding.Chunk{ // One chunk for this batch to pass sanity checks when building DABatch
412+
{
413+
Blocks: blocks,
414+
PrevL1MessageQueueHash: payload.PrevL1MessageQueueHash(),
415+
PostL1MessageQueueHash: payload.PostL1MessageQueueHash(),
416+
},
417+
},
418+
}
419+
daBatch, err := codec.NewDABatch(batch)
420+
if err != nil {
421+
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)
422+
}
423+
return daBatch, nil
424+
}
425+
426+
func assembleBlocksFromPayload(payload encoding.DABlobPayload) ([]*encoding.Block, error) {
427+
daBlocks := payload.Blocks()
428+
txss := payload.Transactions()
429+
if len(daBlocks) != len(txss) {
430+
return nil, fmt.Errorf("mismatched number of blocks and transactions: %d blocks, %d transactions", len(daBlocks), len(txss))
431+
}
432+
blocks := make([]*encoding.Block, len(daBlocks))
433+
for i := range daBlocks {
434+
blocks[i] = &encoding.Block{
435+
Header: &types.Header{
436+
Number: new(big.Int).SetUint64(daBlocks[i].Number()),
437+
Time: daBlocks[i].Timestamp(),
438+
BaseFee: daBlocks[i].BaseFee(),
439+
GasLimit: daBlocks[i].GasLimit(),
440+
},
441+
Transactions: encoding.TxsToTxsData(txss[i]),
442+
}
443+
}
444+
return blocks, nil
445+
}

0 commit comments

Comments
 (0)