Commit 7e9ca1eb authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

l2geth: rollup client batch api (#516)

* l2geth: add batch querying to rollup client

* l2geth: add patch changeset

* l2geth: complete mock client interface

* l2geth: add comments to rollup client

* l2geth: more idiomatic error handling
parent 76c4ceb0
---
"@eth-optimism/l2geth": patch
---
Add batch API to rollup client
...@@ -13,12 +13,21 @@ import ( ...@@ -13,12 +13,21 @@ import (
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
/** // Constants that are used to compare against values in the deserialized JSON
* GET /enqueue/index/{index} // fetched by the RollupClient
* GET /transaction/index/{index} const (
* GET /eth/context/latest sequencer = "sequencer"
*/ l1 = "l1"
EIP155 = "EIP155"
ETH_SIGN = "ETH_SIGN"
)
// errElementNotFound represents the error case of the remote element not being
// found. It applies to transactions, queue elements and batches
var errElementNotFound = errors.New("element not found")
// Batch represents the data structure that is submitted with
// a series of transactions to layer one
type Batch struct { type Batch struct {
Index uint64 `json:"index"` Index uint64 `json:"index"`
Root common.Hash `json:"root,omitempty"` Root common.Hash `json:"root,omitempty"`
...@@ -30,22 +39,32 @@ type Batch struct { ...@@ -30,22 +39,32 @@ type Batch struct {
Submitter common.Address `json:"submitter"` Submitter common.Address `json:"submitter"`
} }
// EthContext represents the L1 EVM context that is injected into
// the OVM at runtime. It is updated with each `enqueue` transaction
// and needs to be fetched from a remote server to be updated when
// too much time has passed between `enqueue` transactions.
type EthContext struct { type EthContext struct {
BlockNumber uint64 `json:"blockNumber"` BlockNumber uint64 `json:"blockNumber"`
BlockHash common.Hash `json:"blockHash"` BlockHash common.Hash `json:"blockHash"`
Timestamp uint64 `json:"timestamp"` Timestamp uint64 `json:"timestamp"`
} }
// SyncStatus represents the state of the remote server. The SyncService
// does not want to begin syncing until the remote server has fully synced.
type SyncStatus struct { type SyncStatus struct {
Syncing bool `json:"syncing"` Syncing bool `json:"syncing"`
HighestKnownTransactionIndex uint64 `json:"highestKnownTransactionIndex"` HighestKnownTransactionIndex uint64 `json:"highestKnownTransactionIndex"`
CurrentTransactionIndex uint64 `json:"currentTransactionIndex"` CurrentTransactionIndex uint64 `json:"currentTransactionIndex"`
} }
// L1GasPrice represents the gas price of L1. It is used as part of the gas
// estimatation logic.
type L1GasPrice struct { type L1GasPrice struct {
GasPrice string `json:"gasPrice"` GasPrice string `json:"gasPrice"`
} }
// transaction represents the return result of the remote server.
// It either came from a batch or was replicated from the sequencer.
type transaction struct { type transaction struct {
Index uint64 `json:"index"` Index uint64 `json:"index"`
BatchIndex uint64 `json:"batchIndex"` BatchIndex uint64 `json:"batchIndex"`
...@@ -61,6 +80,7 @@ type transaction struct { ...@@ -61,6 +80,7 @@ type transaction struct {
Decoded *decoded `json:"decoded"` Decoded *decoded `json:"decoded"`
} }
// Enqueue represents an `enqueue` transaction or a L1 to L2 transaction.
type Enqueue struct { type Enqueue struct {
Index *uint64 `json:"ctcIndex"` Index *uint64 `json:"ctcIndex"`
Target *common.Address `json:"target"` Target *common.Address `json:"target"`
...@@ -72,12 +92,16 @@ type Enqueue struct { ...@@ -72,12 +92,16 @@ type Enqueue struct {
QueueIndex *uint64 `json:"index"` QueueIndex *uint64 `json:"index"`
} }
// signature represents a secp256k1 ECDSA signature
type signature struct { type signature struct {
R hexutil.Bytes `json:"r"` R hexutil.Bytes `json:"r"`
S hexutil.Bytes `json:"s"` S hexutil.Bytes `json:"s"`
V uint `json:"v"` V uint `json:"v"`
} }
// decoded represents the decoded transaction from the batch.
// When this struct exists in other structs and is set to `nil`,
// it means that the decoding failed.
type decoded struct { type decoded struct {
Signature signature `json:"sig"` Signature signature `json:"sig"`
GasLimit uint64 `json:"gasLimit"` GasLimit uint64 `json:"gasLimit"`
...@@ -87,31 +111,47 @@ type decoded struct { ...@@ -87,31 +111,47 @@ type decoded struct {
Data hexutil.Bytes `json:"data"` Data hexutil.Bytes `json:"data"`
} }
// RollupClient is able to query for information
// that is required by the SyncService
type RollupClient interface { type RollupClient interface {
GetEnqueue(index uint64) (*types.Transaction, error) GetEnqueue(index uint64) (*types.Transaction, error)
GetLatestEnqueue() (*types.Transaction, error) GetLatestEnqueue() (*types.Transaction, error)
GetTransaction(index uint64) (*types.Transaction, error) GetTransaction(uint64) (*types.Transaction, error)
GetLatestTransaction() (*types.Transaction, error) GetLatestTransaction() (*types.Transaction, error)
GetEthContext(index uint64) (*EthContext, error) GetEthContext(uint64) (*EthContext, error)
GetLatestEthContext() (*EthContext, error) GetLatestEthContext() (*EthContext, error)
GetLastConfirmedEnqueue() (*types.Transaction, error) GetLastConfirmedEnqueue() (*types.Transaction, error)
GetLatestTransactionBatch() (*Batch, []*types.Transaction, error)
GetTransactionBatch(uint64) (*Batch, []*types.Transaction, error)
SyncStatus() (*SyncStatus, error) SyncStatus() (*SyncStatus, error)
GetL1GasPrice() (*big.Int, error) GetL1GasPrice() (*big.Int, error)
} }
// Client is an HTTP based RollupClient
type Client struct { type Client struct {
client *resty.Client client *resty.Client
signer *types.OVMSigner signer *types.OVMSigner
} }
// TransactionResponse represents the response from the remote server when
// querying transactions.
type TransactionResponse struct { type TransactionResponse struct {
Transaction *transaction `json:"transaction"` Transaction *transaction `json:"transaction"`
Batch *Batch `json:"batch"` Batch *Batch `json:"batch"`
} }
// TransactionBatchResponse represents the response from the remote server
// when querying batches.
type TransactionBatchResponse struct {
Batch *Batch `json:"batch"`
Transactions []*transaction `json:"transactions"`
}
// NewClient create a new Client given a remote HTTP url and a chain id
func NewClient(url string, chainID *big.Int) *Client { func NewClient(url string, chainID *big.Int) *Client {
client := resty.New() client := resty.New()
client.SetHostURL(url) client.SetHostURL(url)
client.SetHeader("User-Agent", "sequencer")
signer := types.NewOVMSigner(chainID) signer := types.NewOVMSigner(chainID)
return &Client{ return &Client{
...@@ -120,7 +160,7 @@ func NewClient(url string, chainID *big.Int) *Client { ...@@ -120,7 +160,7 @@ func NewClient(url string, chainID *big.Int) *Client {
} }
} }
// This needs to return a transaction instead // GetEnqueue fetches an `enqueue` transaction by queue index
func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) { func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) {
str := strconv.FormatUint(index, 10) str := strconv.FormatUint(index, 10)
response, err := c.client.R(). response, err := c.client.R().
...@@ -131,7 +171,7 @@ func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) { ...@@ -131,7 +171,7 @@ func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) {
Get("/enqueue/index/{index}") Get("/enqueue/index/{index}")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot fetch enqueue: %w", err)
} }
enqueue, ok := response.Result().(*Enqueue) enqueue, ok := response.Result().(*Enqueue)
if !ok { if !ok {
...@@ -142,16 +182,21 @@ func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) { ...@@ -142,16 +182,21 @@ func (c *Client) GetEnqueue(index uint64) (*types.Transaction, error) {
} }
tx, err := enqueueToTransaction(enqueue) tx, err := enqueueToTransaction(enqueue)
if err != nil { if err != nil {
return nil, fmt.Errorf("Cannot parse enqueue tx :%w", err) return nil, err
} }
return tx, nil return tx, nil
} }
// enqueueToTransaction turns an Enqueue into a types.Transaction
// so that it can be consumed by the SyncService
func enqueueToTransaction(enqueue *Enqueue) (*types.Transaction, error) { func enqueueToTransaction(enqueue *Enqueue) (*types.Transaction, error) {
if enqueue == nil {
return nil, errElementNotFound
}
// When the queue index is nil, is means that the enqueue'd transaction // When the queue index is nil, is means that the enqueue'd transaction
// does not exist. // does not exist.
if enqueue.QueueIndex == nil { if enqueue.QueueIndex == nil {
return nil, nil return nil, errElementNotFound
} }
// The queue index is the nonce // The queue index is the nonce
nonce := *enqueue.QueueIndex nonce := *enqueue.QueueIndex
...@@ -203,13 +248,15 @@ func enqueueToTransaction(enqueue *Enqueue) (*types.Transaction, error) { ...@@ -203,13 +248,15 @@ func enqueueToTransaction(enqueue *Enqueue) (*types.Transaction, error) {
return tx, nil return tx, nil
} }
// GetLatestEnqueue fetches the latest `enqueue`, meaning the `enqueue`
// transaction with the greatest queue index.
func (c *Client) GetLatestEnqueue() (*types.Transaction, error) { func (c *Client) GetLatestEnqueue() (*types.Transaction, error) {
response, err := c.client.R(). response, err := c.client.R().
SetResult(&Enqueue{}). SetResult(&Enqueue{}).
Get("/enqueue/latest") Get("/enqueue/latest")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot fetch latest enqueue: %w", err)
} }
enqueue, ok := response.Result().(*Enqueue) enqueue, ok := response.Result().(*Enqueue)
if !ok { if !ok {
...@@ -217,50 +264,52 @@ func (c *Client) GetLatestEnqueue() (*types.Transaction, error) { ...@@ -217,50 +264,52 @@ func (c *Client) GetLatestEnqueue() (*types.Transaction, error) {
} }
tx, err := enqueueToTransaction(enqueue) tx, err := enqueueToTransaction(enqueue)
if err != nil { if err != nil {
return nil, fmt.Errorf("Cannot parse enqueue tx :%w", err) return nil, fmt.Errorf("Cannot parse enqueue tx: %w", err)
} }
return tx, nil return tx, nil
} }
func transactionResponseToTransaction(res *TransactionResponse, signer *types.OVMSigner) (*types.Transaction, error) { // batchedTransactionToTransaction converts a transaction into a
// types.Transaction that can be consumed by the SyncService
func batchedTransactionToTransaction(res *transaction, signer *types.OVMSigner) (*types.Transaction, error) {
// `nil` transactions are not found // `nil` transactions are not found
if res.Transaction == nil { if res == nil {
return nil, nil return nil, errElementNotFound
} }
// The queue origin must be either sequencer of l1, otherwise // The queue origin must be either sequencer of l1, otherwise
// it is considered an unknown queue origin and will not be processed // it is considered an unknown queue origin and will not be processed
var queueOrigin types.QueueOrigin var queueOrigin types.QueueOrigin
if res.Transaction.QueueOrigin == "sequencer" { if res.QueueOrigin == sequencer {
queueOrigin = types.QueueOriginSequencer queueOrigin = types.QueueOriginSequencer
} else if res.Transaction.QueueOrigin == "l1" { } else if res.QueueOrigin == l1 {
queueOrigin = types.QueueOriginL1ToL2 queueOrigin = types.QueueOriginL1ToL2
} else { } else {
return nil, fmt.Errorf("Unknown queue origin: %s", res.Transaction.QueueOrigin) return nil, fmt.Errorf("Unknown queue origin: %s", res.QueueOrigin)
} }
// The transaction type must be EIP155 or EthSign. Throughout this // The transaction type must be EIP155 or EthSign. Throughout this
// codebase, it is referred to as "sighash type" but it could actually // codebase, it is referred to as "sighash type" but it could actually
// be generalized to transaction type. Right now the only different // be generalized to transaction type. Right now the only different
// types use a different signature hashing scheme. // types use a different signature hashing scheme.
var sighashType types.SignatureHashType var sighashType types.SignatureHashType
if res.Transaction.Type == "EIP155" { if res.Type == EIP155 {
sighashType = types.SighashEIP155 sighashType = types.SighashEIP155
} else if res.Transaction.Type == "ETH_SIGN" { } else if res.Type == ETH_SIGN {
sighashType = types.SighashEthSign sighashType = types.SighashEthSign
} else { } else {
return nil, fmt.Errorf("Unknown transaction type: %s", res.Transaction.Type) return nil, fmt.Errorf("Unknown transaction type: %s", res.Type)
} }
// Transactions that have been decoded are // Transactions that have been decoded are
// Queue Origin Sequencer transactions // Queue Origin Sequencer transactions
if res.Transaction.Decoded != nil { if res.Decoded != nil {
nonce := res.Transaction.Decoded.Nonce nonce := res.Decoded.Nonce
to := res.Transaction.Decoded.Target to := res.Decoded.Target
value := new(big.Int) value := new(big.Int)
// Note: there are two gas limits, one top level and // Note: there are two gas limits, one top level and
// another on the raw transaction itself. Maybe maxGasLimit // another on the raw transaction itself. Maybe maxGasLimit
// for the top level? // for the top level?
gasLimit := res.Transaction.Decoded.GasLimit gasLimit := res.Decoded.GasLimit
gasPrice := new(big.Int).SetUint64(res.Transaction.Decoded.GasPrice) gasPrice := new(big.Int).SetUint64(res.Decoded.GasPrice)
data := res.Transaction.Decoded.Data data := res.Decoded.Data
var tx *types.Transaction var tx *types.Transaction
if to == (common.Address{}) { if to == (common.Address{}) {
...@@ -270,22 +319,22 @@ func transactionResponseToTransaction(res *TransactionResponse, signer *types.OV ...@@ -270,22 +319,22 @@ func transactionResponseToTransaction(res *TransactionResponse, signer *types.OV
} }
txMeta := types.NewTransactionMeta( txMeta := types.NewTransactionMeta(
new(big.Int).SetUint64(res.Transaction.BlockNumber), new(big.Int).SetUint64(res.BlockNumber),
res.Transaction.Timestamp, res.Timestamp,
res.Transaction.Origin, res.Origin,
sighashType, sighashType,
queueOrigin, queueOrigin,
&res.Transaction.Index, &res.Index,
res.Transaction.QueueIndex, res.QueueIndex,
res.Transaction.Data, res.Data,
) )
tx.SetTransactionMeta(txMeta) tx.SetTransactionMeta(txMeta)
r, s := res.Transaction.Decoded.Signature.R, res.Transaction.Decoded.Signature.S r, s := res.Decoded.Signature.R, res.Decoded.Signature.S
sig := make([]byte, crypto.SignatureLength) sig := make([]byte, crypto.SignatureLength)
copy(sig[32-len(r):32], r) copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s) copy(sig[64-len(s):64], s)
sig[64] = byte(res.Transaction.Decoded.Signature.V) sig[64] = byte(res.Decoded.Signature.V)
tx, err := tx.WithSignature(signer, sig[:]) tx, err := tx.WithSignature(signer, sig[:])
if err != nil { if err != nil {
...@@ -298,31 +347,32 @@ func transactionResponseToTransaction(res *TransactionResponse, signer *types.OV ...@@ -298,31 +347,32 @@ func transactionResponseToTransaction(res *TransactionResponse, signer *types.OV
// The transaction is either an L1 to L2 transaction or it does not have a // The transaction is either an L1 to L2 transaction or it does not have a
// known deserialization // known deserialization
nonce := uint64(0) nonce := uint64(0)
if res.Transaction.QueueOrigin == "l1" { if res.QueueOrigin == l1 {
if res.Transaction.QueueIndex == nil { if res.QueueIndex == nil {
return nil, errors.New("Queue origin L1 to L2 without a queue index") return nil, errors.New("Queue origin L1 to L2 without a queue index")
} }
nonce = *res.Transaction.QueueIndex nonce = *res.QueueIndex
} }
target := res.Transaction.Target target := res.Target
gasLimit := res.Transaction.GasLimit gasLimit := res.GasLimit
data := res.Transaction.Data data := res.Data
origin := res.Transaction.Origin origin := res.Origin
tx := types.NewTransaction(nonce, target, big.NewInt(0), gasLimit, big.NewInt(0), data) tx := types.NewTransaction(nonce, target, big.NewInt(0), gasLimit, big.NewInt(0), data)
txMeta := types.NewTransactionMeta( txMeta := types.NewTransactionMeta(
new(big.Int).SetUint64(res.Transaction.BlockNumber), new(big.Int).SetUint64(res.BlockNumber),
res.Transaction.Timestamp, res.Timestamp,
origin, origin,
sighashType, sighashType,
queueOrigin, queueOrigin,
&res.Transaction.Index, &res.Index,
res.Transaction.QueueIndex, res.QueueIndex,
res.Transaction.Data, res.Data,
) )
tx.SetTransactionMeta(txMeta) tx.SetTransactionMeta(txMeta)
return tx, nil return tx, nil
} }
// GetTransaction will get a transaction by Canonical Transaction Chain index
func (c *Client) GetTransaction(index uint64) (*types.Transaction, error) { func (c *Client) GetTransaction(index uint64) (*types.Transaction, error) {
str := strconv.FormatUint(index, 10) str := strconv.FormatUint(index, 10)
response, err := c.client.R(). response, err := c.client.R().
...@@ -333,32 +383,34 @@ func (c *Client) GetTransaction(index uint64) (*types.Transaction, error) { ...@@ -333,32 +383,34 @@ func (c *Client) GetTransaction(index uint64) (*types.Transaction, error) {
Get("/transaction/index/{index}") Get("/transaction/index/{index}")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot fetch transaction: %w", err)
} }
res, ok := response.Result().(*TransactionResponse) res, ok := response.Result().(*TransactionResponse)
if !ok { if !ok {
return nil, fmt.Errorf("could not get tx with index %d", index) return nil, fmt.Errorf("could not get tx with index %d", index)
} }
return batchedTransactionToTransaction(res.Transaction, c.signer)
return transactionResponseToTransaction(res, c.signer)
} }
// GetLatestTransaction will get the latest transaction, meaning the transaction
// with the greatest Canonical Transaction Chain index
func (c *Client) GetLatestTransaction() (*types.Transaction, error) { func (c *Client) GetLatestTransaction() (*types.Transaction, error) {
response, err := c.client.R(). response, err := c.client.R().
SetResult(&TransactionResponse{}). SetResult(&TransactionResponse{}).
Get("/transaction/latest") Get("/transaction/latest")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot fetch latest transactions: %w", err)
} }
res, ok := response.Result().(*TransactionResponse) res, ok := response.Result().(*TransactionResponse)
if !ok { if !ok {
return nil, errors.New("") return nil, errors.New("Cannot get latest transaction")
} }
return transactionResponseToTransaction(res, c.signer) return batchedTransactionToTransaction(res.Transaction, c.signer)
} }
// GetEthContext will return the EthContext by block number
func (c *Client) GetEthContext(blockNumber uint64) (*EthContext, error) { func (c *Client) GetEthContext(blockNumber uint64) (*EthContext, error) {
str := strconv.FormatUint(blockNumber, 10) str := strconv.FormatUint(blockNumber, 10)
response, err := c.client.R(). response, err := c.client.R().
...@@ -376,10 +428,10 @@ func (c *Client) GetEthContext(blockNumber uint64) (*EthContext, error) { ...@@ -376,10 +428,10 @@ func (c *Client) GetEthContext(blockNumber uint64) (*EthContext, error) {
if !ok { if !ok {
return nil, errors.New("Cannot parse EthContext") return nil, errors.New("Cannot parse EthContext")
} }
return context, nil return context, nil
} }
// GetLatestEthContext will return the latest EthContext
func (c *Client) GetLatestEthContext() (*EthContext, error) { func (c *Client) GetLatestEthContext() (*EthContext, error) {
response, err := c.client.R(). response, err := c.client.R().
SetResult(&EthContext{}). SetResult(&EthContext{}).
...@@ -397,6 +449,8 @@ func (c *Client) GetLatestEthContext() (*EthContext, error) { ...@@ -397,6 +449,8 @@ func (c *Client) GetLatestEthContext() (*EthContext, error) {
return context, nil return context, nil
} }
// GetLastConfirmedEnqueue will get the last `enqueue` transaction that has been
// batched up
func (c *Client) GetLastConfirmedEnqueue() (*types.Transaction, error) { func (c *Client) GetLastConfirmedEnqueue() (*types.Transaction, error) {
enqueue, err := c.GetLatestEnqueue() enqueue, err := c.GetLatestEnqueue()
if err != nil { if err != nil {
...@@ -428,6 +482,7 @@ func (c *Client) GetLastConfirmedEnqueue() (*types.Transaction, error) { ...@@ -428,6 +482,7 @@ func (c *Client) GetLastConfirmedEnqueue() (*types.Transaction, error) {
} }
} }
// SyncStatus will query the remote server to determine if it is still syncing
func (c *Client) SyncStatus() (*SyncStatus, error) { func (c *Client) SyncStatus() (*SyncStatus, error) {
response, err := c.client.R(). response, err := c.client.R().
SetResult(&SyncStatus{}). SetResult(&SyncStatus{}).
...@@ -445,6 +500,61 @@ func (c *Client) SyncStatus() (*SyncStatus, error) { ...@@ -445,6 +500,61 @@ func (c *Client) SyncStatus() (*SyncStatus, error) {
return status, nil return status, nil
} }
// GetLatestTransactionBatch will return the latest transaction batch
func (c *Client) GetLatestTransactionBatch() (*Batch, []*types.Transaction, error) {
response, err := c.client.R().
SetResult(&TransactionBatchResponse{}).
Get("/batch/transaction/latest")
if err != nil {
return nil, nil, errors.New("Cannot get latest transaction batch")
}
txBatch, ok := response.Result().(*TransactionBatchResponse)
if !ok {
return nil, nil, fmt.Errorf("Cannot parse transaction batch response")
}
return parseTransactionBatchResponse(txBatch, c.signer)
}
// GetTransactionBatch will return the transaction batch by batch index
func (c *Client) GetTransactionBatch(index uint64) (*Batch, []*types.Transaction, error) {
str := strconv.FormatUint(index, 10)
response, err := c.client.R().
SetResult(&TransactionBatchResponse{}).
SetPathParams(map[string]string{
"index": str,
}).
Get("/batch/transaction/index/{index}")
if err != nil {
return nil, nil, fmt.Errorf("Cannot get transaction batch %d", index)
}
txBatch, ok := response.Result().(*TransactionBatchResponse)
if !ok {
return nil, nil, fmt.Errorf("Cannot parse transaction batch response")
}
return parseTransactionBatchResponse(txBatch, c.signer)
}
// parseTransactionBatchResponse will turn a TransactionBatchResponse into a
// Batch and its corresponding types.Transactions
func parseTransactionBatchResponse(txBatch *TransactionBatchResponse, signer *types.OVMSigner) (*Batch, []*types.Transaction, error) {
if txBatch == nil {
return nil, nil, nil
}
batch := txBatch.Batch
txs := make([]*types.Transaction, len(txBatch.Transactions))
for i, tx := range txBatch.Transactions {
transaction, err := batchedTransactionToTransaction(tx, signer)
if err != nil {
return nil, nil, fmt.Errorf("Cannot parse transaction batch: %w", err)
}
txs[i] = transaction
}
return batch, txs, nil
}
// GetL1GasPrice will return the current gas price on L1
func (c *Client) GetL1GasPrice() (*big.Int, error) { func (c *Client) GetL1GasPrice() (*big.Int, error) {
response, err := c.client.R(). response, err := c.client.R().
SetResult(&L1GasPrice{}). SetResult(&L1GasPrice{}).
......
...@@ -249,13 +249,15 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { ...@@ -249,13 +249,15 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error {
queueIndex := s.GetLatestEnqueueIndex() queueIndex := s.GetLatestEnqueueIndex()
if queueIndex == nil { if queueIndex == nil {
enqueue, err := s.client.GetLastConfirmedEnqueue() enqueue, err := s.client.GetLastConfirmedEnqueue()
if err != nil {
return fmt.Errorf("Cannot fetch last confirmed queue tx: %w", err)
}
// There are no enqueues yet // There are no enqueues yet
if enqueue == nil { if errors.Is(err, errElementNotFound) {
return nil return nil
} }
// Other unexpected error
if err != nil {
return fmt.Errorf("Cannot fetch last confirmed queue tx: %w", err)
}
// No error, the queue element was found
queueIndex = enqueue.GetMeta().QueueIndex queueIndex = enqueue.GetMeta().QueueIndex
} }
s.SetLatestEnqueueIndex(queueIndex) s.SetLatestEnqueueIndex(queueIndex)
...@@ -310,14 +312,13 @@ func (s *SyncService) verify() error { ...@@ -310,14 +312,13 @@ func (s *SyncService) verify() error {
// The verifier polls for ctc transactions. // The verifier polls for ctc transactions.
// the ctc transactions are extending the chain. // the ctc transactions are extending the chain.
latest, err := s.client.GetLatestTransaction() latest, err := s.client.GetLatestTransaction()
if err != nil { if errors.Is(err, errElementNotFound) {
return err
}
if latest == nil {
log.Debug("latest transaction not found") log.Debug("latest transaction not found")
return nil return nil
} }
if err != nil {
return err
}
var start uint64 var start uint64
if s.GetLatestIndex() == nil { if s.GetLatestIndex() == nil {
...@@ -372,16 +373,13 @@ func (s *SyncService) sequence() error { ...@@ -372,16 +373,13 @@ func (s *SyncService) sequence() error {
// Place as many L1ToL2 transactions in the same context as possible // Place as many L1ToL2 transactions in the same context as possible
// by executing them one after another. // by executing them one after another.
latest, err := s.client.GetLatestEnqueue() latest, err := s.client.GetLatestEnqueue()
if err != nil { if errors.Is(err, errElementNotFound) {
return err
}
// This should never happen unless the backend is empty
if latest == nil {
log.Debug("No enqueue transactions found") log.Debug("No enqueue transactions found")
return nil return nil
} }
if err != nil {
return fmt.Errorf("cannot fetch latest enqueue: %w", err)
}
// Compare the remote latest queue index to the local latest // Compare the remote latest queue index to the local latest
// queue index. If the remote latest queue index is greater // queue index. If the remote latest queue index is greater
// than the local latest queue index, be sure to ingest more // than the local latest queue index, be sure to ingest more
...@@ -486,15 +484,15 @@ func (s *SyncService) syncTransactionsToTip() error { ...@@ -486,15 +484,15 @@ func (s *SyncService) syncTransactionsToTip() error {
// This function must be sure to sync all the way to the tip. // This function must be sure to sync all the way to the tip.
// First query the latest transaction // First query the latest transaction
latest, err := s.client.GetLatestTransaction() latest, err := s.client.GetLatestTransaction()
if errors.Is(err, errElementNotFound) {
log.Info("No transactions to sync")
return nil
}
if err != nil { if err != nil {
log.Error("Cannot get latest transaction", "msg", err) log.Error("Cannot get latest transaction", "msg", err)
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
continue continue
} }
if latest == nil {
log.Info("No transactions to sync")
return nil
}
tipHeight := latest.GetMeta().Index tipHeight := latest.GetMeta().Index
index := rawdb.ReadHeadIndex(s.db) index := rawdb.ReadHeadIndex(s.db)
start := uint64(0) start := uint64(0)
......
...@@ -428,6 +428,14 @@ func (m *mockClient) GetLastConfirmedEnqueue() (*types.Transaction, error) { ...@@ -428,6 +428,14 @@ func (m *mockClient) GetLastConfirmedEnqueue() (*types.Transaction, error) {
return nil, nil return nil, nil
} }
func (m *mockClient) GetLatestTransactionBatch() (*Batch, []*types.Transaction, error) {
return nil, nil, nil
}
func (m *mockClient) GetTransactionBatch(index uint64) (*Batch, []*types.Transaction, error) {
return nil, nil, nil
}
func (m *mockClient) SyncStatus() (*SyncStatus, error) { func (m *mockClient) SyncStatus() (*SyncStatus, error) {
return &SyncStatus{ return &SyncStatus{
Syncing: false, Syncing: false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment