diff --git a/op-supervisor/supervisor/backend/backend.go b/op-supervisor/supervisor/backend/backend.go index 34b6703d63ee2..cbd03eeeaaaf9 100644 --- a/op-supervisor/supervisor/backend/backend.go +++ b/op-supervisor/supervisor/backend/backend.go @@ -444,6 +444,30 @@ func (su *SupervisorBackend) SafeView(ctx context.Context, chainID types.ChainID }, nil } +func (su *SupervisorBackend) LocalSafe(ctx context.Context, chainID types.ChainID) (eth.BlockID, eth.BlockID, error) { + df, d, err := su.chainDBs.LocalSafe(chainID) + if err != nil { + return eth.BlockID{}, eth.BlockID{}, err + } + return df.ID(), d.ID(), nil +} + +func (su *SupervisorBackend) LatestUnsafe(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { + v, err := su.chainDBs.LocalUnsafe(chainID) + if err != nil { + return eth.BlockID{}, err + } + return v.ID(), nil +} + +func (su *SupervisorBackend) SafeDerivedAt(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockID) (eth.BlockID, error) { + v, err := su.chainDBs.SafeDerivedAt(chainID, derivedFrom) + if err != nil { + return eth.BlockID{}, err + } + return v.ID(), nil +} + func (su *SupervisorBackend) Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) { v, err := su.chainDBs.Finalized(chainID) if err != nil { diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index 5e9503c39ade5..e2bfbe7061602 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -103,6 +103,18 @@ func (db *ChainsDB) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) erro return nil } +func (db *ChainsDB) SafeDerivedAt(chainID types.ChainID, derivedFrom eth.BlockID) (types.BlockSeal, error) { + lDB, ok := db.localDBs.Get(chainID) + if !ok { + return types.BlockSeal{}, types.ErrUnknownChain + } + derived, err := lDB.LastDerivedAt(derivedFrom) + if err != nil { + return types.BlockSeal{}, fmt.Errorf("failed to find derived block %s: %w", derivedFrom, err) + } + return derived, nil +} + func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) { eventsDB, ok := db.logDBs.Get(chainID) if !ok { diff --git a/op-supervisor/supervisor/backend/syncnode/controller.go b/op-supervisor/supervisor/backend/syncnode/controller.go index 5253d240d5131..8ffeacf19adde 100644 --- a/op-supervisor/supervisor/backend/syncnode/controller.go +++ b/op-supervisor/supervisor/backend/syncnode/controller.go @@ -27,6 +27,10 @@ type chainsDB interface { type backend interface { UpdateLocalSafe(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockRef, lastDerived eth.BlockRef) error UpdateLocalUnsafe(ctx context.Context, chainID types.ChainID, head eth.BlockRef) error + LocalSafe(ctx context.Context, chainID types.ChainID) (derivedFrom eth.BlockID, derived eth.BlockID, err error) + LatestUnsafe(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) + SafeDerivedAt(ctx context.Context, chainID types.ChainID, derivedFrom eth.BlockID) (derived eth.BlockID, err error) + Finalized(ctx context.Context, chainID types.ChainID) (eth.BlockID, error) } const ( @@ -175,9 +179,47 @@ func (m *ManagedNode) onDerivationUpdate(pair types.DerivedPair) { if err := m.backend.UpdateLocalSafe(ctx, m.chainID, pair.DerivedFrom, pair.Derived); err != nil { m.log.Warn("Backend failed to process local-safe update", "derived", pair.Derived, "derivedFrom", pair.DerivedFrom, "err", err) - // TODO: if conflict error -> send reset to drop - // TODO: if future error -> send reset to rewind - // TODO: if out of order -> warn, just old data + m.resetSignal(err, pair.DerivedFrom) + } +} + +func (m *ManagedNode) resetSignal(errSignal error, l1Ref eth.BlockRef) { + // if conflict error -> send reset to drop + // if future error -> send reset to rewind + // if out of order -> warn, just old data + ctx, cancel := context.WithTimeout(m.ctx, dbTimeout) + defer cancel() + u, err := m.backend.LatestUnsafe(ctx, m.chainID) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + f, err := m.backend.Finalized(ctx, m.chainID) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + switch errSignal { + case types.ErrConflict: + s, err := m.backend.SafeDerivedAt(ctx, m.chainID, l1Ref.ID()) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + log.Debug("Node detected conflict, resetting", "unsafe", u, "safe", s, "finalized", f) + err = m.Node.Reset(ctx, u, s, f) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + case types.ErrFuture: + _, s, err := m.backend.LocalSafe(ctx, m.chainID) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + log.Debug("Node detected future block, resetting", "unsafe", u, "safe", s, "finalized", f) + err = m.Node.Reset(ctx, u, s, f) + if err != nil { + m.log.Warn("Node failed to reset", "err", err) + } + case types.ErrOutOfOrder: + m.log.Warn("Node detected out of order block", "unsafe", u, "finalized", f) } }