/* Imports: External */ import { Contract, Wallet, utils } from 'ethers' import { ethers } from 'hardhat' /* Imports: Internal */ import { expect } from './shared/setup' import { OptimismEnv } from './shared/env' import { executeL1ToL2TransactionsParallel, executeL2ToL1TransactionsParallel, executeL2TransactionsParallel, executeRepeatedL1ToL2Transactions, executeRepeatedL2ToL1Transactions, executeRepeatedL2Transactions, fundRandomWallet, } from './shared/stress-test-helpers' /* Imports: Artifacts */ import { envConfig, fundUser } from './shared/utils' // Need a big timeout to allow for all transactions to be processed. // For some reason I can't figure out how to set the timeout on a per-suite basis // so I'm instead setting it for every test. const STRESS_TEST_TIMEOUT = envConfig.MOCHA_TIMEOUT * 5 describe('stress tests', () => { const numTransactions = 3 let env: OptimismEnv const wallets: Wallet[] = [] before(async function () { if (!envConfig.RUN_STRESS_TESTS) { console.log('Skipping stress tests.') this.skip() return } env = await OptimismEnv.new() for (let i = 0; i < numTransactions; i++) { wallets.push(Wallet.createRandom()) } for (const wallet of wallets) { await fundRandomWallet(env, wallet, utils.parseEther('0.1')) } for (const wallet of wallets) { await fundUser(env.messenger, utils.parseEther('0.1'), wallet.address) } }) let L2SimpleStorage: Contract let L1SimpleStorage: Contract beforeEach(async () => { const factory__L1SimpleStorage = await ethers.getContractFactory( 'SimpleStorage', env.l1Wallet ) const factory__L2SimpleStorage = await ethers.getContractFactory( 'SimpleStorage', env.l2Wallet ) L1SimpleStorage = await factory__L1SimpleStorage.deploy() await L1SimpleStorage.deployTransaction.wait() L2SimpleStorage = await factory__L2SimpleStorage.deploy() await L2SimpleStorage.deployTransaction.wait() }) describe('L1 => L2 stress tests', () => { it(`${numTransactions} L1 => L2 transactions (serial)`, async () => { await executeRepeatedL1ToL2Transactions(env, wallets, { contract: L2SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) it(`${numTransactions} L1 => L2 transactions (parallel)`, async () => { await executeL1ToL2TransactionsParallel(env, wallets, { contract: L2SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) }) describe('L2 => L1 stress tests', () => { it(`${numTransactions} L2 => L1 transactions (serial)`, async () => { await executeRepeatedL2ToL1Transactions(env, wallets, { contract: L1SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) it(`${numTransactions} L2 => L1 transactions (parallel)`, async () => { await executeL2ToL1TransactionsParallel(env, wallets, { contract: L1SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) }) describe('L2 transaction stress tests', () => { it(`${numTransactions} L2 transactions (serial)`, async () => { await executeRepeatedL2Transactions(env, wallets, { contract: L2SimpleStorage, functionName: 'setValueNotXDomain', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) it(`${numTransactions} L2 transactions (parallel)`, async () => { await executeL2TransactionsParallel(env, wallets, { contract: L2SimpleStorage, functionName: 'setValueNotXDomain', functionParams: [`0x${'42'.repeat(32)}`], }) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( numTransactions ) }).timeout(STRESS_TEST_TIMEOUT) }) describe('C-C-C-Combo breakers', () => { it(`${numTransactions} L2 transactions, L1 => L2 transactions, L2 => L1 transactions (txs serial, suites parallel)`, async () => { await Promise.all([ executeRepeatedL1ToL2Transactions(env, wallets, { contract: L2SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }), executeRepeatedL2ToL1Transactions(env, wallets, { contract: L1SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }), executeRepeatedL2Transactions(env, wallets, { contract: L2SimpleStorage, functionName: 'setValueNotXDomain', functionParams: [`0x${'42'.repeat(32)}`], }), ]) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length * 2 ) expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) it(`${numTransactions} L2 transactions, L1 => L2 transactions, L2 => L1 transactions (all parallel)`, async () => { await Promise.all([ executeL1ToL2TransactionsParallel(env, wallets, { contract: L2SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }), executeL2ToL1TransactionsParallel(env, wallets, { contract: L1SimpleStorage, functionName: 'setValue', functionParams: [`0x${'42'.repeat(32)}`], }), executeL2TransactionsParallel(env, wallets, { contract: L2SimpleStorage, functionName: 'setValueNotXDomain', functionParams: [`0x${'42'.repeat(32)}`], }), ]) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length * 2 ) expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal( wallets.length ) }).timeout(STRESS_TEST_TIMEOUT) }) // These tests depend on an archive node due to the historical `eth_call`s describe('Monotonicity Checks', () => { it('should have monotonic timestamps and l1 blocknumbers', async () => { const tip = await env.l2Provider.getBlock('latest') const prev = { block: await env.l2Provider.getBlock(0), l1BlockNumber: await env.messenger.contracts.l2.OVM_L1BlockNumber.getL1BlockNumber({ blockTag: 0, }), } for (let i = 1; i < tip.number; i++) { const block = await env.l2Provider.getBlock(i) expect(block.timestamp).to.be.gte(prev.block.timestamp) const l1BlockNumber = await env.messenger.contracts.l2.OVM_L1BlockNumber.getL1BlockNumber({ blockTag: i, }) expect(l1BlockNumber.gt(prev.l1BlockNumber)) prev.block = block prev.l1BlockNumber = l1BlockNumber } }) }) })