Skip to content

Commit 1434d05

Browse files
authored
Merge branch 'develop' into bridge-history-add-s3-client
2 parents 1e618ad + ae791a0 commit 1434d05

File tree

3 files changed

+197
-21
lines changed

3 files changed

+197
-21
lines changed

rollup/internal/controller/relayer/l2_relayer_sanity.go

Lines changed: 59 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,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.
1819
func (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
}
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)