Skip to content

Commit b7c5ba9

Browse files
jonastheisomerfirmaknoel2004colinlyguoroynalnaruto
authored
feat(relayer): adapt to CodecV7 for EuclidV2 (#1583)
Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com> Co-authored-by: noelwei <fan@scroll.io> Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: Rohit Narurkar <rohit.narurkar@proton.me> Co-authored-by: colinlyguo <colinlyguo@scroll.io> Co-authored-by: Péter Garamvölgyi <peter@scroll.io> Co-authored-by: Morty <70688412+yiweichi@users.noreply.github.com> Co-authored-by: omerfirmak <omerfirmak@users.noreply.github.com> Co-authored-by: jonastheis <jonastheis@users.noreply.github.com> Co-authored-by: georgehao <georgehao@users.noreply.github.com>
1 parent 5f79735 commit b7c5ba9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1547
-234
lines changed

bridge-history-api/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ reset-env:
3737
go build -o $(PWD)/build/bin/bridgehistoryapi-db-cli ./cmd/db_cli && $(PWD)/build/bin/bridgehistoryapi-db-cli reset
3838

3939
bridgehistoryapi-docker:
40-
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-fetcher:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-fetcher.Dockerfile
41-
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-api:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-api.Dockerfile
42-
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-db-cli:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-db-cli.Dockerfile
40+
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-fetcher:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-fetcher.Dockerfile --platform=linux/amd64
41+
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-api:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-api.Dockerfile --platform=linux/amd64
42+
DOCKER_BUILDKIT=1 docker build -t scrolltech/bridgehistoryapi-db-cli:${IMAGE_VERSION} ${REPO_ROOT_DIR}/ -f ${REPO_ROOT_DIR}/build/dockerfiles/bridgehistoryapi-db-cli.Dockerfile --platform=linux/amd64

bridge-history-api/abi/backend_abi.go

Lines changed: 11 additions & 4 deletions
Large diffs are not rendered by default.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package backendabi
2+
3+
import (
4+
"testing"
5+
6+
"github.com/scroll-tech/go-ethereum/crypto"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestEventSignatures(t *testing.T) {
11+
assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,bytes32)")), L1RevertBatchV0EventSig)
12+
assert.Equal(t, crypto.Keccak256Hash([]byte("RevertBatch(uint256,uint256)")), L1RevertBatchV7EventSig)
13+
}

bridge-history-api/cmd/fetcher/app/app.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ func action(ctx *cli.Context) error {
6868

6969
observability.Server(ctx, db)
7070

71-
l1MessageFetcher := fetcher.NewL1MessageFetcher(subCtx, cfg.L1, db, l1Client)
71+
l1MessageFetcher, err := fetcher.NewL1MessageFetcher(subCtx, cfg.L1, db, l1Client)
72+
if err != nil {
73+
log.Crit("failed to create L1MessageFetcher", "err", err)
74+
}
7275
go l1MessageFetcher.Start()
7376

7477
l2MessageFetcher := fetcher.NewL2MessageFetcher(subCtx, cfg.L2, db, l2Client)

bridge-history-api/conf/config.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
"ScrollChainAddr": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556",
2020
"GatewayRouterAddr": "0xF8B1378579659D8F7EE5f3C929c2f3E332E41Fd6",
2121
"MessageQueueAddr": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B",
22+
"MessageQueueV2Addr": "0x0000000000000000000000000000000000000000",
2223
"BatchBridgeGatewayAddr": "0x5Bcfd99c34cf7E06fc756f6f5aE7400504852bc4",
2324
"GasTokenGatewayAddr": "0x0000000000000000000000000000000000000000",
24-
"WrappedTokenGatewayAddr": "0x0000000000000000000000000000000000000000"
25+
"WrappedTokenGatewayAddr": "0x0000000000000000000000000000000000000000",
26+
"BlobScanAPIEndpoint": "https://api.blobscan.com/blobs/"
2527
},
2628
"L2": {
2729
"confirmation": 0,

bridge-history-api/go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ require (
1010
github.com/go-redis/redis/v8 v8.11.5
1111
github.com/pressly/goose/v3 v3.16.0
1212
github.com/prometheus/client_golang v1.19.0
13-
github.com/scroll-tech/go-ethereum v1.10.14-0.20250305084331-57148478e950 // It's a hotfix for the header hash incompatibility issue, pls change this with caution
13+
github.com/scroll-tech/da-codec v0.1.3-0.20250226072559-f8a8d3898f54
14+
github.com/scroll-tech/go-ethereum v1.10.14-0.20250305084331-57148478e950
1415
github.com/stretchr/testify v1.9.0
1516
github.com/urfave/cli/v2 v2.25.7
1617
golang.org/x/sync v0.11.0
1718
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde
1819
)
1920

21+
replace github.com/scroll-tech/go-ethereum => github.com/scroll-tech/go-ethereum v1.10.14-0.20250305084331-57148478e950 // It's a hotfix for the header hash incompatibility issue, pls change this with caution
22+
2023
require (
2124
dario.cat/mergo v1.0.0 // indirect
2225
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
@@ -90,7 +93,6 @@ require (
9093
github.com/rjeczalik/notify v0.9.1 // indirect
9194
github.com/rs/cors v1.7.0 // indirect
9295
github.com/russross/blackfriday/v2 v2.1.0 // indirect
93-
github.com/scroll-tech/da-codec v0.1.3-0.20250226072559-f8a8d3898f54 // indirect
9496
github.com/scroll-tech/zktrie v0.8.4 // indirect
9597
github.com/sethvargo/go-retry v0.2.4 // indirect
9698
github.com/shirou/gopsutil v3.21.11+incompatible // indirect

bridge-history-api/internal/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@ type FetcherConfig struct {
3030
ScrollChainAddr string `json:"ScrollChainAddr"`
3131
GatewayRouterAddr string `json:"GatewayRouterAddr"`
3232
MessageQueueAddr string `json:"MessageQueueAddr"`
33+
MessageQueueV2Addr string `json:"MessageQueueV2Addr"`
3334
BatchBridgeGatewayAddr string `json:"BatchBridgeGatewayAddr"`
3435
GasTokenGatewayAddr string `json:"GasTokenGatewayAddr"`
3536
WrappedTokenGatewayAddr string `json:"WrappedTokenGatewayAddr"`
37+
38+
BeaconNodeAPIEndpoint string `json:"BeaconNodeAPIEndpoint"`
39+
BlobScanAPIEndpoint string `json:"BlobScanAPIEndpoint"`
40+
BlockNativeAPIEndpoint string `json:"BlockNativeAPIEndpoint"`
3641
}
3742

3843
// RedisConfig redis config

bridge-history-api/internal/controller/fetcher/l1_fetcher.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fetcher
22

33
import (
44
"context"
5+
"fmt"
56
"math/big"
67
"time"
78

@@ -10,6 +11,7 @@ import (
1011
"github.com/scroll-tech/go-ethereum/common"
1112
"github.com/scroll-tech/go-ethereum/ethclient"
1213
"github.com/scroll-tech/go-ethereum/log"
14+
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/blob_client"
1315
"gorm.io/gorm"
1416

1517
"scroll-tech/bridge-history-api/internal/config"
@@ -35,13 +37,32 @@ type L1MessageFetcher struct {
3537
}
3638

3739
// NewL1MessageFetcher creates a new L1MessageFetcher instance.
38-
func NewL1MessageFetcher(ctx context.Context, cfg *config.FetcherConfig, db *gorm.DB, client *ethclient.Client) *L1MessageFetcher {
40+
func NewL1MessageFetcher(ctx context.Context, cfg *config.FetcherConfig, db *gorm.DB, client *ethclient.Client) (*L1MessageFetcher, error) {
41+
blobClient := blob_client.NewBlobClients()
42+
if cfg.BeaconNodeAPIEndpoint != "" {
43+
beaconNodeClient, err := blob_client.NewBeaconNodeClient(cfg.BeaconNodeAPIEndpoint)
44+
if err != nil {
45+
log.Warn("failed to create BeaconNodeClient", "err", err)
46+
} else {
47+
blobClient.AddBlobClient(beaconNodeClient)
48+
}
49+
}
50+
if cfg.BlobScanAPIEndpoint != "" {
51+
blobClient.AddBlobClient(blob_client.NewBlobScanClient(cfg.BlobScanAPIEndpoint))
52+
}
53+
if cfg.BlockNativeAPIEndpoint != "" {
54+
blobClient.AddBlobClient(blob_client.NewBlockNativeClient(cfg.BlockNativeAPIEndpoint))
55+
}
56+
if blobClient.Size() == 0 {
57+
return nil, fmt.Errorf("no blob client is configured")
58+
}
59+
3960
c := &L1MessageFetcher{
4061
ctx: ctx,
4162
cfg: cfg,
4263
client: client,
4364
eventUpdateLogic: logic.NewEventUpdateLogic(db, true),
44-
l1FetcherLogic: logic.NewL1FetcherLogic(cfg, db, client),
65+
l1FetcherLogic: logic.NewL1FetcherLogic(cfg, db, client, blobClient),
4566
}
4667

4768
reg := prometheus.DefaultRegisterer
@@ -58,7 +79,7 @@ func NewL1MessageFetcher(ctx context.Context, cfg *config.FetcherConfig, db *gor
5879
Help: "Latest blockchain height the L1 message fetcher has synced with.",
5980
})
6081

61-
return c
82+
return c, nil
6283
}
6384

6485
// Start starts the L1 message fetching process.

bridge-history-api/internal/logic/l1_event_parser.go

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package logic
22

33
import (
44
"context"
5+
"fmt"
56
"math/big"
67

8+
"github.com/scroll-tech/da-codec/encoding"
79
"github.com/scroll-tech/go-ethereum/common"
810
"github.com/scroll-tech/go-ethereum/core/types"
911
"github.com/scroll-tech/go-ethereum/crypto"
1012
"github.com/scroll-tech/go-ethereum/ethclient"
1113
"github.com/scroll-tech/go-ethereum/log"
14+
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/blob_client"
1215

1316
backendabi "scroll-tech/bridge-history-api/abi"
1417
"scroll-tech/bridge-history-api/internal/config"
@@ -19,15 +22,17 @@ import (
1922

2023
// L1EventParser the l1 event parser
2124
type L1EventParser struct {
22-
cfg *config.FetcherConfig
23-
client *ethclient.Client
25+
cfg *config.FetcherConfig
26+
client *ethclient.Client
27+
blobClient blob_client.BlobClient
2428
}
2529

2630
// NewL1EventParser creates l1 event parser
27-
func NewL1EventParser(cfg *config.FetcherConfig, client *ethclient.Client) *L1EventParser {
31+
func NewL1EventParser(cfg *config.FetcherConfig, client *ethclient.Client, blobClient blob_client.BlobClient) *L1EventParser {
2832
return &L1EventParser{
29-
cfg: cfg,
30-
client: client,
33+
cfg: cfg,
34+
client: client,
35+
blobClient: blobClient,
3136
}
3237
}
3338

@@ -232,7 +237,21 @@ func (e *L1EventParser) ParseL1SingleCrossChainEventLogs(ctx context.Context, lo
232237
}
233238

234239
// ParseL1BatchEventLogs parses L1 watched batch events.
235-
func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types.Log, client *ethclient.Client) ([]*orm.BatchEvent, error) {
240+
func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types.Log, client *ethclient.Client, blockTimestampsMap map[uint64]uint64) ([]*orm.BatchEvent, error) {
241+
// Since multiple CommitBatch events per transaction is introduced >= CodecV7,
242+
// with one transaction carrying multiple blobs,
243+
// each CommitBatch event corresponds to a blob containing block range data.
244+
// To correctly process these events, we need to:
245+
// 1. Parsing the associated blob data to extract the block range for each event
246+
// 2. Tracking the parent batch hash for each processed CommitBatch event, to:
247+
// - Validate the batch hash, since parent batch hash is needed to calculate the batch hash
248+
// - Derive the index of the current batch by the number of parent batch hashes tracked
249+
// In commitBatches and commitAndFinalizeBatch, the parent batch hash is passed in calldata,
250+
// so that we can use it to get the first batch's parent batch hash, and derive the rest.
251+
// The index map serves this purpose with:
252+
// Key: commit transaction hash
253+
// Value: parent batch hashes (in order) for each processed CommitBatch event in the transaction
254+
txBlobIndexMap := make(map[common.Hash][]common.Hash)
236255
var l1BatchEvents []*orm.BatchEvent
237256
for _, vlog := range logs {
238257
switch vlog.Topics[0] {
@@ -247,11 +266,59 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types.
247266
log.Error("Failed to get commit batch tx or the tx is still pending", "err", err, "isPending", isPending)
248267
return nil, err
249268
}
250-
startBlock, endBlock, err := utils.GetBatchRangeFromCalldata(commitTx.Data())
269+
version, startBlock, endBlock, err := utils.GetBatchVersionAndBlockRangeFromCalldata(commitTx.Data())
251270
if err != nil {
252271
log.Error("Failed to get batch range from calldata", "hash", commitTx.Hash().String(), "height", vlog.BlockNumber)
253272
return nil, err
254273
}
274+
if version >= 7 { // It's a batch with version >= 7.
275+
codec, err := encoding.CodecFromVersion(encoding.CodecVersion(version))
276+
if err != nil {
277+
return nil, fmt.Errorf("unsupported codec version: %v, err: %w", version, err)
278+
}
279+
280+
// we append the batch hash to the slice for the current commit transaction after processing the batch.
281+
// that means the current index of the batch within the transaction is len(txBlobIndexMap[vlog.TxHash]).
282+
currentIndex := len(txBlobIndexMap[vlog.TxHash])
283+
if currentIndex >= len(commitTx.BlobHashes()) {
284+
return nil, fmt.Errorf("commit transaction %s has %d blobs, but trying to access index %d (batch index %d)",
285+
vlog.TxHash.String(), len(commitTx.BlobHashes()), currentIndex, event.BatchIndex.Uint64())
286+
}
287+
blobVersionedHash := commitTx.BlobHashes()[currentIndex]
288+
289+
// validate the batch hash
290+
var parentBatchHash common.Hash
291+
if currentIndex == 0 {
292+
parentBatchHash, err = utils.GetParentBatchHashFromCalldata(commitTx.Data())
293+
if err != nil {
294+
return nil, fmt.Errorf("failed to get parent batch header from calldata, tx hash: %s, err: %w", vlog.TxHash.String(), err)
295+
}
296+
} else {
297+
// here we need to subtract 1 from the current index to get the parent batch hash.
298+
parentBatchHash = txBlobIndexMap[vlog.TxHash][currentIndex-1]
299+
}
300+
calculatedBatch, err := codec.NewDABatchFromParams(event.BatchIndex.Uint64(), blobVersionedHash, parentBatchHash)
301+
if err != nil {
302+
return nil, fmt.Errorf("failed to create new DA batch from params, batch index: %d, err: %w", event.BatchIndex.Uint64(), err)
303+
}
304+
if calculatedBatch.Hash() != event.BatchHash {
305+
return nil, fmt.Errorf("batch hash mismatch for batch %d, expected: %s, got: %s", event.BatchIndex, event.BatchHash.String(), calculatedBatch.Hash().String())
306+
}
307+
308+
blocks, err := e.getBatchBlockRangeFromBlob(ctx, codec, blobVersionedHash, blockTimestampsMap[vlog.BlockNumber])
309+
if err != nil {
310+
return nil, fmt.Errorf("failed to process versioned blob, blobVersionedHash: %s, block number: %d, blob index: %d, err: %w",
311+
blobVersionedHash.String(), vlog.BlockNumber, currentIndex, err)
312+
}
313+
if len(blocks) == 0 {
314+
return nil, fmt.Errorf("no blocks found in the blob, blobVersionedHash: %s, block number: %d, blob index: %d",
315+
blobVersionedHash.String(), vlog.BlockNumber, currentIndex)
316+
}
317+
startBlock = blocks[0].Number()
318+
endBlock = blocks[len(blocks)-1].Number()
319+
320+
txBlobIndexMap[vlog.TxHash] = append(txBlobIndexMap[vlog.TxHash], event.BatchHash)
321+
}
255322
l1BatchEvents = append(l1BatchEvents, &orm.BatchEvent{
256323
BatchStatus: int(btypes.BatchStatusTypeCommitted),
257324
BatchIndex: event.BatchIndex.Uint64(),
@@ -260,8 +327,8 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types.
260327
EndBlockNumber: endBlock,
261328
L1BlockNumber: vlog.BlockNumber,
262329
})
263-
case backendabi.L1RevertBatchEventSig:
264-
event := backendabi.L1RevertBatchEvent{}
330+
case backendabi.L1RevertBatchV0EventSig:
331+
event := backendabi.L1RevertBatchV0Event{}
265332
if err := utils.UnpackLog(backendabi.IScrollChainABI, &event, "RevertBatch", vlog); err != nil {
266333
log.Error("Failed to unpack RevertBatch event", "err", err)
267334
return nil, err
@@ -272,6 +339,19 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types.
272339
BatchHash: event.BatchHash.String(),
273340
L1BlockNumber: vlog.BlockNumber,
274341
})
342+
case backendabi.L1RevertBatchV7EventSig:
343+
event := backendabi.L1RevertBatchV7Event{}
344+
if err := utils.UnpackLog(backendabi.IScrollChainABI, &event, "RevertBatch0", vlog); err != nil {
345+
log.Error("Failed to unpack RevertBatch event", "err", err)
346+
return nil, err
347+
}
348+
for i := event.StartBatchIndex.Uint64(); i <= event.FinishBatchIndex.Uint64(); i++ {
349+
l1BatchEvents = append(l1BatchEvents, &orm.BatchEvent{
350+
BatchStatus: int(btypes.BatchStatusTypeReverted),
351+
BatchIndex: i,
352+
L1BlockNumber: vlog.BlockNumber,
353+
})
354+
}
275355
case backendabi.L1FinalizeBatchEventSig:
276356
event := backendabi.L1FinalizeBatchEvent{}
277357
if err := utils.UnpackLog(backendabi.IScrollChainABI, &event, "FinalizeBatch", vlog); err != nil {
@@ -389,3 +469,27 @@ func getRealFromAddress(ctx context.Context, eventSender common.Address, eventMe
389469
}
390470
return sender.String(), nil
391471
}
472+
473+
func (e *L1EventParser) getBatchBlockRangeFromBlob(ctx context.Context, codec encoding.Codec, blobVersionedHash common.Hash, l1BlockTime uint64) ([]encoding.DABlock, error) {
474+
blob, err := e.blobClient.GetBlobByVersionedHashAndBlockTime(ctx, blobVersionedHash, l1BlockTime)
475+
if err != nil {
476+
return nil, fmt.Errorf("failed to get blob %s: %w", blobVersionedHash.Hex(), err)
477+
}
478+
if blob == nil {
479+
return nil, fmt.Errorf("blob %s not found", blobVersionedHash.Hex())
480+
}
481+
482+
blobPayload, err := codec.DecodeBlob(blob)
483+
if err != nil {
484+
return nil, fmt.Errorf("blob %s decode error: %w", blobVersionedHash.Hex(), err)
485+
}
486+
487+
blocks := blobPayload.Blocks()
488+
if len(blocks) == 0 {
489+
return nil, fmt.Errorf("empty blocks in blob %s", blobVersionedHash.Hex())
490+
}
491+
492+
log.Debug("Successfully processed blob", "blobVersionedHash", blobVersionedHash.Hex(), "blocksCount", len(blocks))
493+
494+
return blocks, nil
495+
}

0 commit comments

Comments
 (0)