Skip to content

Commit 3f9d9cc

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

File tree

1 file changed

+99
-90
lines changed

1 file changed

+99
-90
lines changed

rollup/internal/controller/relayer/l2_relayer_sanity.go

Lines changed: 99 additions & 90 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"
@@ -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.
1618
func (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

Comments
 (0)