diff --git a/op-supervisor/supervisor/backend/db/fromda/db.go b/op-supervisor/supervisor/backend/db/fromda/db.go index c4c98624d26d..48d8564ced20 100644 --- a/op-supervisor/supervisor/backend/db/fromda/db.go +++ b/op-supervisor/supervisor/backend/db/fromda/db.go @@ -189,7 +189,8 @@ func (db *DB) PreviousDerivedFrom(derivedFrom eth.BlockID) (prevDerivedFrom type if self.derivedFrom.Number == 0 { return types.BlockSeal{}, nil } else { - return types.BlockSeal{}, fmt.Errorf("cannot find previous derived before start of database: %s", derivedFrom) + return types.BlockSeal{}, + fmt.Errorf("cannot find previous derived before start of database: %s (%w)", derivedFrom, types.ErrPreviousToFirst) } } prev, err := db.readAt(selfIndex - 1) diff --git a/op-supervisor/supervisor/backend/db/query.go b/op-supervisor/supervisor/backend/db/query.go index e3040998859f..bbee01d71b4d 100644 --- a/op-supervisor/supervisor/backend/db/query.go +++ b/op-supervisor/supervisor/backend/db/query.go @@ -154,7 +154,11 @@ func (db *ChainsDB) CrossDerivedFromBlockRef(chainID types.ChainID, derived eth. return eth.BlockRef{}, err } parent, err := xdb.PreviousDerivedFrom(res.ID()) - if err != nil { + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + return res.ForceWithParent(eth.BlockID{}), nil + } else if err != nil { return eth.BlockRef{}, err } return res.MustWithParent(parent.ID()), nil @@ -266,7 +270,11 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c candidateRef := candidate.MustWithParent(crossDerived.ID()) parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID()) - if err != nil { + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + parentDerivedFrom = types.BlockSeal{} + } else if err != nil { return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err) } candidateFromRef := candidateFrom.MustWithParent(parentDerivedFrom.ID()) @@ -275,12 +283,18 @@ func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, c if candidateFrom.Number > crossDerivedFrom.Number+1 { // If we are not ready to process the candidate block, // then we need to stick to the current scope, so the caller can bump up from there. + var crossDerivedFromRef eth.BlockRef parent, err := lDB.PreviousDerivedFrom(crossDerivedFrom.ID()) - if err != nil { - return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", - crossDerivedFrom, err) + // if we are working with the first item in the database, PreviousDerivedFrom will return ErrPreviousToFirst + // in which case we can attach a zero parent to the cross-derived-from block, as the parent block is unknown + if errors.Is(err, types.ErrPreviousToFirst) { + crossDerivedFromRef = crossDerivedFrom.ForceWithParent(eth.BlockID{}) + } else if err != nil { + return eth.BlockRef{}, eth.BlockRef{}, + fmt.Errorf("failed to find parent-block of cross-derived-from %s: %w", crossDerivedFrom, err) + } else { + crossDerivedFromRef = crossDerivedFrom.MustWithParent(parent.ID()) } - crossDerivedFromRef := crossDerivedFrom.MustWithParent(parent.ID()) return crossDerivedFromRef, eth.BlockRef{}, fmt.Errorf("candidate is from %s, while current scope is %s: %w", candidateFrom, crossDerivedFrom, types.ErrOutOfScope) diff --git a/op-supervisor/supervisor/types/error.go b/op-supervisor/supervisor/types/error.go index b4e1c57ba908..fc493fb87f91 100644 --- a/op-supervisor/supervisor/types/error.go +++ b/op-supervisor/supervisor/types/error.go @@ -20,6 +20,9 @@ var ( // ErrOutOfScope is when data is accessed, but access is not allowed, because of a limited scope. // E.g. when limiting scope to L2 blocks derived from a specific subset of the L1 chain. ErrOutOfScope = errors.New("out of scope") + // ErrPreviousToFirst is when you try to get the previous block of the first block + // E.g. when calling PreviousDerivedFrom on the first L1 block in the DB. + ErrPreviousToFirst = errors.New("cannot get parent of first block in the database") // ErrUnknownChain is when a chain is unknown, not in the dependency set. ErrUnknownChain = errors.New("unknown chain") // ErrNoRPCSource happens when a sub-service needs an RPC data source, but is not configured with one.