origin_selector.go 2.89 KB
Newer Older
1 2 3 4
package driver

import (
	"context"
5 6
	"errors"
	"fmt"
7

8
	"github.com/ethereum/go-ethereum"
9 10 11 12
	"github.com/ethereum/go-ethereum/log"

	"github.com/ethereum-optimism/optimism/op-node/rollup"
	"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
13
	"github.com/ethereum-optimism/optimism/op-service/eth"
14 15 16 17 18 19 20 21 22 23 24
)

type L1Blocks interface {
	derive.L1BlockRefByHashFetcher
	derive.L1BlockRefByNumberFetcher
}

type L1OriginSelector struct {
	log log.Logger
	cfg *rollup.Config

25
	l1 L1Blocks
26 27
}

28
func NewL1OriginSelector(log log.Logger, cfg *rollup.Config, l1 L1Blocks) *L1OriginSelector {
29
	return &L1OriginSelector{
30 31 32
		log: log,
		cfg: cfg,
		l1:  l1,
33 34 35 36 37 38
	}
}

// FindL1Origin determines what the next L1 Origin should be.
// The L1 Origin is either the L2 Head's Origin, or the following L1 block
// if the next L2 block's time is greater than or equal to the L2 Head's Origin.
39 40
func (los *L1OriginSelector) FindL1Origin(ctx context.Context, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
	// Grab a reference to the current L1 origin block. This call is by hash and thus easily cached.
41 42 43 44
	currentOrigin, err := los.l1.L1BlockRefByHash(ctx, l2Head.L1Origin.Hash)
	if err != nil {
		return eth.L1BlockRef{}, err
	}
45 46
	log := los.log.New("current", currentOrigin, "current_time", currentOrigin.Time,
		"l2_head", l2Head, "l2_head_time", l2Head.Time)
47

48 49 50 51
	// If we are past the sequencer depth, we may want to advance the origin, but need to still
	// check the time of the next origin.
	pastSeqDrift := l2Head.Time+los.cfg.BlockTime > currentOrigin.Time+los.cfg.MaxSequencerDrift
	if pastSeqDrift {
52
		log.Warn("Next L2 block time is past the sequencer drift + current origin time")
53 54 55 56
	}

	// Attempt to find the next L1 origin block, where the next origin is the immediate child of
	// the current origin block.
57
	// The L1 source can be shimmed to hide new L1 blocks and enforce a sequencer confirmation distance.
58 59
	nextOrigin, err := los.l1.L1BlockRefByNumber(ctx, currentOrigin.Number+1)
	if err != nil {
60 61 62 63 64 65 66 67
		if pastSeqDrift {
			return eth.L1BlockRef{}, fmt.Errorf("cannot build next L2 block past current L1 origin %s by more than sequencer time drift, and failed to find next L1 origin: %w", currentOrigin, err)
		}
		if errors.Is(err, ethereum.NotFound) {
			log.Debug("No next L1 block found, repeating current origin")
		} else {
			log.Error("Failed to get next origin. Falling back to current origin", "err", err)
		}
68 69 70 71 72 73 74 75 76 77 78 79 80 81
		return currentOrigin, nil
	}

	// If the next L2 block time is greater than the next origin block's time, we can choose to
	// start building on top of the next origin. Sequencer implementation has some leeway here and
	// could decide to continue to build on top of the previous origin until the Sequencer runs out
	// of slack. For simplicity, we implement our Sequencer to always start building on the latest
	// L1 block when we can.
	if l2Head.Time+los.cfg.BlockTime >= nextOrigin.Time {
		return nextOrigin, nil
	}

	return currentOrigin, nil
}