• 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
unsafe_frontier.go 2.64 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 UnsafeFrontierCheckDeps interface {
	ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (parent eth.BlockID, err error)

	IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error
	IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error

	DependencySet() depset.DependencySet
}

// HazardUnsafeFrontierChecks verifies all the hazard blocks are either:
//   - already cross-unsafe.
//   - the first (if not first: local blocks to verify before proceeding)
//     local-unsafe block, after the cross-unsafe block.
func HazardUnsafeFrontierChecks(d UnsafeFrontierCheckDeps, 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-unsafe verify block %s of unknown chain index %s: %w", hazardBlock, hazardChainIndex, types.ErrConflict)
			}
			return err
		}
		// Anything we depend on in this timestamp must be cross-unsafe already, or the first block after.
		err = d.IsCrossUnsafe(hazardChainID, hazardBlock.ID())
		if err != nil {
			if errors.Is(err, types.ErrFuture) {
				// Not already cross-unsafe, so we check if the block is local-unsafe
				// (a sanity check if part of the canonical chain).
				err = d.IsLocalUnsafe(hazardChainID, hazardBlock.ID())
				if err != nil {
					// can be ErrFuture (missing data) or ErrConflict (non-canonical)
					return fmt.Errorf("hazard block %s (chain %d) is not local-unsafe: %w", hazardBlock, hazardChainID, err)
				}
				// If it doesn't have a parent block, then there is no prior block required to be cross-safe
				if hazardBlock.Number > 0 {
					// Check that parent of hazardBlockID is cross-safe within view
					parent, err := d.ParentBlock(hazardChainID, hazardBlock.ID())
					if err != nil {
						return fmt.Errorf("failed to retrieve parent-block of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err)
					}
					if err := d.IsCrossUnsafe(hazardChainID, parent); err != nil {
						return fmt.Errorf("cannot rely on hazard-block %s (chain %s), parent block %s is not cross-unsafe: %w", hazardBlock, hazardChainID, parent, err)
					}
				}
			} else {
				return fmt.Errorf("failed to determine cross-derived of hazard block %s (chain %s): %w", hazardBlock, hazardChainID, err)
			}
		}
	}
	return nil
}