query.go 15 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
func (db *ChainsDB) LocalUnsafe(chainID types.ChainID) (types.BlockSeal, error) {
107
	eventsDB, ok := db.logDBs.Get(chainID)
108
	if !ok {
109
		return types.BlockSeal{}, types.ErrUnknownChain
110 111 112
	}
	n, ok := eventsDB.LatestSealedBlockNum()
	if !ok {
113
		return types.BlockSeal{}, types.ErrFuture
114 115 116 117 118
	}
	return eventsDB.FindSealedBlock(n)
}

func (db *ChainsDB) CrossUnsafe(chainID types.ChainID) (types.BlockSeal, error) {
119
	result, ok := db.crossUnsafe.Get(chainID)
120
	if !ok {
121
		return types.BlockSeal{}, types.ErrUnknownChain
122
	}
123
	crossUnsafe := result.Get()
124
	// Fall back to cross-safe if cross-unsafe is not known yet
125
	if crossUnsafe == (types.BlockSeal{}) {
126 127 128 129 130 131
		_, crossSafe, err := db.CrossSafe(chainID)
		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)
		}
		return crossSafe, nil
	}
132
	return crossUnsafe, nil
133 134
}

135
func (db *ChainsDB) LocalSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) {
136
	localDB, ok := db.localDBs.Get(chainID)
137
	if !ok {
138
		return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain
139
	}
140
	return localDB.Latest()
141 142
}

143
func (db *ChainsDB) CrossSafe(chainID types.ChainID) (derivedFrom types.BlockSeal, derived types.BlockSeal, err error) {
144
	crossDB, ok := db.crossDBs.Get(chainID)
145
	if !ok {
146
		return types.BlockSeal{}, types.BlockSeal{}, types.ErrUnknownChain
147
	}
148
	return crossDB.Latest()
149 150
}

151
func (db *ChainsDB) Finalized(chainID types.ChainID) (types.BlockSeal, error) {
152
	finalizedL1 := db.finalizedL1.Get()
153
	if finalizedL1 == (eth.L1BlockRef{}) {
154
		return types.BlockSeal{}, errors.New("no finalized L1 signal, cannot determine L2 finality yet")
155 156 157
	}
	derived, err := db.LastDerivedFrom(chainID, finalizedL1.ID())
	if err != nil {
158
		return types.BlockSeal{}, errors.New("could not find what was last derived from the finalized L1 block")
159 160 161 162
	}
	return derived, nil
}

163
func (db *ChainsDB) LastDerivedFrom(chainID types.ChainID, derivedFrom eth.BlockID) (derived types.BlockSeal, err error) {
164
	crossDB, ok := db.crossDBs.Get(chainID)
165
	if !ok {
166
		return types.BlockSeal{}, types.ErrUnknownChain
167
	}
168
	return crossDB.LastDerivedAt(derivedFrom)
169 170
}

171 172 173 174
// 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)
175
	if !ok {
176 177
		return eth.BlockRef{}, types.ErrUnknownChain
	}
178
	res, err := xdb.DerivedFrom(derived)
179 180 181
	if err != nil {
		return eth.BlockRef{}, err
	}
182
	parent, err := xdb.PreviousDerivedFrom(res.ID())
183 184 185 186 187
	// 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 {
188
		return eth.BlockRef{}, err
189
	}
190
	return res.MustWithParent(parent.ID()), nil
191 192 193 194 195
}

// 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.
func (db *ChainsDB) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash common.Hash) (includedIn types.BlockSeal, err error) {
196
	logDB, ok := db.logDBs.Get(chain)
197
	if !ok {
198
		return types.BlockSeal{}, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain)
199 200 201 202
	}
	return logDB.Contains(blockNum, logIdx, logHash)
}

203 204 205
// 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) {
206
	logDB, ok := db.logDBs.Get(chainID)
207 208 209 210 211 212 213 214 215
	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) {
216
	lDB, ok := db.localDBs.Get(chain)
217 218 219 220 221 222 223 224 225
	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) {
226
	xDB, ok := db.crossDBs.Get(chain)
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	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) {
242
	xDB, ok := db.crossDBs.Get(chain)
243 244 245 246
	if !ok {
		return eth.BlockRef{}, eth.BlockRef{}, types.ErrUnknownChain
	}

247
	lDB, ok := db.localDBs.Get(chain)
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
	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)
			}
271 272 273 274 275 276 277 278
			// 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
279
			derivedRef := derived.MustWithParent(eth.BlockID{})
280
			return derivedFromRef, derivedRef, nil
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
		}
		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
	}

296
	candidateRef := candidate.MustWithParent(crossDerived.ID())
297 298

	parentDerivedFrom, err := lDB.PreviousDerivedFrom(candidateFrom.ID())
299 300 301 302 303
	// 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 {
304 305
		return eth.BlockRef{}, eth.BlockRef{}, fmt.Errorf("failed to find parent-block of derived-from %s: %w", candidateFrom, err)
	}
306
	candidateFromRef := candidateFrom.MustWithParent(parentDerivedFrom.ID())
307 308 309 310 311

	// 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.
312
		var crossDerivedFromRef eth.BlockRef
313
		parent, err := lDB.PreviousDerivedFrom(crossDerivedFrom.ID())
314 315 316 317 318 319 320 321 322
		// 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())
323 324 325 326 327 328 329 330 331
		}
		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) {
332
	lDB, ok := db.localDBs.Get(chain)
333 334 335 336 337 338 339
	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) {
340
	lDB, ok := db.localDBs.Get(chain)
341 342 343 344 345 346 347
	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) {
348
	lDB, ok := db.localDBs.Get(chain)
349 350 351 352 353 354 355
	if !ok {
		return eth.BlockRef{}, types.ErrUnknownChain
	}
	v, err := lDB.NextDerivedFrom(derivedFrom)
	if err != nil {
		return eth.BlockRef{}, err
	}
356
	return v.MustWithParent(derivedFrom), nil
357 358
}

359 360
// 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.
361
// Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest.
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
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
		}
	}
	_, crossSafe, err := db.CrossSafe(chainID)
	if err != nil {
		return types.Invalid, err
	}
	if crossSafe.Number >= blockNum {
		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
	}
	_, localSafe, err := db.LocalSafe(chainID)
	if err != nil {
		return types.Invalid, err
	}
	if blockNum <= localSafe.Number {
		return types.LocalSafe, nil
	}
	return types.LocalUnsafe, nil
}

func (db *ChainsDB) IteratorStartingAt(chain types.ChainID, sealedNum uint64, logIndex uint32) (logs.Iterator, error) {
395
	logDB, ok := db.logDBs.Get(chain)
396
	if !ok {
397
		return nil, fmt.Errorf("%w: %v", types.ErrUnknownChain, chain)
398 399 400
	}
	return logDB.IteratorStartingAt(sealedNum, logIndex)
}