• protolambda's avatar
    op-supervisor: Cross-safe updates [rebased] (#12624) · da4c33c5
    protolambda authored
    * op-supervisor: cross-safe-updates PR squashed
    
    op-supervisor: experimental cross-safety, with hazard detection
    
    tweak: Add some errors/error returns in backend/cross.
    
    wip: Chain index <> ID mapping.
    
    fix: Check parent instead of re-checking hazardBlock.
    
    Remove Hazard Work
    
    Write missing DB Bindings OpenBlock, LocallyDerivedFrom, CrossDerivedFrom
    
    Configurable WorkFn for Workers
    
    op-supervisor: move chain-index <> chain ID translation into dependency set, fix some interfaces
    
    op-supervisor: update cross-safety worker routine
    
    op-supervisor: update more error handling
    
    op-supervisor: move errors to types package
    
    op-supervisor: check CanExecuteAt and CanInitiateAt
    
    op-supervisor: determine cross-safe candidate and L1 scope, and more fixes
    
    todo L1 scope increment
    
    op-supervisor: cross-safe L1 scope bump
    
    op-supervisor: dependency set getter
    
    op-supervisor: L1 scope increment fix
    
    op-supervisor: fix cross-safe updates typing
    
    op-node: signal L1 traversal of derivation to supervisor
    
    op-supervisor: fromda fixes and tests
    
    op-supervisor: fix OpenBlock, fix/add missing interface methods, hook up cross-safe worker routines
    
    OpenBlock to return map[uint32]ExecutingMessage
    
    Add Frontier Unit Tests
    
    fix WithParent panic
    
    op-node: register L1 traversal with op-supervisor
    
    op-node,op-supervisor: add logging, temp work around for interop local-safe updates
    
    Add safe_start_test, unsafe_start_test
    
    Add safe_update_test and unsafe_update_test
    
    add worker_test
    
    op-supervisor: fix cross-safe L1 scope bumping
    
    op-supervisor: fix logs DB test
    Co-authored-by: default avataraxelKingsley <axel.kingsley@gmail.com>
    Co-authored-by: default avatarTyler Smith <mail@tcry.pt>
    
    * op-node: fix interop deriver test
    
    * op-e2e: fix interop action test
    
    * op-supervisor: improve map init
    
    * op-node: link interop TODO comment to issue, in engine events emitter
    
    * op-supervisor: cleanup Worker instances of tests
    
    ---------
    Co-authored-by: default avataraxelKingsley <axel.kingsley@gmail.com>
    Co-authored-by: default avatarTyler Smith <mail@tcry.pt>
    da4c33c5
safe_frontier.go 2.58 KB
package cross

import (
	"errors"
	"fmt"

	"github.com/ethereum-optimism/optimism/op-service/eth"
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)

type SafeFrontierCheckDeps interface {
	CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error)

	CrossDerivedFrom(chainID types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error)

	DependencySet() depset.DependencySet
}

// HazardSafeFrontierChecks verifies all the hazard blocks are either:
//   - already cross-safe.
//   - the first (if not first: local blocks to verify before proceeding)
//     local-safe block, after the cross-safe block.
func HazardSafeFrontierChecks(d SafeFrontierCheckDeps, inL1DerivedFrom eth.BlockID, hazards map[types.ChainIndex]types.BlockSeal) error {
	depSet := d.DependencySet()
	for hazardChainIndex, hazardBlock := range hazards {
		hazardChainID, err := depSet.ChainIDFromIndex(hazardChainIndex)
		if err != nil {
			if errors.Is(err, types.ErrUnknownChain) {
				err = fmt.Errorf("cannot cross-safe verify block %s of unknown chain index %s: %w", hazardBlock, hazardChainIndex, types.ErrConflict)
			}
			return err
		}
		initDerivedFrom, err := d.CrossDerivedFrom(hazardChainID, hazardBlock.ID())
		if err != nil {
			if errors.Is(err, types.ErrFuture) {
				// If not in cross-safe scope, then check if it's the candidate cross-safe block.
				initDerivedFrom, initSelf, err := d.CandidateCrossSafe(hazardChainID)
				if err != nil {
					return fmt.Errorf("failed to determine cross-safe candidate block of hazard dependency %s (chain %s): %w", hazardBlock, hazardChainID, err)
				}
				if initSelf.Number == hazardBlock.Number && initSelf.ID() != hazardBlock.ID() {
					return fmt.Errorf("expected block %s (chain %d) does not match candidate local-safe block %s: %w",
						hazardBlock, hazardChainID, initSelf, types.ErrConflict)
				}
				if initDerivedFrom.Number > inL1DerivedFrom.Number {
					return fmt.Errorf("local-safe hazard block %s derived from L1 block %s is after scope %s: %w",
						hazardBlock.ID(), initDerivedFrom, inL1DerivedFrom, types.ErrOutOfScope)
				}
			} else {
				return fmt.Errorf("failed to determine cross-derived of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err)
			}
		} else if initDerivedFrom.Number > inL1DerivedFrom.Number {
			return fmt.Errorf("cross-safe hazard block %s derived from L1 block %s is after scope %s: %w",
				hazardBlock.ID(), initDerivedFrom, inL1DerivedFrom, types.ErrOutOfScope)
		}
	}
	return nil
}