• Sebastian Stammler's avatar
    Update op-geth dependency to upstream geth v1.13.8 and migrate to slog (#8917) · 15e868a6
    Sebastian Stammler authored
    * Update op-geth dependency (v1.13.8) & migrate to slog
    
    * op-e2e: format system_test.go
    
    * op-chain-ops/genesis: Ignore nil addresses in BuildL1DeveloperGenesis
    
    * go: Update to latest op-geth
    
    commit fb90ca39bc5c4f45e99ef320abfab85eeb56c561
    
    * update latest op-geth dependency
    
    * op-program,op-wheel: Use new StateDB.OpenStorageTrie
    
    * all: fix more slog stuff after merging
    
    * proxyd: update geth 1.13.8 & migrate to slog
    
    * op-ufm: update monorepo dependency to prev commit
    
    * testlog: Return pointer with FindLog
    
    * genesis: Parse addresses from dump string keys in BuildL1DeveloperGenesis
    
    * op-ufm: go mod tidy
    
    * update to latest op-geth
    
    * genesis: Update ForgeDump.UnmarshalJSON to latest geth types
    
    * eth: Use hexutils.U256 instead of uint256.Int as type in ExecutionPayload
    
    This fixes JSON mashaling.
    
    * op-e2e: fix usage of legacy geth levels
    
    * go: update latest op-geth dependency
    
    * check-ecotone: adapt to field type change
    
    * Resolve remaining TODOs
    
    * op-program: remove json-pretty formatting option from test
    
    * go: update to latest op-geth v1.101308.0-rc.1
    
    * op-dispute-mon: Fix logger setup
    
    * log: improve LevelFromString docs
    
    * op-e2e/config: treat EthNodeVerbosity as legacy log level
    
    * all: fix order of imports
    15e868a6
engine.go 5.25 KB
package l2

import (
	"context"
	"errors"
	"fmt"

	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
	"github.com/ethereum-optimism/optimism/op-node/rollup"
	"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
	"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
	"github.com/ethereum-optimism/optimism/op-service/eth"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/log"
)

var ErrNotFound = errors.New("not found")

type OracleEngine struct {
	api       *engineapi.L2EngineAPI
	backend   engineapi.EngineBackend
	rollupCfg *rollup.Config
}

func NewOracleEngine(rollupCfg *rollup.Config, logger log.Logger, backend engineapi.EngineBackend) *OracleEngine {
	engineAPI := engineapi.NewL2EngineAPI(logger, backend, nil)
	return &OracleEngine{
		api:       engineAPI,
		backend:   backend,
		rollupCfg: rollupCfg,
	}
}

func (o *OracleEngine) L2OutputRoot(l2ClaimBlockNum uint64) (eth.Bytes32, error) {
	outBlock := o.backend.GetHeaderByNumber(l2ClaimBlockNum)
	if outBlock == nil {
		return eth.Bytes32{}, fmt.Errorf("failed to get L2 block at %d", l2ClaimBlockNum)
	}
	stateDB, err := o.backend.StateAt(outBlock.Root)
	if err != nil {
		return eth.Bytes32{}, fmt.Errorf("failed to open L2 state db at block %s: %w", outBlock.Hash(), err)
	}
	withdrawalsTrie, err := stateDB.OpenStorageTrie(predeploys.L2ToL1MessagePasserAddr)
	if err != nil {
		return eth.Bytes32{}, fmt.Errorf("withdrawals trie unavailable at block %v: %w", outBlock.Hash(), err)
	}
	return rollup.ComputeL2OutputRootV0(eth.HeaderBlockInfo(outBlock), withdrawalsTrie.Hash())
}

func (o *OracleEngine) GetPayload(ctx context.Context, payloadInfo eth.PayloadInfo) (*eth.ExecutionPayloadEnvelope, error) {
	var res *eth.ExecutionPayloadEnvelope
	var err error
	switch method := o.rollupCfg.GetPayloadVersion(payloadInfo.Timestamp); method {
	case eth.GetPayloadV3:
		res, err = o.api.GetPayloadV3(ctx, payloadInfo.ID)
	case eth.GetPayloadV2:
		res, err = o.api.GetPayloadV2(ctx, payloadInfo.ID)
	default:
		return nil, fmt.Errorf("unsupported GetPayload version: %s", method)
	}
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (o *OracleEngine) ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) {
	switch method := o.rollupCfg.ForkchoiceUpdatedVersion(attr); method {
	case eth.FCUV3:
		return o.api.ForkchoiceUpdatedV3(ctx, state, attr)
	case eth.FCUV2:
		return o.api.ForkchoiceUpdatedV2(ctx, state, attr)
	case eth.FCUV1:
		return o.api.ForkchoiceUpdatedV1(ctx, state, attr)
	default:
		return nil, fmt.Errorf("unsupported ForkchoiceUpdated version: %s", method)
	}
}

func (o *OracleEngine) NewPayload(ctx context.Context, payload *eth.ExecutionPayload, parentBeaconBlockRoot *common.Hash) (*eth.PayloadStatusV1, error) {
	switch method := o.rollupCfg.NewPayloadVersion(uint64(payload.Timestamp)); method {
	case eth.NewPayloadV3:
		return o.api.NewPayloadV3(ctx, payload, []common.Hash{}, parentBeaconBlockRoot)
	case eth.NewPayloadV2:
		return o.api.NewPayloadV2(ctx, payload)
	default:
		return nil, fmt.Errorf("unsupported NewPayload version: %s", method)
	}
}

func (o *OracleEngine) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.ExecutionPayloadEnvelope, error) {
	block := o.backend.GetBlockByHash(hash)
	if block == nil {
		return nil, ErrNotFound
	}
	payload, err := eth.BlockAsPayload(block, o.rollupCfg.CanyonTime)
	if err != nil {
		return nil, err
	}
	return &eth.ExecutionPayloadEnvelope{
		ParentBeaconBlockRoot: block.BeaconRoot(),
		ExecutionPayload:      payload,
	}, nil
}

func (o *OracleEngine) PayloadByNumber(ctx context.Context, n uint64) (*eth.ExecutionPayloadEnvelope, error) {
	hash := o.backend.GetCanonicalHash(n)
	if hash == (common.Hash{}) {
		return nil, ErrNotFound
	}
	return o.PayloadByHash(ctx, hash)
}

func (o *OracleEngine) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) {
	var header *types.Header
	switch label {
	case eth.Unsafe:
		header = o.backend.CurrentHeader()
	case eth.Safe:
		header = o.backend.CurrentSafeBlock()
	case eth.Finalized:
		header = o.backend.CurrentFinalBlock()
	default:
		return eth.L2BlockRef{}, fmt.Errorf("unknown label: %v", label)
	}
	if header == nil {
		return eth.L2BlockRef{}, ErrNotFound
	}
	block := o.backend.GetBlockByHash(header.Hash())
	if block == nil {
		return eth.L2BlockRef{}, ErrNotFound
	}
	return derive.L2BlockToBlockRef(o.rollupCfg, block)
}

func (o *OracleEngine) L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) {
	block := o.backend.GetBlockByHash(l2Hash)
	if block == nil {
		return eth.L2BlockRef{}, ErrNotFound
	}
	return derive.L2BlockToBlockRef(o.rollupCfg, block)
}

func (o *OracleEngine) L2BlockRefByNumber(ctx context.Context, n uint64) (eth.L2BlockRef, error) {
	hash := o.backend.GetCanonicalHash(n)
	if hash == (common.Hash{}) {
		return eth.L2BlockRef{}, ErrNotFound
	}
	return o.L2BlockRefByHash(ctx, hash)
}

func (o *OracleEngine) SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error) {
	payload, err := o.PayloadByHash(ctx, hash)
	if err != nil {
		return eth.SystemConfig{}, err
	}
	return derive.PayloadToSystemConfig(o.rollupCfg, payload.ExecutionPayload)
}