diff --git a/.changeset/stupid-pears-cough.md b/.changeset/stupid-pears-cough.md new file mode 100644 index 0000000000000..fbc0b9be70871 --- /dev/null +++ b/.changeset/stupid-pears-cough.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Patch for L1 syncing nodes that got stuck after DTL batch sync errors diff --git a/l2geth/rollup/client.go b/l2geth/rollup/client.go index 64850c7b7f79c..beaff0d375b4e 100644 --- a/l2geth/rollup/client.go +++ b/l2geth/rollup/client.go @@ -120,6 +120,7 @@ type RollupClient interface { GetEnqueue(index uint64) (*types.Transaction, error) GetLatestEnqueue() (*types.Transaction, error) GetLatestEnqueueIndex() (*uint64, error) + GetRawTransaction(uint64, Backend) (*TransactionResponse, error) GetTransaction(uint64, Backend) (*types.Transaction, error) GetLatestTransaction(Backend) (*types.Transaction, error) GetLatestTransactionIndex(Backend) (*uint64, error) @@ -419,7 +420,7 @@ func batchedTransactionToTransaction(res *transaction, chainID *big.Int) (*types } // 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) response, err := c.client.R(). SetPathParams(map[string]string{ @@ -438,6 +439,15 @@ func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transacti if !ok { 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) } diff --git a/l2geth/rollup/sync_service.go b/l2geth/rollup/sync_service.go index 709e11db442e9..c2743e05a2da1 100644 --- a/l2geth/rollup/sync_service.go +++ b/l2geth/rollup/sync_service.go @@ -278,6 +278,23 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { s.SetLatestL1Timestamp(context.Timestamp) s.SetLatestL1BlockNumber(context.BlockNumber) } 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) block := s.bc.GetBlockByNumber(*index + 1) if block == nil { diff --git a/l2geth/rollup/sync_service_test.go b/l2geth/rollup/sync_service_test.go index 6d7f16677178e..17778fad0056c 100644 --- a/l2geth/rollup/sync_service_test.go +++ b/l2geth/rollup/sync_service_test.go @@ -957,6 +957,8 @@ func newTestSyncService(isVerifier bool, alloc *common.Address) (*SyncService, c type mockClient struct { getEnqueueCallCount int getEnqueue []*types.Transaction + getRawTransactionCallCount int + getRawTransaction []*TransactionResponse getTransactionCallCount int getTransaction []*types.Transaction getEthContextCallCount int @@ -974,6 +976,7 @@ func setupMockClient(service *SyncService, responses map[string]interface{}) { func newMockClient(responses map[string]interface{}) *mockClient { getEnqueueResponses := []*types.Transaction{} + getRawTransactionResponses := []*TransactionResponse{} getTransactionResponses := []*types.Transaction{} getEthContextResponses := []*EthContext{} getLatestEthContextResponse := &EthContext{} @@ -983,6 +986,10 @@ func newMockClient(responses map[string]interface{}) *mockClient { if ok { getEnqueueResponses = enqueue.([]*types.Transaction) } + getRawTx, ok := responses["GetRawTransaction"] + if ok { + getRawTransactionResponses = getRawTx.([]*TransactionResponse) + } getTx, ok := responses["GetTransaction"] if ok { getTransactionResponses = getTx.([]*types.Transaction) @@ -1002,6 +1009,7 @@ func newMockClient(responses map[string]interface{}) *mockClient { return &mockClient{ getEnqueue: getEnqueueResponses, + getRawTransaction: getRawTransactionResponses, getTransaction: getTransactionResponses, getEthContext: getEthContextResponses, getLatestEthContext: getLatestEthContextResponse, @@ -1025,6 +1033,15 @@ func (m *mockClient) GetLatestEnqueue() (*types.Transaction, error) { 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) { if m.getTransactionCallCount < len(m.getTransaction) { tx := m.getTransaction[m.getTransactionCallCount]