query.go 16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
package db

import (
	"errors"
	"fmt"

	"github.com/ethereum/go-ethereum/common"

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

func (db *ChainsDB) FindSealedBlock(chain types.ChainID, number uint64) (seal types.BlockSeal, err error) {
15
	logDB, ok := db.logDBs.Get(chain)
16
	if !ok {
17
		return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain)
18 19 20 21 22 23 24 25
	}
	return logDB.FindSealedBlock(number)
}

// LatestBlockNum returns the latest fully-sealed block number that has been recorded to the logs db
// for the given chain. It does not contain safety guarantees.
// The block number might not be available (empty database, or non-existent chain).
func (db *ChainsDB) LatestBlockNum(chain types.ChainID) (num uint64, ok bool) {
26
	logDB, knownChain := db.logDBs.Get(chain)
27 28 29 30 31 32
	if !knownChain {
		return 0, false
	}
	return logDB.LatestSealedBlockNum()
}

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
// LastCommonL1 returns the latest common L1 block between all chains in the database.
// it only considers block numbers, not hash. That's because the L1 source is the same for all chains
// this data can be used to determine the starting point for L1 processing
func (db *ChainsDB) LastCommonL1() (types.BlockSeal, error) {
	common := types.BlockSeal{}
	for _, chain := range db.depSet.Chains() {
		ldb, ok := db.localDBs.Get(chain)
		if !ok {
			return types.BlockSeal{}, types.ErrUnknownChain
		}
		_, derivedFrom, err := ldb.Latest()
		if err != nil {
			return types.BlockSeal{}, fmt.Errorf("failed to determine Last Common L1: %w", err)
		}
		common = derivedFrom
		// if the common block isn't yet set,
		// or if the new common block is older than the current common block
		// set the common block
		if common == (types.BlockSeal{}) ||
			derivedFrom.Number < common.Number {
			common = derivedFrom
		}
	}
	return common, nil
}

59
func (db *ChainsDB) IsCrossUnsafe(chainID types.ChainID, block eth.BlockID) error {
60
	v, ok := db.crossUnsafe.Get(chainID)
61 62 63
	if !ok {
		return types.ErrUnknownChain
	}
64 65
	crossUnsafe := v.Get()
	if crossUnsafe == (types.BlockSeal{}) {
66 67
		return types.ErrFuture
	}
68
	if block.Number > crossUnsafe.Number {
69 70 71 72 73 74 75
		return types.ErrFuture
	}
	// TODO(#11693): make cross-unsafe reorg safe
	return nil
}

func (db *ChainsDB) ParentBlock(chainID types.ChainID, parentOf eth.BlockID) (parent eth.BlockID, err error) {
76
	logDB, ok := db.logDBs.Get(chainID)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
	if !ok {
		return eth.BlockID{}, types.ErrUnknownChain
	}
	if parentOf.Number == 0 {
		return eth.BlockID{}, nil
	}
	// TODO(#11693): make parent-lookup reorg safe
	got, err := logDB.FindSealedBlock(parentOf.Number - 1)
	if err != nil {
		return eth.BlockID{}, err
	}
	return got.ID(), nil
}

func (db *ChainsDB) IsLocalUnsafe(chainID types.ChainID, block eth.BlockID) error {
92
	logDB, ok := db.logDBs.Get(chainID)
93 94 95 96 97 98 99 100 101 102 103 104 105
	if !ok {
		return types.ErrUnknownChain
	}
	got, err := logDB.FindSealedBlock(block.Number)
	if err != nil {
		return err
	}
	if got.ID() != block {
		return fmt.Errorf("found %s but was looking for unsafe block %s: %w", got, block, types.ErrConflict)
	}
	return nil
}

106 107 108 109 110 111 112 113 114 115 116 117
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
}

118
func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) {
119
	eventsDB, ok := db.logDBs.Get(chainID)
120
	if !ok {
121
		return types.BlockSeal{}, types.ErrUnknownChain
122 123 124
	}
	n, ok := eventsDB.LatestSealedBlockNum()
	if !ok {
125
		return types.BlockSeal{}, types.ErrFuture
126 127 128 129 130
	}
	return eventsDB.FindSealedBlock(n)
}

func (db *ChainsDB) CrossUnsafe(chainID types.ChainID) (types.BlockSeal, error) {
131
	result, ok := db.crossUnsafe.Get(chainID)
132
	if !ok {
133
		return types.BlockSeal{}, types.ErrUnknownChain
134
	}
135
	crossUnsafe := result.Get()
136
	// Fall back to cross-safe if cross-unsafe is not known yet
137
	if crossUnsafe == (types.BlockSeal{}) {
138
		crossSafe, err := db.CrossSafe(chainID)
139 140 141
		if err != nil {
			return types.BlockSeal{}, fmt.Errorf("no cross-unsafe known for chain %s, and failed to fall back to cross-safe value: %w", chainID, err)
		}
142
		return crossSafe.Derived, nil
143
	}
144
	return crossUnsafe, nil
145 146
}

147
func (db *ChainsDB) LocalSafe(chainID types.ChainID) (pair types.DerivedBlockSealPair, err error) {
148
	localDB, ok := db.localDBs.Get(chainID)
149
	if !ok {
150
		return types.DerivedBlockSealPair{}, types.ErrUnknownChain
151
	}
152 153
	df, d, err := localDB.Latest()
	return types.DerivedBlockSealPair{DerivedFrom: df, Derived: d}, err
154 155
}

156
func (db *ChainsDB) CrossSafe(chainID types.ChainID) (pair types.DerivedBlockSealPair, err error) {
157
	crossDB, ok := db.crossDBs.Get(chainID)
158
	if !ok {
159
		return types.DerivedBlockSealPair{}, types.ErrUnknownChain
160
	}
161 162
	df, d, err := crossDB.Latest()
	return types.DerivedBlockSealPair{DerivedFrom: df, Derived: d}, err
163 164
}

165 166 167 168
func (db *ChainsDB) FinalizedL1() eth.BlockRef {
	return db.finalizedL1.Get()
}

169
func (db *ChainsDB) Finalized(chainID types.ChainID) (types.BlockSeal, error) {
170
	finalizedL1 := db.finalizedL1.Get()
171
	if finalizedL1 == (eth.L1BlockRef{}) {
172
		return types.BlockSeal{}, fmt.Errorf("no finalized L1 signal, cannot determine L2 finality of chain %s yet", chainID)
173 174 175
	}
	derived, err := db.LastDerivedFrom(chainID, finalizedL1.ID())
	if err != nil {
176
		return types.BlockSeal{}, fmt.Errorf("could not find what was last derived in L2 chain %s from the finalized L1 block %s: %w", chainID, finalizedL1, err)
177 178 179 180
	}
	return derived, nil
}

181
func (db *ChainsDB) LastDerivedFrom(chainID types.ChainID, derivedFrom eth.BlockID) (derived types.BlockSeal, err error) {
182
	crossDB, ok := db.crossDBs.Get(chainID)
183
	if !ok {
184
		return types.BlockSeal{}, types.ErrUnknownChain
185
	}
186
	return crossDB.LastDerivedAt(derivedFrom)
187 188
}

189 190 191 192
// CrossDerivedFromBlockRef returns the block that the given block was derived from, if it exists in the cross derived-from storage.
// This includes the parent-block lookup. Use CrossDerivedFrom if no parent-block info is needed.
func (db *ChainsDB) CrossDerivedFromBlockRef(chainID types.ChainID, derived eth.BlockID) (derivedFrom eth.BlockRef, err error) {
	xdb, ok := db.crossDBs.Get(chainID)
193
	if !ok {
194 195
		return eth.BlockRef{}, types.ErrUnknownChain
	}
196
	res, err := xdb.DerivedFrom(derived)
197 198 199
	if err != nil {
		return eth.BlockRef{}, err
	}
200
	parent, err := xdb.PreviousDerivedFrom(res.ID())
201 202 203 204 205
	// 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 {
206
		return eth.BlockRef{}, err
207
	}
208
	return res.MustWithParent(parent.ID()), nil
209 210 211 212
}

// Check calls the underlying logDB to determine if the given log entry exists at the given location.
// If the block-seal of the block that includes the log is known, it is returned. It is fully zeroed otherwise, if the block is in-progress.
213
func (db *ChainsDB) Check(chain types.ChainID, blockNum uint64, timestamp uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) {
214
	logDB, ok := db.logDBs.Get(chain)
215
	if !ok {
216
		return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain)
217
	}
218 219 220 221 222 223 224 225 226 227 228 229 230
	includedIn, err = logDB.Contains(blockNum, logIdx, logHash)
	if err != nil {
		return types.BlockSeal{}, err
	}
	if includedIn.Timestamp != timestamp {
		return types.BlockSeal{},
			fmt.Errorf("log exists in block %s, but block timestamp %d does not match %d: %w",
				includedIn,
				includedIn.Timestamp,
				timestamp,
				types.ErrConflict)
	}
	return includedIn, nil
231 232
}

233 234 235
// OpenBlock returns the Executing Messages for the block at the given number on the given chain.
// it routes the request to the appropriate logDB.
func (db *ChainsDB) OpenBlock(chainID types.ChainID, blockNum uint64) (seal eth.BlockRef, logCount uint32, execMsgs map[uint32]*types.ExecutingMessage, err error) {
236
	logDB, ok := db.logDBs.Get(chainID)
237 238 239 240 241 242 243 244 245
	if !ok {
		return eth.BlockRef{}, 0, nil, types.ErrUnknownChain
	}
	return logDB.OpenBlock(blockNum)
}

// LocalDerivedFrom returns the block that the given block was derived from, if it exists in the local derived-from storage.
// it routes the request to the appropriate localDB.
func (db *ChainsDB) LocalDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) {
246
	lDB, ok := db.localDBs.Get(chain)
247 248 249 250 251 252 253 254 255
	if !ok {
		return types.BlockSeal{}, types.ErrUnknownChain
	}
	return lDB.DerivedFrom(derived)
}

// CrossDerivedFrom returns the block that the given block was derived from, if it exists in the cross derived-from storage.
// it routes the request to the appropriate crossDB.
func (db *ChainsDB) CrossDerivedFrom(chain types.ChainID, derived eth.BlockID) (derivedFrom types.BlockSeal, err error) {
256
	xDB, ok := db.crossDBs.Get(chain)
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
	if !ok {
		return types.BlockSeal{}, types.ErrUnknownChain
	}
	return xDB.DerivedFrom(derived)
}

// CandidateCrossSafe returns the candidate local-safe block that may become cross-safe.
//
// This returns ErrFuture if no block is known yet.
//
// Or ErrConflict if there is an inconsistency between the local-safe and cross-safe DB.
//
// Or ErrOutOfScope, with non-zero derivedFromScope,
// if additional L1 data is needed to cross-verify the candidate L2 block.
func (db *ChainsDB) CandidateCrossSafe(chain types.ChainID) (derivedFromScope, crossSafe eth.BlockRef, err error) {
272
	xDB, ok := db.crossDBs.Get(chain)
273 274 275 276
	if !ok {
		return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain
	}

277
	lDB, ok := db.localDBs.Get(chain)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	if !ok {
		return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain
	}

	// Example:
	// A B C D      <- L1
	// 1     2      <- L2
	// return:
	// (A, 0) -> initial scope, no L2 block yet. Genesis found to be cross-safe
	// (A, 1) -> 1 is determined cross-safe, won't be a candidate anymore after. 2 is the new candidate
	// (B, 2) -> 2 is out of scope, go to B
	// (C, 2) -> 2 is out of scope, go to C
	// (D, 2) -> 2 is in scope, stay on D, promote candidate to cross-safe
	// (D, 3) -> look at 3 next, see if we have to bump L1 yet, try with same L1 scope first

	crossDerivedFrom, crossDerived, err := xDB.Latest()
	if err != nil {
		if errors.Is(err, types.ErrFuture) {
			// If we do not have any cross-safe block yet, then return the first local-safe block.
			derivedFrom, derived, err := lDB.First()
			if err != nil {
				return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find first local-safe block: %w", err)
			}
301 302 303 304 305 306 307 308
			// the first derivedFrom (L1 block) is unlikely to be the genesis block,
			derivedFromRef, err := derivedFrom.WithParent(eth.BlockID{})
			if err != nil {
				// if the first derivedFrom isn't the genesis block, just warn and continue anyway
				db.logger.Warn("First DerivedFrom is not genesis block")
				derivedFromRef = derivedFrom.ForceWithParent(eth.BlockID{})
			}
			// the first derived must be the genesis block, panic otherwise
309
			derivedRef := derived.MustWithParent(eth.BlockID{})
310
			return derivedFromRef, derivedRef, nil
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
		}
		return eth.BlockRef{}, eth.BlockRef{}, err
	}
	// Find the local-safe block that comes right after the last seen cross-safe block.
	// Just L2 block by block traversal, conditional on being local-safe.
	// This will be the candidate L2 block to promote.

	// While the local-safe block isn't cross-safe given limited L1 scope, we'll keep bumping the L1 scope,
	// And update cross-safe accordingly.
	// This method will keep returning the latest known scope that has been verified to be cross-safe.
	candidateFrom, candidate, err := lDB.NextDerived(crossDerived.ID())
	if err != nil {
		return eth.BlockRef{}, eth.BlockRef{}, err
	}

326
	candidateRef := candidate.MustWithParent(crossDerived.ID())
327 328

	parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID())
329 330 331 332 333
	// 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 {
334 335
		return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err)
	}
336
	candidateFromRef := candidateFrom.MustWithParent(parentDerivedFrom.ID())
337 338 339 340 341

	// Allow increment of DA by 1, if we know the floor (due to local safety) is 1 ahead of the current cross-safe L1 scope.
	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.
342
		var crossDerivedFromRef eth.BlockRef
343
		parent, err := lDB.PreviousDerivedFrom(crossDerivedFrom.ID())
344 345 346 347 348 349 350 351 352
		// 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())
353 354 355 356 357 358 359 360 361
		}
		return crossDerivedFromRef, eth.BlockRef{},
			fmt.Errorf("candidate is from %s, while current scope is %s: %w",
				candidateFrom, crossDerivedFrom, types.ErrOutOfScope)
	}
	return candidateFromRef, candidateRef, nil
}

func (db *ChainsDB) PreviousDerived(chain types.ChainID, derived eth.BlockID) (prevDerived types.BlockSeal, err error) {
362
	lDB, ok := db.localDBs.Get(chain)
363 364 365 366 367 368 369
	if !ok {
		return types.BlockSeal{}, types.ErrUnknownChain
	}
	return lDB.PreviousDerived(derived)
}

func (db *ChainsDB) PreviousDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (prevDerivedFrom types.BlockSeal, err error) {
370
	lDB, ok := db.localDBs.Get(chain)
371 372 373 374 375 376 377
	if !ok {
		return types.BlockSeal{}, types.ErrUnknownChain
	}
	return lDB.PreviousDerivedFrom(derivedFrom)
}

func (db *ChainsDB) NextDerivedFrom(chain types.ChainID, derivedFrom eth.BlockID) (after eth.BlockRef, err error) {
378
	lDB, ok := db.localDBs.Get(chain)
379 380 381 382 383 384 385
	if !ok {
		return eth.BlockRef{}, types.ErrUnknownChain
	}
	v, err := lDB.NextDerivedFrom(derivedFrom)
	if err != nil {
		return eth.BlockRef{}, err
	}
386
	return v.MustWithParent(derivedFrom), nil
387 388
}

389 390
// Safest returns the strongest safety level that can be guaranteed for the given log entry.
// it assumes the log entry has already been checked and is valid, this function only checks safety levels.
391
// Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest.
392 393 394 395 396 397
func (db *ChainsDB) Safest(chainID types.ChainID, blockNum uint64, index uint32) (safest types.SafetyLevel, err error) {
	if finalized, err := db.Finalized(chainID); err == nil {
		if finalized.Number >= blockNum {
			return types.Finalized, nil
		}
	}
398
	crossSafe, err := db.CrossSafe(chainID)
399 400 401
	if err != nil {
		return types.Invalid, err
	}
402
	if crossSafe.Derived.Number >= blockNum {
403 404 405 406 407 408 409 410 411 412 413
		return types.CrossSafe, nil
	}
	crossUnsafe, err := db.CrossUnsafe(chainID)
	if err != nil {
		return types.Invalid, err
	}
	// TODO(#12425): API: "index" for in-progress block building shouldn't be exposed from DB.
	//  For now we're not counting anything cross-safe until the block is sealed.
	if blockNum <= crossUnsafe.Number {
		return types.CrossUnsafe, nil
	}
414
	localSafe, err := db.LocalSafe(chainID)
415 416 417
	if err != nil {
		return types.Invalid, err
	}
418
	if blockNum <= localSafe.Derived.Number {
419 420 421 422 423 424
		return types.LocalSafe, nil
	}
	return types.LocalUnsafe, nil
}

func (db *ChainsDB) IteratorStartingAt(chain types.ChainID, sealedNum uint64, logIndex uint32) (logs.Iterator, error) {
425
	logDB, ok := db.logDBs.Get(chain)
426
	if !ok {
427
		return nil, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain)
428 429 430
	}
	return logDB.IteratorStartingAt(sealedNum, logIndex)
}