Skip to content

Commit 819f4ce

Browse files
author
colinlyguo
committed
fix(rollup-relayer): sanity checks
1 parent c012f71 commit 819f4ce

File tree

4 files changed

+181
-22
lines changed

4 files changed

+181
-22
lines changed

common/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"runtime/debug"
66
)
77

8-
var tag = "v4.5.39"
8+
var tag = "v4.5.40"
99

1010
var commit = func() string {
1111
if info, ok := debug.ReadBuildInfo(); ok {

rollup/internal/controller/relayer/l2_relayer_sanity.go

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
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,17 @@ 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.
1819
func (r *Layer2Relayer) sanityChecksCommitBatchCodecV7CalldataAndBlobs(calldata []byte, blobs []*kzg4844.Blob) error {
19-
calldataInfo, err := r.parseCommitBatchesCalldata(calldata)
20+
calldataInfo, err := parseCommitBatchesCalldata(r.l1RollupABI, calldata)
2021
if err != nil {
2122
return fmt.Errorf("failed to parse calldata: %w", err)
2223
}
2324

24-
batchesToValidate, err := r.getBatchesFromCalldata(calldataInfo)
25+
batchesToValidate, l1MessagesWithBlockNumbers, err := r.getBatchesFromCalldata(calldataInfo)
2526
if err != nil {
2627
return fmt.Errorf("failed to get batches from database: %w", err)
2728
}
2829

29-
if err := r.validateCalldataAndBlobsAgainstDatabase(calldataInfo, blobs, batchesToValidate); err != nil {
30+
if err := r.validateCalldataAndBlobsAgainstDatabase(calldataInfo, blobs, batchesToValidate, l1MessagesWithBlockNumbers); err != nil {
3031
return fmt.Errorf("calldata and blobs validation failed: %w", err)
3132
}
3233

@@ -45,8 +46,8 @@ type CalldataInfo struct {
4546
}
4647

4748
// parseCommitBatchesCalldata parses the commitBatches calldata and extracts key information
48-
func (r *Layer2Relayer) parseCommitBatchesCalldata(calldata []byte) (*CalldataInfo, error) {
49-
method := r.l1RollupABI.Methods["commitBatches"]
49+
func parseCommitBatchesCalldata(abi *abi.ABI, calldata []byte) (*CalldataInfo, error) {
50+
method := abi.Methods["commitBatches"]
5051
decoded, err := method.Inputs.Unpack(calldata[4:])
5152
if err != nil {
5253
return nil, fmt.Errorf("failed to unpack commitBatches calldata: %w", err)
@@ -81,17 +82,17 @@ func (r *Layer2Relayer) parseCommitBatchesCalldata(calldata []byte) (*CalldataIn
8182
}
8283

8384
// getBatchesFromCalldata retrieves the relevant batches from database based on calldata information
84-
func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWithChunks, error) {
85+
func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWithChunks, map[uint64][]*types.TransactionData, error) {
8586
// Get the parent batch to determine the starting point
8687
parentBatch, err := r.batchOrm.GetBatchByHash(r.ctx, info.ParentBatchHash.Hex())
8788
if err != nil {
88-
return nil, fmt.Errorf("failed to get parent batch by hash %s: %w", info.ParentBatchHash.Hex(), err)
89+
return nil, nil, fmt.Errorf("failed to get parent batch by hash %s: %w", info.ParentBatchHash.Hex(), err)
8990
}
9091

9192
// Get the last batch to determine the ending point
9293
lastBatch, err := r.batchOrm.GetBatchByHash(r.ctx, info.LastBatchHash.Hex())
9394
if err != nil {
94-
return nil, fmt.Errorf("failed to get last batch by hash %s: %w", info.LastBatchHash.Hex(), err)
95+
return nil, nil, fmt.Errorf("failed to get last batch by hash %s: %w", info.LastBatchHash.Hex(), err)
9596
}
9697

9798
// Get all batches in the range (parent+1 to last)
@@ -100,29 +101,46 @@ func (r *Layer2Relayer) getBatchesFromCalldata(info *CalldataInfo) ([]*dbBatchWi
100101

101102
// Check if the range is valid
102103
if firstBatchIndex > lastBatchIndex {
103-
return nil, fmt.Errorf("no batches found in range: first index %d, last index %d", firstBatchIndex, lastBatchIndex)
104+
return nil, nil, fmt.Errorf("no batches found in range: first index %d, last index %d", firstBatchIndex, lastBatchIndex)
104105
}
105106

106107
var batchesToValidate []*dbBatchWithChunks
108+
l1MessagesWithBlockNumbers := make(map[uint64][]*types.TransactionData)
107109
for batchIndex := firstBatchIndex; batchIndex <= lastBatchIndex; batchIndex++ {
108110
dbBatch, err := r.batchOrm.GetBatchByIndex(r.ctx, batchIndex)
109111
if err != nil {
110-
return nil, fmt.Errorf("failed to get batch by index %d: %w", batchIndex, err)
112+
return nil, nil, fmt.Errorf("failed to get batch by index %d: %w", batchIndex, err)
111113
}
112114

113115
// Get chunks for this batch
114116
dbChunks, err := r.chunkOrm.GetChunksInRange(r.ctx, dbBatch.StartChunkIndex, dbBatch.EndChunkIndex)
115117
if err != nil {
116-
return nil, fmt.Errorf("failed to get chunks for batch %d: %w", batchIndex, err)
118+
return nil, nil, fmt.Errorf("failed to get chunks for batch %d: %w", batchIndex, err)
117119
}
118120

119121
batchesToValidate = append(batchesToValidate, &dbBatchWithChunks{
120122
Batch: dbBatch,
121123
Chunks: dbChunks,
122124
})
123-
}
124125

125-
return batchesToValidate, nil
126+
// If there are L1 messages in this batch, retrieve L1 messages with block numbers
127+
for _, chunk := range dbChunks {
128+
if chunk.TotalL1MessagesPoppedInChunk > 0 {
129+
blockWithL1Messages, err := r.l2BlockOrm.GetL2BlocksInRange(r.ctx, chunk.StartBlockNumber, chunk.EndBlockNumber)
130+
if err != nil {
131+
return nil, nil, fmt.Errorf("failed to get L2 blocks for chunk %d: %w", chunk.Index, err)
132+
}
133+
for _, block := range blockWithL1Messages {
134+
for _, tx := range block.Transactions {
135+
if tx.Type == types.L1MessageTxType {
136+
l1MessagesWithBlockNumbers[block.Header.Number.Uint64()] = append(l1MessagesWithBlockNumbers[block.Header.Number.Uint64()], tx)
137+
}
138+
}
139+
}
140+
}
141+
}
142+
}
143+
return batchesToValidate, l1MessagesWithBlockNumbers, nil
126144
}
127145

128146
// validateDatabaseConsistency performs comprehensive validation of database records
@@ -299,7 +317,7 @@ func (r *Layer2Relayer) validateSingleChunkConsistency(chunk *orm.Chunk, prevChu
299317
}
300318

301319
// validateCalldataAndBlobsAgainstDatabase validates calldata and blobs against database records
302-
func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *CalldataInfo, blobs []*kzg4844.Blob, batchesToValidate []*dbBatchWithChunks) error {
320+
func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *CalldataInfo, blobs []*kzg4844.Blob, batchesToValidate []*dbBatchWithChunks, l1MessagesWithBlockNumbers map[uint64][]*types.TransactionData) error {
303321
// Validate blobs
304322
if len(blobs) == 0 {
305323
return fmt.Errorf("no blobs provided")
@@ -338,7 +356,7 @@ func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *Ca
338356
// Validate each blob against its corresponding batch
339357
for i, blob := range blobs {
340358
dbBatch := batchesToValidate[i].Batch
341-
if err := r.validateSingleBlobAgainstBatch(blob, dbBatch, codec); err != nil {
359+
if err := r.validateSingleBlobAgainstBatch(blob, dbBatch, codec, l1MessagesWithBlockNumbers); err != nil {
342360
return fmt.Errorf("blob validation failed for batch %d: %w", dbBatch.Index, err)
343361
}
344362
}
@@ -347,15 +365,15 @@ func (r *Layer2Relayer) validateCalldataAndBlobsAgainstDatabase(calldataInfo *Ca
347365
}
348366

349367
// validateSingleBlobAgainstBatch validates a single blob against its batch data
350-
func (r *Layer2Relayer) validateSingleBlobAgainstBatch(blob *kzg4844.Blob, dbBatch *orm.Batch, codec encoding.Codec) error {
368+
func (r *Layer2Relayer) validateSingleBlobAgainstBatch(blob *kzg4844.Blob, dbBatch *orm.Batch, codec encoding.Codec, l1MessagesWithBlockNumbers map[uint64][]*types.TransactionData) error {
351369
// Decode blob payload
352370
payload, err := codec.DecodeBlob(blob)
353371
if err != nil {
354372
return fmt.Errorf("failed to decode blob: %w", err)
355373
}
356374

357375
// Validate batch hash
358-
daBatch, err := assembleDABatchFromPayload(payload, dbBatch, codec)
376+
daBatch, err := assembleDABatchFromPayload(payload, dbBatch, codec, l1MessagesWithBlockNumbers)
359377
if err != nil {
360378
return fmt.Errorf("failed to assemble batch from payload: %w", err)
361379
}
@@ -401,8 +419,8 @@ func (r *Layer2Relayer) validateMessageQueueConsistency(batchIndex uint64, chunk
401419
return nil
402420
}
403421

404-
func assembleDABatchFromPayload(payload encoding.DABlobPayload, dbBatch *orm.Batch, codec encoding.Codec) (encoding.DABatch, error) {
405-
blocks, err := assembleBlocksFromPayload(payload)
422+
func assembleDABatchFromPayload(payload encoding.DABlobPayload, dbBatch *orm.Batch, codec encoding.Codec, l1MessagesWithBlockNumbers map[uint64][]*types.TransactionData) (encoding.DABatch, error) {
423+
blocks, err := assembleBlocksFromPayload(payload, l1MessagesWithBlockNumbers)
406424
if err != nil {
407425
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)
408426
}
@@ -427,7 +445,7 @@ func assembleDABatchFromPayload(payload encoding.DABlobPayload, dbBatch *orm.Bat
427445
return daBatch, nil
428446
}
429447

430-
func assembleBlocksFromPayload(payload encoding.DABlobPayload) ([]*encoding.Block, error) {
448+
func assembleBlocksFromPayload(payload encoding.DABlobPayload, l1MessagesWithBlockNumbers map[uint64][]*types.TransactionData) ([]*encoding.Block, error) {
431449
daBlocks := payload.Blocks()
432450
txns := payload.Transactions()
433451
if len(daBlocks) != len(txns) {
@@ -442,8 +460,11 @@ func assembleBlocksFromPayload(payload encoding.DABlobPayload) ([]*encoding.Bloc
442460
BaseFee: daBlocks[i].BaseFee(),
443461
GasLimit: daBlocks[i].GasLimit(),
444462
},
445-
Transactions: encoding.TxsToTxsData(txns[i]),
446463
}
464+
if l1Messages, ok := l1MessagesWithBlockNumbers[daBlocks[i].Number()]; ok {
465+
blocks[i].Transactions = l1Messages
466+
}
467+
blocks[i].Transactions = append(blocks[i].Transactions, encoding.TxsToTxsData(txns[i])...)
447468
}
448469
return blocks, nil
449470
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package relayer
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"math/big"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
14+
"github.com/scroll-tech/da-codec/encoding"
15+
"github.com/scroll-tech/go-ethereum/common"
16+
"github.com/scroll-tech/go-ethereum/common/hexutil"
17+
"github.com/scroll-tech/go-ethereum/core/types"
18+
"github.com/scroll-tech/go-ethereum/crypto/kzg4844"
19+
20+
bridgeabi "scroll-tech/rollup/abi"
21+
"scroll-tech/rollup/internal/orm"
22+
)
23+
24+
func TestAssembleDABatch(t *testing.T) {
25+
calldataHex := "0x9bbaa2ba0000000000000000000000000000000000000000000000000000000000000008146793a7d71663cd87ec9713f72242a3798d5e801050130a3e16efaa09fb803e58af2593dadc8b9fff75a2d27199cb97ec115bade109b8d691a512608ef180eb"
26+
blobsPath := filepath.Join("../../../testdata", "commit_batches_blobs.json")
27+
28+
calldata, err := hexutil.Decode(strings.TrimSpace(calldataHex))
29+
assert.NoErrorf(t, err, "failed to decode calldata: %s", calldataHex)
30+
31+
blobs, err := loadBlobsFromJSON(blobsPath)
32+
assert.NoErrorf(t, err, "failed to read blobs: %s", blobsPath)
33+
assert.NotEmpty(t, blobs, "no blobs provided")
34+
35+
info, err := parseCommitBatchesCalldata(bridgeabi.ScrollChainABI, calldata)
36+
assert.NoError(t, err)
37+
38+
codec, err := encoding.CodecFromVersion(encoding.CodecVersion(info.Version))
39+
assert.NoErrorf(t, err, "failed to get codec from version %d", info.Version)
40+
41+
parentBatchHash := info.ParentBatchHash
42+
index := uint64(113571)
43+
44+
t.Logf("calldata parsed: version=%d parentBatchHash=%s lastBatchHash=%s blobs=%d", info.Version, info.ParentBatchHash.Hex(), info.LastBatchHash.Hex(), len(blobs))
45+
46+
fromAddr := common.HexToAddress("0x61d8d3e7f7c656493d1d76aaa1a836cedfcbc27b")
47+
toAddr := common.HexToAddress("0xba50f5340fb9f3bd074bd638c9be13ecb36e603d")
48+
l1MessagesWithBlockNumbers := map[uint64][]*types.TransactionData{
49+
11488527: {
50+
&types.TransactionData{
51+
Type: types.L1MessageTxType,
52+
Nonce: 1072515,
53+
Gas: 340000,
54+
To: &toAddr,
55+
Value: (*hexutil.Big)(big.NewInt(0)),
56+
Data: "0x8ef1332e00000000000000000000000081f3843af1fbab046b771f0d440c04ebb2b7513f000000000000000000000000cec03800074d0ac0854bf1f34153cc4c8baeeb1e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105d8300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000084f03efa3700000000000000000000000000000000000000000000000000000000000024730000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000171bdb6e3062daaee1845ba4cb1902169feb5a9b9555a882d45637d3bd29eb83500000000000000000000000000000000000000000000000000000000",
57+
From: fromAddr,
58+
},
59+
},
60+
11488622: {
61+
&types.TransactionData{
62+
Type: types.L1MessageTxType,
63+
Nonce: 1072516,
64+
Gas: 340000,
65+
To: &toAddr,
66+
Value: (*hexutil.Big)(big.NewInt(0)),
67+
Data: "0x8ef1332e00000000000000000000000081f3843af1fbab046b771f0d440c04ebb2b7513f000000000000000000000000cec03800074d0ac0854bf1f34153cc4c8baeeb1e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105d8400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000084f03efa370000000000000000000000000000000000000000000000000000000000002474000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000012aeb01535c1845b689bfce22e53029ec59ec75ea20f660d7c5fcd99f55b75b6900000000000000000000000000000000000000000000000000000000",
68+
From: fromAddr,
69+
},
70+
},
71+
11489190: {
72+
&types.TransactionData{
73+
Type: types.L1MessageTxType,
74+
Nonce: 1072517,
75+
Gas: 168000,
76+
To: &toAddr,
77+
Value: (*hexutil.Big)(big.NewInt(0)),
78+
Data: "0x8ef1332e0000000000000000000000003b1399523f819ea4c4d3e76dddefaf4226c6ba570000000000000000000000003b1399523f819ea4c4d3e76dddefaf4226c6ba5700000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000105d8500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000",
79+
From: fromAddr,
80+
},
81+
},
82+
}
83+
84+
for i, blob := range blobs {
85+
payload, decErr := codec.DecodeBlob(blob)
86+
assert.NoErrorf(t, decErr, "blob[%d] decode failed", i)
87+
if decErr != nil {
88+
continue
89+
}
90+
91+
dbBatch := &orm.Batch{
92+
Index: index,
93+
ParentBatchHash: parentBatchHash.Hex(),
94+
}
95+
96+
daBatch, asmErr := assembleDABatchFromPayload(payload, dbBatch, codec, l1MessagesWithBlockNumbers)
97+
assert.NoErrorf(t, asmErr, "blob[%d] assemble failed", i)
98+
if asmErr == nil {
99+
t.Logf("blob[%d] DABatch hash=%s", i, daBatch.Hash().Hex())
100+
}
101+
102+
index += 1
103+
parentBatchHash = daBatch.Hash()
104+
}
105+
}
106+
107+
func loadBlobsFromJSON(path string) ([]*kzg4844.Blob, error) {
108+
raw, err := os.ReadFile(path)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
var arr []hexutil.Bytes
114+
if err := json.Unmarshal(raw, &arr); err != nil {
115+
return nil, fmt.Errorf("invalid JSON; expect [\"0x...\"] array: %w", err)
116+
}
117+
118+
out := make([]*kzg4844.Blob, 0, len(arr))
119+
var empty kzg4844.Blob
120+
want := len(empty)
121+
122+
for i, b := range arr {
123+
if len(b) != want {
124+
return nil, fmt.Errorf("blob[%d] length mismatch: got %d, want %d", i, len(b), want)
125+
}
126+
blob := new(kzg4844.Blob)
127+
copy(blob[:], b)
128+
out = append(out, blob)
129+
}
130+
return out, nil
131+
}

rollup/testdata/commit_batches_blobs.json

Lines changed: 7 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)