github.com/ethereum-optimism/optimism@v1.7.2/packages/chain-mon/test/fault-mon/helpers.spec.ts (about)

     1  import hre from 'hardhat'
     2  import '@nomiclabs/hardhat-ethers'
     3  import { Contract, utils } from 'ethers'
     4  import { toRpcHexString } from '@eth-optimism/core-utils'
     5  import Artifact__L2OutputOracle from '@eth-optimism/contracts-bedrock/forge-artifacts/L2OutputOracle.sol/L2OutputOracle.json'
     6  import Artifact__Proxy from '@eth-optimism/contracts-bedrock/forge-artifacts/Proxy.sol/Proxy.json'
     7  import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
     8  
     9  import { expect } from './setup'
    10  import {
    11    findOutputForIndex,
    12    findFirstUnfinalizedOutputIndex,
    13  } from '../../src/fault-mon'
    14  
    15  describe('helpers', () => {
    16    const deployConfig = {
    17      l2OutputOracleSubmissionInterval: 6,
    18      l2BlockTime: 2,
    19      l2OutputOracleStartingBlockNumber: 0,
    20      l2OutputOracleStartingTimestamp: 0,
    21      l2OutputOracleProposer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    22      l2OutputOracleChallenger: '0x6925B8704Ff96DEe942623d6FB5e946EF5884b63',
    23      // Can be any non-zero value, 1000 is fine.
    24      finalizationPeriodSeconds: 1000,
    25    }
    26  
    27    let signer: SignerWithAddress
    28    before(async () => {
    29      ;[signer] = await hre.ethers.getSigners()
    30    })
    31  
    32    let L2OutputOracle: Contract
    33    let Proxy: Contract
    34    beforeEach(async () => {
    35      const Factory__Proxy = new hre.ethers.ContractFactory(
    36        Artifact__Proxy.abi,
    37        Artifact__Proxy.bytecode.object,
    38        signer
    39      )
    40  
    41      Proxy = await Factory__Proxy.deploy(signer.address)
    42  
    43      const Factory__L2OutputOracle = new hre.ethers.ContractFactory(
    44        Artifact__L2OutputOracle.abi,
    45        Artifact__L2OutputOracle.bytecode.object,
    46        signer
    47      )
    48  
    49      const L2OutputOracleImplementation = await Factory__L2OutputOracle.deploy()
    50  
    51      await Proxy.upgradeToAndCall(
    52        L2OutputOracleImplementation.address,
    53        L2OutputOracleImplementation.interface.encodeFunctionData('initialize', [
    54          deployConfig.l2OutputOracleSubmissionInterval,
    55          deployConfig.l2BlockTime,
    56          deployConfig.l2OutputOracleStartingBlockNumber,
    57          deployConfig.l2OutputOracleStartingTimestamp,
    58          deployConfig.l2OutputOracleProposer,
    59          deployConfig.l2OutputOracleChallenger,
    60          deployConfig.finalizationPeriodSeconds,
    61        ])
    62      )
    63  
    64      L2OutputOracle = new hre.ethers.Contract(
    65        Proxy.address,
    66        Artifact__L2OutputOracle.abi,
    67        signer
    68      )
    69    })
    70  
    71    describe('findOutputForIndex', () => {
    72      describe('when the output exists once', () => {
    73        beforeEach(async () => {
    74          const latestBlock = await hre.ethers.provider.getBlock('latest')
    75          const params = {
    76            _outputRoot: utils.formatBytes32String('testhash'),
    77            _l2BlockNumber:
    78              deployConfig.l2OutputOracleStartingBlockNumber +
    79              deployConfig.l2OutputOracleSubmissionInterval,
    80            _l1BlockHash: latestBlock.hash,
    81            _l1BlockNumber: latestBlock.number,
    82          }
    83          await L2OutputOracle.proposeL2Output(
    84            params._outputRoot,
    85            params._l2BlockNumber,
    86            params._l1BlockHash,
    87            params._l1BlockNumber
    88          )
    89        })
    90  
    91        it('should return the output', async () => {
    92          const output = await findOutputForIndex(L2OutputOracle, 0)
    93  
    94          expect(output.l2OutputIndex).to.equal(0)
    95        })
    96      })
    97  
    98      describe('when the output does not exist', () => {
    99        it('should throw an error', async () => {
   100          await expect(
   101            findOutputForIndex(L2OutputOracle, 0)
   102          ).to.eventually.be.rejectedWith('unable to find output for index')
   103        })
   104      })
   105    })
   106  
   107    describe('findFirstUnfinalizedIndex', () => {
   108      describe('when the chain is more then FPW seconds old', () => {
   109        beforeEach(async () => {
   110          const latestBlock = await hre.ethers.provider.getBlock('latest')
   111          const params = {
   112            _l2BlockNumber:
   113              deployConfig.l2OutputOracleStartingBlockNumber +
   114              deployConfig.l2OutputOracleSubmissionInterval,
   115            _l1BlockHash: latestBlock.hash,
   116            _l1BlockNumber: latestBlock.number,
   117          }
   118          await L2OutputOracle.proposeL2Output(
   119            utils.formatBytes32String('outputRoot1'),
   120            params._l2BlockNumber,
   121            params._l1BlockHash,
   122            params._l1BlockNumber
   123          )
   124  
   125          // Simulate FPW passing
   126          await hre.ethers.provider.send('evm_increaseTime', [
   127            toRpcHexString(deployConfig.finalizationPeriodSeconds * 2),
   128          ])
   129  
   130          await L2OutputOracle.proposeL2Output(
   131            utils.formatBytes32String('outputRoot2'),
   132            params._l2BlockNumber + deployConfig.l2OutputOracleSubmissionInterval,
   133            params._l1BlockHash,
   134            params._l1BlockNumber
   135          )
   136          await L2OutputOracle.proposeL2Output(
   137            utils.formatBytes32String('outputRoot3'),
   138            params._l2BlockNumber +
   139              deployConfig.l2OutputOracleSubmissionInterval * 2,
   140            params._l1BlockHash,
   141            params._l1BlockNumber
   142          )
   143        })
   144  
   145        it('should find the first batch older than the FPW', async () => {
   146          const first = await findFirstUnfinalizedOutputIndex(
   147            L2OutputOracle,
   148            deployConfig.finalizationPeriodSeconds
   149          )
   150  
   151          expect(first).to.equal(1)
   152        })
   153      })
   154  
   155      describe('when the chain is less than FPW seconds old', () => {
   156        beforeEach(async () => {
   157          const latestBlock = await hre.ethers.provider.getBlock('latest')
   158          const params = {
   159            _outputRoot: utils.formatBytes32String('testhash'),
   160            _l2BlockNumber:
   161              deployConfig.l2OutputOracleStartingBlockNumber +
   162              deployConfig.l2OutputOracleSubmissionInterval,
   163            _l1BlockHash: latestBlock.hash,
   164            _l1BlockNumber: latestBlock.number,
   165          }
   166          await L2OutputOracle.proposeL2Output(
   167            params._outputRoot,
   168            params._l2BlockNumber,
   169            params._l1BlockHash,
   170            params._l1BlockNumber
   171          )
   172          await L2OutputOracle.proposeL2Output(
   173            params._outputRoot,
   174            params._l2BlockNumber + deployConfig.l2OutputOracleSubmissionInterval,
   175            params._l1BlockHash,
   176            params._l1BlockNumber
   177          )
   178          await L2OutputOracle.proposeL2Output(
   179            params._outputRoot,
   180            params._l2BlockNumber +
   181              deployConfig.l2OutputOracleSubmissionInterval * 2,
   182            params._l1BlockHash,
   183            params._l1BlockNumber
   184          )
   185        })
   186  
   187        it('should return zero', async () => {
   188          const first = await findFirstUnfinalizedOutputIndex(
   189            L2OutputOracle,
   190            deployConfig.finalizationPeriodSeconds
   191          )
   192  
   193          expect(first).to.equal(0)
   194        })
   195      })
   196  
   197      describe('when no batches submitted for the entire FPW', () => {
   198        beforeEach(async () => {
   199          const latestBlock = await hre.ethers.provider.getBlock('latest')
   200          const params = {
   201            _outputRoot: utils.formatBytes32String('testhash'),
   202            _l2BlockNumber:
   203              deployConfig.l2OutputOracleStartingBlockNumber +
   204              deployConfig.l2OutputOracleSubmissionInterval,
   205            _l1BlockHash: latestBlock.hash,
   206            _l1BlockNumber: latestBlock.number,
   207          }
   208          await L2OutputOracle.proposeL2Output(
   209            params._outputRoot,
   210            params._l2BlockNumber,
   211            params._l1BlockHash,
   212            params._l1BlockNumber
   213          )
   214          await L2OutputOracle.proposeL2Output(
   215            params._outputRoot,
   216            params._l2BlockNumber + deployConfig.l2OutputOracleSubmissionInterval,
   217            params._l1BlockHash,
   218            params._l1BlockNumber
   219          )
   220          await L2OutputOracle.proposeL2Output(
   221            params._outputRoot,
   222            params._l2BlockNumber +
   223              deployConfig.l2OutputOracleSubmissionInterval * 2,
   224            params._l1BlockHash,
   225            params._l1BlockNumber
   226          )
   227  
   228          // Simulate FPW passing and no new batches
   229          await hre.ethers.provider.send('evm_increaseTime', [
   230            toRpcHexString(deployConfig.finalizationPeriodSeconds * 2),
   231          ])
   232  
   233          // Mine a block to force timestamp to update
   234          await hre.ethers.provider.send('hardhat_mine', ['0x1'])
   235        })
   236  
   237        it('should return undefined', async () => {
   238          const first = await findFirstUnfinalizedOutputIndex(
   239            L2OutputOracle,
   240            deployConfig.finalizationPeriodSeconds
   241          )
   242  
   243          expect(first).to.equal(undefined)
   244        })
   245      })
   246    })
   247  })