go-ethereum/rollup/rollup_sync_service/l1client.go
Jonas Theis ac8164f5a4
feat: follower node sync from DA (#1098)
* port changes from #1013

* port changes from #1068

* go.mod tidy

* fix compile error

* fix goimports

* fix log

* address review comments

* upgrade golang.org/x/net to 0.23.0

* bump version

* remove unused flag

* update da-codec commit

---------

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2024-12-18 17:27:51 +01:00

158 lines
5.8 KiB
Go

package rollup_sync_service
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/scroll-tech/go-ethereum"
"github.com/scroll-tech/go-ethereum/accounts/abi"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/rpc"
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
)
// L1Client is a wrapper around EthClient that adds
// methods for conveniently collecting rollup events of ScrollChain contract.
type L1Client struct {
ctx context.Context
client sync_service.EthClient
scrollChainAddress common.Address
l1CommitBatchEventSignature common.Hash
l1RevertBatchEventSignature common.Hash
l1FinalizeBatchEventSignature common.Hash
}
// NewL1Client initializes a new L1Client instance with the provided configuration.
// It checks for a valid scrollChainAddress and verifies the chain ID.
func NewL1Client(ctx context.Context, l1Client sync_service.EthClient, l1ChainId uint64, scrollChainAddress common.Address, scrollChainABI *abi.ABI) (*L1Client, error) {
if scrollChainAddress == (common.Address{}) {
return nil, errors.New("must pass non-zero scrollChainAddress to L1Client")
}
// sanity check: compare chain IDs
got, err := l1Client.ChainID(ctx)
if err != nil {
return nil, fmt.Errorf("failed to query L1 chain ID, err: %w", err)
}
if got.Cmp(big.NewInt(0).SetUint64(l1ChainId)) != 0 {
return nil, fmt.Errorf("unexpected chain ID, expected: %v, got: %v", l1ChainId, got)
}
client := L1Client{
ctx: ctx,
client: l1Client,
scrollChainAddress: scrollChainAddress,
l1CommitBatchEventSignature: scrollChainABI.Events["CommitBatch"].ID,
l1RevertBatchEventSignature: scrollChainABI.Events["RevertBatch"].ID,
l1FinalizeBatchEventSignature: scrollChainABI.Events["FinalizeBatch"].ID,
}
return &client, nil
}
// FetchRollupEventsInRange retrieves and parses commit/revert/finalize rollup events between block numbers: [from, to].
func (c *L1Client) FetchRollupEventsInRange(from, to uint64) ([]types.Log, error) {
log.Trace("L1Client FetchRollupEventsInRange", "fromBlock", from, "toBlock", to)
query := ethereum.FilterQuery{
FromBlock: big.NewInt(int64(from)), // inclusive
ToBlock: big.NewInt(int64(to)), // inclusive
Addresses: []common.Address{
c.scrollChainAddress,
},
Topics: make([][]common.Hash, 1),
}
query.Topics[0] = make([]common.Hash, 3)
query.Topics[0][0] = c.l1CommitBatchEventSignature
query.Topics[0][1] = c.l1RevertBatchEventSignature
query.Topics[0][2] = c.l1FinalizeBatchEventSignature
logs, err := c.client.FilterLogs(c.ctx, query)
if err != nil {
return nil, fmt.Errorf("failed to filter logs, err: %w", err)
}
return logs, nil
}
// GetLatestFinalizedBlockNumber fetches the block number of the latest finalized block from the L1 chain.
func (c *L1Client) GetLatestFinalizedBlockNumber() (uint64, error) {
header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(int64(rpc.FinalizedBlockNumber)))
if err != nil {
return 0, err
}
if !header.Number.IsInt64() {
return 0, fmt.Errorf("received unexpected block number in L1Client: %v", header.Number)
}
return header.Number.Uint64(), nil
}
// FetchTxData fetches tx data corresponding to given event log
func (c *L1Client) FetchTxData(vLog *types.Log) ([]byte, error) {
tx, _, err := c.client.TransactionByHash(c.ctx, vLog.TxHash)
if err != nil {
log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction",
"tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err)
block, err := c.client.BlockByHash(c.ctx, vLog.BlockHash)
if err != nil {
return nil, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err)
}
found := false
for _, txInBlock := range block.Transactions() {
if txInBlock.Hash() == vLog.TxHash {
tx = txInBlock
found = true
break
}
}
if !found {
return nil, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex())
}
}
return tx.Data(), nil
}
// FetchTxBlobHash fetches tx blob hash corresponding to given event log
func (c *L1Client) FetchTxBlobHash(vLog *types.Log) (common.Hash, error) {
tx, _, err := c.client.TransactionByHash(c.ctx, vLog.TxHash)
if err != nil {
log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction",
"tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err)
block, err := c.client.BlockByHash(c.ctx, vLog.BlockHash)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err)
}
found := false
for _, txInBlock := range block.Transactions() {
if txInBlock.Hash() == vLog.TxHash {
tx = txInBlock
found = true
break
}
}
if !found {
return common.Hash{}, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex())
}
}
blobHashes := tx.BlobHashes()
if len(blobHashes) == 0 {
return common.Hash{}, fmt.Errorf("transaction does not contain any blobs, tx hash: %v", vLog.TxHash.Hex())
}
return blobHashes[0], nil
}
// GetHeaderByNumber fetches the block header by number
func (c *L1Client) GetHeaderByNumber(blockNumber uint64) (*types.Header, error) {
header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(0).SetUint64(blockNumber))
if err != nil {
return nil, err
}
return header, nil
}