Commit 8c70da44 authored by Kelvin Fichter's avatar Kelvin Fichter

feat[integration-tests]: add new basic stress tests

parent 0d6ec566
...@@ -18,6 +18,12 @@ contract SimpleStorage { ...@@ -18,6 +18,12 @@ contract SimpleStorage {
totalCount++; totalCount++;
} }
function setValueNotXDomain(bytes32 newValue) public {
msgSender = msg.sender;
value = newValue;
totalCount++;
}
function dumbSetValue(bytes32 newValue) public { function dumbSetValue(bytes32 newValue) public {
value = newValue; value = newValue;
} }
......
...@@ -170,6 +170,8 @@ export class OptimismEnv { ...@@ -170,6 +170,8 @@ export class OptimismEnv {
} catch (err) { } catch (err) {
if (err.message.includes('execution failed due to an exception')) { if (err.message.includes('execution failed due to an exception')) {
await sleep(5000) await sleep(5000)
} else if (err.message.includes('Nonce too low')) {
await sleep(5000)
} else if ( } else if (
err.message.includes('message has already been received') err.message.includes('message has already been received')
) { ) {
......
/* Imports: External */
import { ethers } from 'ethers'
/* Imports: Internal */
import { OptimismEnv } from './env'
import { Direction } from './watcher-utils'
interface TransactionParams {
contract: ethers.Contract
functionName: string
functionParams: any[]
}
// Arbitrary big amount of gas for the L1<>L2 messages.
const MESSAGE_GAS = 8_000_000
export const executeL1ToL2Transactions = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
for (const tx of txs) {
const signer = ethers.Wallet.createRandom().connect(env.l1Wallet.provider)
const receipt = await env.l1Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: 0,
}
)
await env.waitForXDomainTransaction(receipt, Direction.L1ToL2)
}
}
export const executeL2ToL1Transactions = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
for (const tx of txs) {
const signer = ethers.Wallet.createRandom().connect(env.l2Wallet.provider)
const receipt = await env.l2Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: 0,
}
)
await env.relayXDomainMessages(receipt)
await env.waitForXDomainTransaction(receipt, Direction.L2ToL1)
}
}
export const executeL2Transactions = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
for (const tx of txs) {
const signer = ethers.Wallet.createRandom().connect(env.l2Wallet.provider)
const result = await tx.contract
.connect(signer)
.functions[tx.functionName](...tx.functionParams, {
gasPrice: 0,
})
await result.wait()
}
}
export const executeRepeatedL1ToL2Transactions = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL1ToL2Transactions(
env,
[...Array(count).keys()].map(() => tx)
)
}
export const executeRepeatedL2ToL1Transactions = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL2ToL1Transactions(
env,
[...Array(count).keys()].map(() => tx)
)
}
export const executeRepeatedL2Transactions = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL2Transactions(
env,
[...Array(count).keys()].map(() => tx)
)
}
export const executeL1ToL2TransactionsParallel = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
await Promise.all(
txs.map(async (tx) => {
const signer = ethers.Wallet.createRandom().connect(env.l1Wallet.provider)
const receipt = await env.l1Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: 0,
}
)
await env.waitForXDomainTransaction(receipt, Direction.L1ToL2)
})
)
}
export const executeL2ToL1TransactionsParallel = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
await Promise.all(
txs.map(async (tx) => {
const signer = ethers.Wallet.createRandom().connect(env.l2Wallet.provider)
const receipt = await env.l2Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: 0,
}
)
await env.relayXDomainMessages(receipt)
await env.waitForXDomainTransaction(receipt, Direction.L2ToL1)
})
)
}
export const executeL2TransactionsParallel = async (
env: OptimismEnv,
txs: TransactionParams[]
) => {
await Promise.all(
txs.map(async (tx) => {
const signer = ethers.Wallet.createRandom().connect(env.l2Wallet.provider)
const result = await tx.contract
.connect(signer)
.functions[tx.functionName](...tx.functionParams, {
gasPrice: 0,
})
await result.wait()
})
)
}
export const executeRepeatedL1ToL2TransactionsParallel = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL1ToL2TransactionsParallel(
env,
[...Array(count).keys()].map(() => tx)
)
}
export const executeRepeatedL2ToL1TransactionsParallel = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL2ToL1TransactionsParallel(
env,
[...Array(count).keys()].map(() => tx)
)
}
export const executeRepeatedL2TransactionsParallel = async (
env: OptimismEnv,
tx: TransactionParams,
count: number
) => {
await executeL2TransactionsParallel(
env,
[...Array(count).keys()].map(() => tx)
)
}
import { expect } from 'chai'
/* Imports: External */
import { Contract, ContractFactory } from 'ethers'
/* Imports: Internal */
import { OptimismEnv } from './shared/env'
import {
executeRepeatedL1ToL2Transactions,
executeRepeatedL2ToL1Transactions,
executeRepeatedL2Transactions,
executeRepeatedL1ToL2TransactionsParallel,
executeRepeatedL2ToL1TransactionsParallel,
executeRepeatedL2TransactionsParallel,
} from './shared/stress-test-helpers'
/* Imports: Artifacts */
import l1SimpleStorageJson from '../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json'
import l2SimpleStorageJson from '../artifacts-ovm/contracts/SimpleStorage.sol/SimpleStorage.json'
// 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 = 300_000
describe('stress tests', () => {
let env: OptimismEnv
before(async () => {
env = await OptimismEnv.new()
})
let L2SimpleStorage: Contract
let L1SimpleStorage: Contract
beforeEach(async () => {
const factory__L1SimpleStorage = new ContractFactory(
l1SimpleStorageJson.abi,
l1SimpleStorageJson.bytecode,
env.l1Wallet
)
const factory__L2SimpleStorage = new ContractFactory(
l2SimpleStorageJson.abi,
l2SimpleStorageJson.bytecode,
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', () => {
const numTransactions = 10
it(`${numTransactions} L1 => L2 transactions (serial)`, async () => {
await executeRepeatedL1ToL2Transactions(
env,
{
contract: L2SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
it(`${numTransactions} L1 => L2 transactions (parallel)`, async () => {
await executeRepeatedL1ToL2TransactionsParallel(
env,
{
contract: L2SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
})
describe('L2 => L1 stress tests', () => {
const numTransactions = 10
it(`${numTransactions} L2 => L1 transactions (serial)`, async () => {
await executeRepeatedL2ToL1Transactions(
env,
{
contract: L1SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
it(`${numTransactions} L2 => L1 transactions (parallel)`, async () => {
await executeRepeatedL2ToL1TransactionsParallel(
env,
{
contract: L1SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
})
describe('L2 transaction stress tests', () => {
const numTransactions = 10
it(`${numTransactions} L2 transactions (serial)`, async () => {
await executeRepeatedL2Transactions(
env,
{
contract: L2SimpleStorage,
functionName: 'setValueNotXDomain',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
it(`${numTransactions} L2 transactions (parallel)`, async () => {
await executeRepeatedL2TransactionsParallel(
env,
{
contract: L2SimpleStorage,
functionName: 'setValueNotXDomain',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
)
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
})
describe('C-C-C-Combo breakers', () => {
const numTransactions = 10
it(`${numTransactions} L2 transactions, L1 => L2 transactions, L2 => L1 transactions (txs serial, suites parallel)`, async () => {
await Promise.all([
executeRepeatedL1ToL2Transactions(
env,
{
contract: L2SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
executeRepeatedL2ToL1Transactions(
env,
{
contract: L1SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
executeRepeatedL2Transactions(
env,
{
contract: L2SimpleStorage,
functionName: 'setValueNotXDomain',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
])
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions * 2
)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
it(`${numTransactions} L2 transactions, L1 => L2 transactions, L2 => L1 transactions (all parallel)`, async () => {
await Promise.all([
executeRepeatedL1ToL2TransactionsParallel(
env,
{
contract: L2SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
executeRepeatedL2ToL1TransactionsParallel(
env,
{
contract: L1SimpleStorage,
functionName: 'setValue',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
executeRepeatedL2TransactionsParallel(
env,
{
contract: L2SimpleStorage,
functionName: 'setValueNotXDomain',
functionParams: [`0x${'42'.repeat(32)}`],
},
numTransactions
),
])
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions * 2
)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(
numTransactions
)
}).timeout(STRESS_TEST_TIMEOUT)
})
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment