Commit e97edd27 authored by Maurelian's avatar Maurelian Committed by GitHub

Test gas consumption of EM.run() (#263)

* Add gas test output for em.run()

* Move gas helper code to util'

* Use GasMeasurement class in SM gas spec

* Add expect statements

* remove only()

* Address review comments

* add newline at eof

* remove .only
Co-authored-by: default avatarKelvin Fichter <kelvinfichter@gmail.com>
parent a25ad8ba
import { expect } from '../../../setup'
import { deployContractCode } from '../../../helpers/utils'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, ContractFactory, Signer } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
GasMeasurement,
} from '../../../helpers'
const DUMMY_GASMETERCONFIG = {
minTransactionGasLimit: 0,
maxTransactionGasLimit: NON_NULL_BYTES32,
maxGasPerQueuePerEpoch: NON_NULL_BYTES32,
secondsPerEpoch: NON_NULL_BYTES32,
}
const DUMMY_GLOBALCONTEXT = {
ovmCHAINID: 420,
}
const QUEUE_ORIGIN = {
SEQUENCER_QUEUE: 0,
L1TOL2_QUEUE: 1
}
let DUMMY_TRANSACTION = {
timestamp: 111111111111,
blockNumber: 20,
l1QueueOrigin: QUEUE_ORIGIN.SEQUENCER_QUEUE,
l1TxOrigin: NON_ZERO_ADDRESS,
entrypoint: NON_ZERO_ADDRESS, // update this below
gasLimit: 10_000_000,
data: 0
}
describe('OVM_ExecutionManager gas consumption', () => {
let wallet: Signer
before(async () => {
;[wallet] = await ethers.getSigners()
})
let Factory__OVM_ExecutionManager: ContractFactory
let MOCK__STATE_MANAGER: MockContract
let AddressManager: Contract
let targetContractAddress: string
let gasMeasurement: GasMeasurement
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
// Deploy a simple contract that just returns successfully with no data
targetContractAddress = await deployContractCode('60206001f3', wallet, 10_000_000)
DUMMY_TRANSACTION.entrypoint = targetContractAddress
AddressManager = await makeAddressManager()
// deploy the state manager and mock it for the state transitioner
MOCK__STATE_MANAGER = await smockit(
await (
await ethers.getContractFactory('OVM_StateManager')
).deploy(NON_ZERO_ADDRESS)
)
// Setup the SM to satisfy all the checks executed during EM.run()
MOCK__STATE_MANAGER.smocked.isAuthenticated.will.return.with(true)
MOCK__STATE_MANAGER.smocked.getAccountEthAddress.will.return.with(targetContractAddress)
MOCK__STATE_MANAGER.smocked.hasAccount.will.return.with(true)
MOCK__STATE_MANAGER.smocked.testAndSetAccountLoaded.will.return.with(true)
await AddressManager.setAddress(
'OVM_StateManagerFactory',
MOCK__STATE_MANAGER.address
)
gasMeasurement = new GasMeasurement()
await gasMeasurement.init(wallet)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = (
await Factory__OVM_ExecutionManager.deploy(
AddressManager.address,
DUMMY_GASMETERCONFIG,
DUMMY_GLOBALCONTEXT
)
).connect(wallet)
})
describe('Measure cost of a very simple contract', async () => {
it('Gas cost of run', async () => {
let gasCost = await gasMeasurement.getGasCost(
OVM_ExecutionManager, 'run',
[DUMMY_TRANSACTION, MOCK__STATE_MANAGER.address]
)
console.log(`calculated gas cost of ${gasCost}`)
let benchmark:number = 226_516
expect(gasCost).to.be.lte(benchmark)
expect(gasCost).to.be.gte(
benchmark - 1_000,
"Gas cost has significantly decreased, consider updating the benchmark to reflect the change"
)
})
})
})
...@@ -14,6 +14,7 @@ import { ...@@ -14,6 +14,7 @@ import {
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_NULL_BYTES32, NON_NULL_BYTES32,
STORAGE_XOR_VALUE, STORAGE_XOR_VALUE,
GasMeasurement,
} from '../../../helpers' } from '../../../helpers'
const DUMMY_ACCOUNT = DUMMY_ACCOUNTS[0] const DUMMY_ACCOUNT = DUMMY_ACCOUNTS[0]
...@@ -29,23 +30,23 @@ describe('OVM_StateManager gas consumption', () => { ...@@ -29,23 +30,23 @@ describe('OVM_StateManager gas consumption', () => {
let Factory__OVM_StateManager: ContractFactory let Factory__OVM_StateManager: ContractFactory
let Helper_GasMeasurer: Contract let Helper_GasMeasurer: Contract
let gasMeasurement: GasMeasurement
before(async () => { before(async () => {
Factory__OVM_StateManager = await ethers.getContractFactory( Factory__OVM_StateManager = await ethers.getContractFactory(
'OVM_StateManager' 'OVM_StateManager'
) )
gasMeasurement = new GasMeasurement()
Helper_GasMeasurer = await ( await gasMeasurement.init(owner)
await (await ethers.getContractFactory('Helper_GasMeasurer')).deploy()
).connect(owner)
}) })
let OVM_StateManager: Contract let OVM_StateManager: Contract
beforeEach(async () => { beforeEach(async () => {
OVM_StateManager = ( OVM_StateManager = (
await Factory__OVM_StateManager.deploy(await owner.getAddress()) await Factory__OVM_StateManager.deploy(await owner.getAddress())
).connect(owner) ).connect(owner)
await OVM_StateManager.setExecutionManager(Helper_GasMeasurer.address) await OVM_StateManager.setExecutionManager(gasMeasurement.GasMeasurementContract.address)
}) })
const measure = ( const measure = (
...@@ -57,23 +58,11 @@ describe('OVM_StateManager gas consumption', () => { ...@@ -57,23 +58,11 @@ describe('OVM_StateManager gas consumption', () => {
) => { ) => {
it('measured consumption!', async () => { it('measured consumption!', async () => {
await doFirst() await doFirst()
await getSMGasCost(methodName, methodArgs) let gasCost = await gasMeasurement.getGasCost(OVM_StateManager, methodName, methodArgs)
console.log(` calculated gas cost of ${gasCost}`)
}) })
} }
const getSMGasCost = async (
methodName: string,
methodArgs: Array<any> = []
): Promise<number> => {
const gasCost: number = await Helper_GasMeasurer.callStatic.measureCallGas(
OVM_StateManager.address,
OVM_StateManager.interface.encodeFunctionData(methodName, methodArgs)
)
console.log(` calculated gas cost of ${gasCost}`)
return gasCost
}
const setupFreshAccount = async () => { const setupFreshAccount = async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, { await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data, ...DUMMY_ACCOUNT.data,
......
import { ethers } from 'hardhat'
import { Contract, Signer } from 'ethers'
export class GasMeasurement {
GasMeasurementContract: Contract
public async init(wallet: Signer){
this.GasMeasurementContract = await (
await (await ethers.getContractFactory('Helper_GasMeasurer')).deploy()
).connect(wallet)
}
public async getGasCost(
targetContract: Contract,
methodName: string,
methodArgs: Array<any> = []
): Promise<number>
{
const gasCost: number = await this.GasMeasurementContract.callStatic.measureCallGas(
targetContract.address,
targetContract.interface.encodeFunctionData(methodName, methodArgs)
)
return gasCost
}
}
...@@ -6,3 +6,4 @@ export * from './utils' ...@@ -6,3 +6,4 @@ export * from './utils'
export * from './codec' export * from './codec'
export * from './test-runner' export * from './test-runner'
export * from './trie' export * from './trie'
export * from './gas'
import { Signer } from 'ethers'
import { toHexString } from '../../../src/utils'
export const deployContractCode = async (code: string, signer: Signer, gasLimit: number): Promise<string> => {
// "Magic" prefix to be prepended to the contract code. Contains a series of opcodes that will
// copy the given code into memory and return it, thereby storing at the contract address.
const prefix = '0x600D380380600D6000396000f3'
const deployCode = prefix + toHexString(code).slice(2)
const response = await signer.sendTransaction({
to: null,
data: deployCode,
gasLimit,
})
const result = await response.wait()
return result.contractAddress
}
export * from '../../../src/utils' export * from '../../../src/utils'
export * from './eth-time' export * from './eth-time'
export * from './custom-deployer'
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