github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/rollup/sync_service/bridge_client.go (about)

     1  package sync_service
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/big"
     8  
     9  	"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
    10  	"github.com/scroll-tech/go-ethereum/common"
    11  	"github.com/scroll-tech/go-ethereum/core/types"
    12  	"github.com/scroll-tech/go-ethereum/log"
    13  	"github.com/scroll-tech/go-ethereum/rpc"
    14  )
    15  
    16  // BridgeClient is a wrapper around EthClient that adds
    17  // methods for conveniently collecting L1 messages.
    18  type BridgeClient struct {
    19  	client                EthClient
    20  	confirmations         rpc.BlockNumber
    21  	l1MessageQueueAddress common.Address
    22  	filterer              *L1MessageQueueFilterer
    23  }
    24  
    25  func newBridgeClient(ctx context.Context, l1Client EthClient, l1ChainId uint64, confirmations rpc.BlockNumber, l1MessageQueueAddress common.Address) (*BridgeClient, error) {
    26  	if l1MessageQueueAddress == (common.Address{}) {
    27  		return nil, errors.New("must pass non-zero l1MessageQueueAddress to BridgeClient")
    28  	}
    29  
    30  	// sanity check: compare chain IDs
    31  	got, err := l1Client.ChainID(ctx)
    32  	if err != nil {
    33  		return nil, fmt.Errorf("failed to query L1 chain ID, err = %w", err)
    34  	}
    35  	if got.Cmp(big.NewInt(0).SetUint64(l1ChainId)) != 0 {
    36  		return nil, fmt.Errorf("unexpected chain ID, expected = %v, got = %v", l1ChainId, got)
    37  	}
    38  
    39  	filterer, err := NewL1MessageQueueFilterer(l1MessageQueueAddress, l1Client)
    40  	if err != nil {
    41  		return nil, fmt.Errorf("failed to initialize L1MessageQueueFilterer, err = %w", err)
    42  	}
    43  
    44  	client := BridgeClient{
    45  		client:                l1Client,
    46  		confirmations:         confirmations,
    47  		l1MessageQueueAddress: l1MessageQueueAddress,
    48  		filterer:              filterer,
    49  	}
    50  
    51  	return &client, nil
    52  }
    53  
    54  // fetchMessagesInRange retrieves and parses all L1 messages between the
    55  // provided from and to L1 block numbers (inclusive).
    56  func (c *BridgeClient) fetchMessagesInRange(ctx context.Context, from, to uint64) ([]types.L1MessageTx, error) {
    57  	log.Trace("BridgeClient fetchMessagesInRange", "fromBlock", from, "toBlock", to)
    58  
    59  	opts := bind.FilterOpts{
    60  		Start:   from,
    61  		End:     &to,
    62  		Context: ctx,
    63  	}
    64  	it, err := c.filterer.FilterQueueTransaction(&opts, nil, nil)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	var msgs []types.L1MessageTx
    70  
    71  	for it.Next() {
    72  		event := it.Event
    73  		log.Trace("Received new L1 QueueTransaction event", "event", event)
    74  
    75  		if !event.GasLimit.IsUint64() {
    76  			return nil, fmt.Errorf("invalid QueueTransaction event: QueueIndex = %v, GasLimit = %v", event.QueueIndex, event.GasLimit)
    77  		}
    78  
    79  		msgs = append(msgs, types.L1MessageTx{
    80  			QueueIndex: event.QueueIndex,
    81  			Gas:        event.GasLimit.Uint64(),
    82  			To:         &event.Target,
    83  			Value:      event.Value,
    84  			Data:       event.Data,
    85  			Sender:     event.Sender,
    86  		})
    87  	}
    88  
    89  	return msgs, nil
    90  }
    91  
    92  func (c *BridgeClient) getLatestConfirmedBlockNumber(ctx context.Context) (uint64, error) {
    93  	// confirmation based on "safe" or "finalized" block tag
    94  	if c.confirmations == rpc.SafeBlockNumber || c.confirmations == rpc.FinalizedBlockNumber {
    95  		tag := big.NewInt(int64(c.confirmations))
    96  		header, err := c.client.HeaderByNumber(ctx, tag)
    97  		if err != nil {
    98  			return 0, err
    99  		}
   100  		if !header.Number.IsInt64() {
   101  			return 0, fmt.Errorf("received unexpected block number in BridgeClient: %v", header.Number)
   102  		}
   103  		return header.Number.Uint64(), nil
   104  	}
   105  
   106  	// confirmation based on latest block number
   107  	if c.confirmations == rpc.LatestBlockNumber {
   108  		number, err := c.client.BlockNumber(ctx)
   109  		if err != nil {
   110  			return 0, err
   111  		}
   112  		return number, nil
   113  	}
   114  
   115  	// confirmation based on a certain number of blocks
   116  	if c.confirmations.Int64() >= 0 {
   117  		number, err := c.client.BlockNumber(ctx)
   118  		if err != nil {
   119  			return 0, err
   120  		}
   121  		confirmations := uint64(c.confirmations.Int64())
   122  		if number >= confirmations {
   123  			return number - confirmations, nil
   124  		}
   125  		return 0, nil
   126  	}
   127  
   128  	return 0, fmt.Errorf("unknown confirmation type: %v", c.confirmations)
   129  }