Commit 359bc604 authored by smartcontracts's avatar smartcontracts Committed by GitHub

fix(l2geth): L1 sync error recovery logic (#2647)

Adds logic to startup that will attempt to recover from situations in
which a DTL batch was skipped. Specifically meant to allow existing L1
syncing nodes to continue to sync after the recent DTL bug.
parent 553180e9
---
'@eth-optimism/l2geth': patch
---
Patch for L1 syncing nodes that got stuck after DTL batch sync errors
...@@ -120,6 +120,7 @@ type RollupClient interface { ...@@ -120,6 +120,7 @@ type RollupClient interface {
GetEnqueue(index uint64) (*types.Transaction, error) GetEnqueue(index uint64) (*types.Transaction, error)
GetLatestEnqueue() (*types.Transaction, error) GetLatestEnqueue() (*types.Transaction, error)
GetLatestEnqueueIndex() (*uint64, error) GetLatestEnqueueIndex() (*uint64, error)
GetRawTransaction(uint64, Backend) (*TransactionResponse, error)
GetTransaction(uint64, Backend) (*types.Transaction, error) GetTransaction(uint64, Backend) (*types.Transaction, error)
GetLatestTransaction(Backend) (*types.Transaction, error) GetLatestTransaction(Backend) (*types.Transaction, error)
GetLatestTransactionIndex(Backend) (*uint64, error) GetLatestTransactionIndex(Backend) (*uint64, error)
...@@ -419,7 +420,7 @@ func batchedTransactionToTransaction(res *transaction, chainID *big.Int) (*types ...@@ -419,7 +420,7 @@ func batchedTransactionToTransaction(res *transaction, chainID *big.Int) (*types
} }
// GetTransaction will get a transaction by Canonical Transaction Chain index // GetTransaction will get a transaction by Canonical Transaction Chain index
func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transaction, error) { func (c *Client) GetRawTransaction(index uint64, backend Backend) (*TransactionResponse, error) {
str := strconv.FormatUint(index, 10) str := strconv.FormatUint(index, 10)
response, err := c.client.R(). response, err := c.client.R().
SetPathParams(map[string]string{ SetPathParams(map[string]string{
...@@ -438,6 +439,15 @@ func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transacti ...@@ -438,6 +439,15 @@ func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transacti
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 res, nil
}
// GetTransaction will get a transaction by Canonical Transaction Chain index
func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transaction, error) {
res, err := c.GetRawTransaction(index, backend)
if err != nil {
return nil, err
}
return batchedTransactionToTransaction(res.Transaction, c.chainID) return batchedTransactionToTransaction(res.Transaction, c.chainID)
} }
......
...@@ -278,6 +278,23 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { ...@@ -278,6 +278,23 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error {
s.SetLatestL1Timestamp(context.Timestamp) s.SetLatestL1Timestamp(context.Timestamp)
s.SetLatestL1BlockNumber(context.BlockNumber) s.SetLatestL1BlockNumber(context.BlockNumber)
} else { } else {
// Recover from accidentally skipped batches if necessary.
if s.verifier && s.backend == BackendL1 {
tx, err := s.client.GetRawTransaction(*index, s.backend)
if err != nil {
return fmt.Errorf("Cannot fetch transaction from dtl at index %d: %w", *index, err)
}
oldbatchIndex := s.GetLatestBatchIndex()
newBatchIndex := tx.Transaction.BatchIndex
if tx.Transaction.BatchIndex > 0 {
newBatchIndex -= 1
}
log.Info("Updating batch index", "old", oldbatchIndex, "new", newBatchIndex)
s.SetLatestBatchIndex(&newBatchIndex)
}
log.Info("Found latest index", "index", *index) log.Info("Found latest index", "index", *index)
block := s.bc.GetBlockByNumber(*index + 1) block := s.bc.GetBlockByNumber(*index + 1)
if block == nil { if block == nil {
......
...@@ -957,6 +957,8 @@ func newTestSyncService(isVerifier bool, alloc *common.Address) (*SyncService, c ...@@ -957,6 +957,8 @@ func newTestSyncService(isVerifier bool, alloc *common.Address) (*SyncService, c
type mockClient struct { type mockClient struct {
getEnqueueCallCount int getEnqueueCallCount int
getEnqueue []*types.Transaction getEnqueue []*types.Transaction
getRawTransactionCallCount int
getRawTransaction []*TransactionResponse
getTransactionCallCount int getTransactionCallCount int
getTransaction []*types.Transaction getTransaction []*types.Transaction
getEthContextCallCount int getEthContextCallCount int
...@@ -974,6 +976,7 @@ func setupMockClient(service *SyncService, responses map[string]interface{}) { ...@@ -974,6 +976,7 @@ func setupMockClient(service *SyncService, responses map[string]interface{}) {
func newMockClient(responses map[string]interface{}) *mockClient { func newMockClient(responses map[string]interface{}) *mockClient {
getEnqueueResponses := []*types.Transaction{} getEnqueueResponses := []*types.Transaction{}
getRawTransactionResponses := []*TransactionResponse{}
getTransactionResponses := []*types.Transaction{} getTransactionResponses := []*types.Transaction{}
getEthContextResponses := []*EthContext{} getEthContextResponses := []*EthContext{}
getLatestEthContextResponse := &EthContext{} getLatestEthContextResponse := &EthContext{}
...@@ -983,6 +986,10 @@ func newMockClient(responses map[string]interface{}) *mockClient { ...@@ -983,6 +986,10 @@ func newMockClient(responses map[string]interface{}) *mockClient {
if ok { if ok {
getEnqueueResponses = enqueue.([]*types.Transaction) getEnqueueResponses = enqueue.([]*types.Transaction)
} }
getRawTx, ok := responses["GetRawTransaction"]
if ok {
getRawTransactionResponses = getRawTx.([]*TransactionResponse)
}
getTx, ok := responses["GetTransaction"] getTx, ok := responses["GetTransaction"]
if ok { if ok {
getTransactionResponses = getTx.([]*types.Transaction) getTransactionResponses = getTx.([]*types.Transaction)
...@@ -1002,6 +1009,7 @@ func newMockClient(responses map[string]interface{}) *mockClient { ...@@ -1002,6 +1009,7 @@ func newMockClient(responses map[string]interface{}) *mockClient {
return &mockClient{ return &mockClient{
getEnqueue: getEnqueueResponses, getEnqueue: getEnqueueResponses,
getRawTransaction: getRawTransactionResponses,
getTransaction: getTransactionResponses, getTransaction: getTransactionResponses,
getEthContext: getEthContextResponses, getEthContext: getEthContextResponses,
getLatestEthContext: getLatestEthContextResponse, getLatestEthContext: getLatestEthContextResponse,
...@@ -1025,6 +1033,15 @@ func (m *mockClient) GetLatestEnqueue() (*types.Transaction, error) { ...@@ -1025,6 +1033,15 @@ func (m *mockClient) GetLatestEnqueue() (*types.Transaction, error) {
return m.getEnqueue[len(m.getEnqueue)-1], nil return m.getEnqueue[len(m.getEnqueue)-1], nil
} }
func (m *mockClient) GetRawTransaction(index uint64, backend Backend) (*TransactionResponse, error) {
if m.getRawTransactionCallCount < len(m.getRawTransaction) {
tx := m.getRawTransaction[m.getRawTransactionCallCount]
m.getRawTransactionCallCount++
return tx, nil
}
return nil, fmt.Errorf("Cannot get raw transaction: mocks (%d), call count (%d)", len(m.getRawTransaction), m.getRawTransactionCallCount)
}
func (m *mockClient) GetTransaction(index uint64, backend Backend) (*types.Transaction, error) { func (m *mockClient) GetTransaction(index uint64, backend Backend) (*types.Transaction, error) {
if m.getTransactionCallCount < len(m.getTransaction) { if m.getTransactionCallCount < len(m.getTransaction) {
tx := m.getTransaction[m.getTransactionCallCount] tx := m.getTransaction[m.getTransactionCallCount]
......
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