github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/plasma_data_source.go (about)

     1  package derive
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	plasma "github.com/ethereum-optimism/optimism/op-plasma"
     9  	"github.com/ethereum-optimism/optimism/op-service/eth"
    10  	"github.com/ethereum/go-ethereum/log"
    11  )
    12  
    13  // PlasmaDataSource is a data source that fetches inputs from a plasma DA provider given
    14  // their onchain commitments. Same as CalldataSource it will keep attempting to fetch.
    15  type PlasmaDataSource struct {
    16  	log     log.Logger
    17  	src     DataIter
    18  	fetcher PlasmaInputFetcher
    19  	l1      L1Fetcher
    20  	id      eth.BlockID
    21  	// keep track of a pending commitment so we can keep trying to fetch the input.
    22  	comm plasma.Keccak256Commitment
    23  }
    24  
    25  func NewPlasmaDataSource(log log.Logger, src DataIter, l1 L1Fetcher, fetcher PlasmaInputFetcher, id eth.BlockID) *PlasmaDataSource {
    26  	return &PlasmaDataSource{
    27  		log:     log,
    28  		src:     src,
    29  		fetcher: fetcher,
    30  		l1:      l1,
    31  		id:      id,
    32  	}
    33  }
    34  
    35  func (s *PlasmaDataSource) Next(ctx context.Context) (eth.Data, error) {
    36  	// Process origin syncs the challenge contract events and updates the local challenge states
    37  	// before we can proceed to fetch the input data. This function can be called multiple times
    38  	// for the same origin and noop if the origin was already processed. It is also called if
    39  	// there is not commitment in the current origin.
    40  	if err := s.fetcher.AdvanceL1Origin(ctx, s.l1, s.id); err != nil {
    41  		if errors.Is(err, plasma.ErrReorgRequired) {
    42  			return nil, NewResetError(fmt.Errorf("new expired challenge"))
    43  		}
    44  		return nil, NewTemporaryError(fmt.Errorf("failed to advance plasma L1 origin: %w", err))
    45  	}
    46  
    47  	if s.comm == nil {
    48  		// the l1 source returns the input commitment for the batch.
    49  		data, err := s.src.Next(ctx)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  
    54  		if len(data) == 0 {
    55  			return nil, NotEnoughData
    56  		}
    57  		// If the tx data type is not plasma, we forward it downstream to let the next
    58  		// steps validate and potentially parse it as L1 DA inputs.
    59  		if data[0] != plasma.TxDataVersion1 {
    60  			return data, nil
    61  		}
    62  
    63  		// validate batcher inbox data is a commitment.
    64  		comm, err := plasma.DecodeKeccak256(data[1:])
    65  		if err != nil {
    66  			s.log.Warn("invalid commitment", "commitment", data, "err", err)
    67  			return s.Next(ctx)
    68  		}
    69  		s.comm = comm
    70  	}
    71  	// use the commitment to fetch the input from the plasma DA provider.
    72  	data, err := s.fetcher.GetInput(ctx, s.l1, s.comm, s.id)
    73  	// GetInput may call for a reorg if the pipeline is stalled and the plasma DA manager
    74  	// continued syncing origins detached from the pipeline origin.
    75  	if errors.Is(err, plasma.ErrReorgRequired) {
    76  		// challenge for a new previously derived commitment expired.
    77  		return nil, NewResetError(err)
    78  	} else if errors.Is(err, plasma.ErrExpiredChallenge) {
    79  		// this commitment was challenged and the challenge expired.
    80  		s.log.Warn("challenge expired, skipping batch", "comm", s.comm)
    81  		s.comm = nil
    82  		// skip the input
    83  		return s.Next(ctx)
    84  	} else if errors.Is(err, plasma.ErrMissingPastWindow) {
    85  		return nil, NewCriticalError(fmt.Errorf("data for comm %x not available: %w", s.comm, err))
    86  	} else if errors.Is(err, plasma.ErrPendingChallenge) {
    87  		// continue stepping without slowing down.
    88  		return nil, NotEnoughData
    89  	} else if err != nil {
    90  		// return temporary error so we can keep retrying.
    91  		return nil, NewTemporaryError(fmt.Errorf("failed to fetch input data with comm %x from da service: %w", s.comm, err))
    92  	}
    93  	// inputs are limited to a max size to ensure they can be challenged in the DA contract.
    94  	if len(data) > plasma.MaxInputSize {
    95  		s.log.Warn("input data exceeds max size", "size", len(data), "max", plasma.MaxInputSize)
    96  		s.comm = nil
    97  		return s.Next(ctx)
    98  	}
    99  	// reset the commitment so we can fetch the next one from the source at the next iteration.
   100  	s.comm = nil
   101  	return data, nil
   102  }