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 })