Skip to content

Commit

Permalink
op-node: supervisor api usage updates
Browse files Browse the repository at this point in the history
  • Loading branch information
protolambda committed Sep 27, 2024
1 parent db067d4 commit 2a487bd
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 30 deletions.
19 changes: 19 additions & 0 deletions op-node/rollup/engine/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,22 @@ func (ev PromoteFinalizedEvent) String() string {
return "promote-finalized"
}

// FinalizedUpdateEvent signals that a block has been marked as finalized.
type FinalizedUpdateEvent struct {
Ref eth.L2BlockRef
}

func (ev FinalizedUpdateEvent) String() string {
return "finalized-update"
}

// RequestFinalizedUpdateEvent signals that a FinalizedUpdateEvent is needed.
type RequestFinalizedUpdateEvent struct{}

func (ev RequestFinalizedUpdateEvent) String() string {
return "request-finalized-update"
}

// CrossUpdateRequestEvent triggers update events to be emitted, repeating the current state.
type CrossUpdateRequestEvent struct {
CrossUnsafe bool
Expand Down Expand Up @@ -419,8 +435,11 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool {
return true
}
d.ec.SetFinalizedHead(x.Ref)
d.emitter.Emit(FinalizedUpdateEvent{Ref: x.Ref})
// Try to apply the forkchoice changes
d.emitter.Emit(TryUpdateEngineEvent{})
case RequestFinalizedUpdateEvent:

case CrossUpdateRequestEvent:
if x.CrossUnsafe {
d.emitter.Emit(CrossUnsafeUpdateEvent{
Expand Down
109 changes: 79 additions & 30 deletions op-node/rollup/interop/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type InteropBackend interface {

type L2Source interface {
L2BlockRefByNumber(context.Context, uint64) (eth.L2BlockRef, error)
L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error)
}

// InteropDeriver watches for update events (either real changes to block safety,
Expand Down Expand Up @@ -94,6 +95,10 @@ func (d *InteropDeriver) OnEvent(ev event.Event) bool {
if err := d.onCrossSafeUpdateEvent(x); err != nil {
d.log.Error("Failed to process cross-safe update", "err", err)
}
case engine.FinalizedUpdateEvent:
if err := d.onFinalizedUpdate(x); err != nil {
d.log.Error("Failed to process finalized update", "err", err)
}
default:
return false
}
Expand Down Expand Up @@ -143,17 +148,31 @@ func (d *InteropDeriver) onCrossUnsafe(x engine.CrossUnsafeUpdateEvent) error {
}
ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout)
defer cancel()

blockSafety, err := d.backend.CheckBlock(ctx, d.chainID, x.LocalUnsafe.Hash, x.LocalUnsafe.Number)
view := types.ReferenceView{
Local: x.LocalUnsafe.ID(),
Cross: x.CrossUnsafe.ID(),
}
result, err := d.backend.UnsafeView(ctx, d.chainID, view)
if err != nil {
return fmt.Errorf("failed to check interop safety of unsafe block: %w", err)
return fmt.Errorf("failed to check unsafe-level view: %w", err)
}
if result.Cross.Number == x.CrossUnsafe.Number {
// supervisor is in sync with op-node
return nil
}
if result.Cross.Number < x.CrossUnsafe.Number {
d.log.Warn("op-supervisor is behind known cross-unsafe block", "supervisor", result.Cross, "known", x.CrossUnsafe)
return nil
}
switch blockSafety {
case types.CrossUnsafe, types.CrossSafe, types.Finalized:
// Hold off on promoting higher than cross-unsafe,
// this will happen once we verify it to be local-safe first.
d.emitter.Emit(engine.PromoteCrossUnsafeEvent{Ref: candidate})
d.log.Info("New cross-unsafe block", "block", result.Cross.Number)
// Note: in the future we want to do reorg-checks,
// and initiate a reorg, if found to be on a conflicting chain.
ref, err := d.l2.L2BlockRefByHash(ctx, result.Cross.Hash)
if err != nil {
return fmt.Errorf("failed to get cross-unsafe block info of %s: %w", result.Cross, err)
}
d.emitter.Emit(engine.PromoteCrossUnsafeEvent{Ref: ref})

return nil
}

Expand All @@ -166,35 +185,65 @@ func (d *InteropDeriver) onCrossSafeUpdateEvent(x engine.CrossSafeUpdateEvent) e
if !d.cfg.IsInterop(d.cfg.TimestampForBlock(x.CrossSafe.Number + 1)) {
return nil
}

// TODO: we should just pull the latest local-safe cross-safe block from the op-supervisor,
// and trigger a derivation-reset if the local-safe block is older,
// or if the new cross-safe block is newer than the local-safe block.

ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout)
defer cancel()
candidate, err := d.l2.L2BlockRefByNumber(ctx, x.CrossSafe.Number+1)
view := types.ReferenceView{
Local: x.LocalSafe.ID(),
Cross: x.CrossSafe.ID(),
}
result, err := d.backend.SafeView(ctx, d.chainID, view)
if err != nil {
return fmt.Errorf("failed to fetch next cross-safe candidate: %w", err)
return fmt.Errorf("failed to check safe-level view: %w", err)
}
if result.Cross.Number == x.CrossSafe.Number {
// supervisor is in sync with op-node
return nil
}
if result.Cross.Number < x.CrossSafe.Number {
d.log.Warn("op-supervisor is behind known cross-safe block", "supervisor", result.Cross, "known", x.CrossSafe)
// TODO: we may want to force set the cross-safe block in the engine,
// and then reset derivation, so this op-node can help get the supervisor back in sync.
return nil
}
blockSafety, err := d.backend.CheckBlock(ctx, d.chainID, candidate.Hash, candidate.Number)
derivedFrom, err := d.backend.DerivedFrom(ctx, d.chainID, result.Cross.Hash, result.Cross.Number)
if err != nil {
return fmt.Errorf("failed to check interop safety of local-safe block: %w", err)
return fmt.Errorf("failed to get derived-from of %s: %w", result.Cross, err)
}
if blockSafety == types.CrossSafe || blockSafety == types.Finalized {
derivedFrom, err := d.backend.DerivedFrom(ctx, d.chainID, candidate.Hash, candidate.Number)
if err != nil {
return fmt.Errorf("failed to get derived-from of %s: %w", candidate, err)
}
d.emitter.Emit(engine.PromoteSafeEvent{
Ref: candidate,
DerivedFrom: derivedFrom,
})
ref, err := d.l2.L2BlockRefByHash(ctx, result.Cross.Hash)
if err != nil {
return fmt.Errorf("failed to get block ref of %s: %w", result.Cross, err)
}
if blockSafety == types.Finalized {
d.emitter.Emit(engine.PromoteFinalizedEvent{
Ref: candidate,
})
d.emitter.Emit(engine.PromoteSafeEvent{
Ref: ref,
DerivedFrom: derivedFrom,
})
d.emitter.Emit(engine.RequestFinalizedUpdateEvent{})
return nil
}

func (d *InteropDeriver) onFinalizedUpdate(x engine.FinalizedUpdateEvent) error {
ctx, cancel := context.WithTimeout(d.driverCtx, rpcTimeout)
defer cancel()

finalized, err := d.backend.Finalized(ctx, d.chainID)
if err != nil {
return fmt.Errorf("failed to retrieve finalized L2 block from supervisor: %w", err)
}
// Check if we can finalize something new
if finalized.Number == x.Ref.Number {
// supervisor is in sync with op-node
return nil
}
if finalized.Number < x.Ref.Number {
d.log.Warn("op-supervisor is behind known finalized block", "supervisor", finalized, "known", x.Ref)
return nil
}
ref, err := d.l2.L2BlockRefByHash(ctx, finalized.Hash)
if err != nil {
return fmt.Errorf("failed to get block ref of %s: %w", finalized, err)
}
d.emitter.Emit(engine.PromoteFinalizedEvent{
Ref: ref,
})
return nil
}

0 comments on commit 2a487bd

Please sign in to comment.