• Mark Tyneway's avatar
    chain-mon: migrate fault detector · 1d09a6a9
    Mark Tyneway authored
    This commit migrates the fault-detector service to the chain-mon
    package. This is useful because it moves towards standardizing all
    of the offchain monitoring services into a single typescript package,
    following the pattern that is already used in the repo.
    
    This simplifies all of the linting and removes boilerplate, as the
    services in chain-mon are all very similar and use the same
    dependencies.
    
    This also ports the tests from fault-detector to chain-mon, making
    it the first tests in the repo. Longer term we could likely remove
    the need to test using hardhat, but no need to do that for now.
    This will make it easier to upgrade to a newer version of hardhat
    as the version we are currently on only supports up to node v16
    which is no longer LTS.
    
    This change will save a bit of build time and a bit of time before
    each commit because it will be one less package that needs to be
    built or linted.
    1d09a6a9
helpers.ts 1.99 KB
import { Contract } from 'ethers'
import { Logger } from '@eth-optimism/common-ts'
import { BedrockOutputData } from '@eth-optimism/core-utils'

/**
 * Finds the BedrockOutputData that corresponds to a given output index.
 *
 * @param oracle Output oracle contract
 * @param index Output index to search for.
 * @returns BedrockOutputData corresponding to the output index.
 */
export const findOutputForIndex = async (
  oracle: Contract,
  index: number,
  logger?: Logger
): Promise<BedrockOutputData> => {
  try {
    const proposal = await oracle.getL2Output(index)
    return {
      outputRoot: proposal.outputRoot,
      l1Timestamp: proposal.timestamp.toNumber(),
      l2BlockNumber: proposal.l2BlockNumber.toNumber(),
      l2OutputIndex: index,
    }
  } catch (err) {
    logger?.fatal('error when calling L2OuputOracle.getL2Output', {
      errors: err,
    })
    throw new Error(`unable to find output for index ${index}`)
  }
}

/**
 * Finds the first state batch index that has not yet passed the fault proof window.
 *
 * @param oracle Output oracle contract.
 * @returns Starting state root batch index.
 */
export const findFirstUnfinalizedStateBatchIndex = async (
  oracle: Contract,
  fpw: number,
  logger?: Logger
): Promise<number> => {
  const latestBlock = await oracle.provider.getBlock('latest')
  const totalBatches = (await oracle.nextOutputIndex()).toNumber()

  // Perform a binary search to find the next batch that will pass the challenge period.
  let lo = 0
  let hi = totalBatches
  while (lo !== hi) {
    const mid = Math.floor((lo + hi) / 2)
    const outputData = await findOutputForIndex(oracle, mid, logger)

    if (outputData.l1Timestamp + fpw < latestBlock.timestamp) {
      lo = mid + 1
    } else {
      hi = mid
    }
  }

  // Result will be zero if the chain is less than FPW seconds old. Only returns undefined in the
  // case that no batches have been submitted for an entire challenge period.
  if (lo === totalBatches) {
    return undefined
  } else {
    return lo
  }
}