github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/l1_traversal.go (about) 1 package derive 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 9 "github.com/ethereum/go-ethereum" 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/log" 13 14 "github.com/ethereum-optimism/optimism/op-node/rollup" 15 "github.com/ethereum-optimism/optimism/op-service/eth" 16 ) 17 18 // L1 Traversal fetches the next L1 block and exposes it through the progress API 19 20 type L1BlockRefByNumberFetcher interface { 21 L1BlockRefByNumber(context.Context, uint64) (eth.L1BlockRef, error) 22 FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) 23 } 24 25 type L1Traversal struct { 26 block eth.L1BlockRef 27 done bool 28 l1Blocks L1BlockRefByNumberFetcher 29 log log.Logger 30 sysCfg eth.SystemConfig 31 cfg *rollup.Config 32 } 33 34 var _ ResettableStage = (*L1Traversal)(nil) 35 36 func NewL1Traversal(log log.Logger, cfg *rollup.Config, l1Blocks L1BlockRefByNumberFetcher) *L1Traversal { 37 return &L1Traversal{ 38 log: log, 39 l1Blocks: l1Blocks, 40 cfg: cfg, 41 } 42 } 43 44 func (l1t *L1Traversal) Origin() eth.L1BlockRef { 45 return l1t.block 46 } 47 48 // NextL1Block returns the next block. It does not advance, but it can only be 49 // called once before returning io.EOF 50 func (l1t *L1Traversal) NextL1Block(_ context.Context) (eth.L1BlockRef, error) { 51 if !l1t.done { 52 l1t.done = true 53 return l1t.block, nil 54 } else { 55 return eth.L1BlockRef{}, io.EOF 56 } 57 } 58 59 // AdvanceL1Block advances the internal state of L1 Traversal 60 func (l1t *L1Traversal) AdvanceL1Block(ctx context.Context) error { 61 origin := l1t.block 62 nextL1Origin, err := l1t.l1Blocks.L1BlockRefByNumber(ctx, origin.Number+1) 63 if errors.Is(err, ethereum.NotFound) { 64 l1t.log.Debug("can't find next L1 block info (yet)", "number", origin.Number+1, "origin", origin) 65 return io.EOF 66 } else if err != nil { 67 return NewTemporaryError(fmt.Errorf("failed to find L1 block info by number, at origin %s next %d: %w", origin, origin.Number+1, err)) 68 } 69 if l1t.block.Hash != nextL1Origin.ParentHash { 70 return NewResetError(fmt.Errorf("detected L1 reorg from %s to %s with conflicting parent %s", l1t.block, nextL1Origin, nextL1Origin.ParentID())) 71 } 72 73 // Parse L1 receipts of the given block and update the L1 system configuration 74 _, receipts, err := l1t.l1Blocks.FetchReceipts(ctx, nextL1Origin.Hash) 75 if err != nil { 76 return NewTemporaryError(fmt.Errorf("failed to fetch receipts of L1 block %s (parent: %s) for L1 sysCfg update: %w", nextL1Origin, origin, err)) 77 } 78 if err := UpdateSystemConfigWithL1Receipts(&l1t.sysCfg, receipts, l1t.cfg, nextL1Origin.Time); err != nil { 79 // the sysCfg changes should always be formatted correctly. 80 return NewCriticalError(fmt.Errorf("failed to update L1 sysCfg with receipts from block %s: %w", nextL1Origin, err)) 81 } 82 83 l1t.block = nextL1Origin 84 l1t.done = false 85 return nil 86 } 87 88 // Reset sets the internal L1 block to the supplied base. 89 func (l1t *L1Traversal) Reset(ctx context.Context, base eth.L1BlockRef, cfg eth.SystemConfig) error { 90 l1t.block = base 91 l1t.done = false 92 l1t.sysCfg = cfg 93 l1t.log.Info("completed reset of derivation pipeline", "origin", base) 94 return io.EOF 95 } 96 97 func (l1c *L1Traversal) SystemConfig() eth.SystemConfig { 98 return l1c.sysCfg 99 }