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 }