Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VM: EVM Opts Chaining for better DevEx #3481

Merged
merged 3 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/vm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Bloom } from './bloom/index.js'
import type { Block, BlockOptions, HeaderData } from '@ethereumjs/block'
import type { BlockchainInterface } from '@ethereumjs/blockchain'
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'
import type { EVMInterface, EVMResult, Log } from '@ethereumjs/evm'
import type { EVMInterface, EVMOpts, EVMResult, Log } from '@ethereumjs/evm'
import type { AccessList, TypedTransaction } from '@ethereumjs/tx'
import type {
BigIntLike,
Expand Down Expand Up @@ -159,6 +159,14 @@ export interface VMOpts {
*/
evm?: EVMInterface

/**
* Often there is no need to provide a full custom EVM but only a few options need to be
* adopted. This option allows to provide a custom set of EVM options to be passed.
*
* Note: This option will throw if used in conjunction with a full custom EVM passed.
*/
evmOpts?: EVMOpts

profilerOpts?: VMProfilerOpts
}

Expand Down
12 changes: 9 additions & 3 deletions packages/vm/src/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export class VM {
}
}

if (opts.evm !== undefined && opts.evmOpts !== undefined) {
throw new Error('the evm and evmOpts options cannot be used in conjunction')
}

if (opts.evm === undefined) {
let enableProfiler = false
if (
Expand All @@ -117,13 +121,15 @@ export class VM {
) {
enableProfiler = true
}
const evmOpts = opts.evmOpts ?? {}
opts.evm = await EVM.create({
common: opts.common,
stateManager: opts.stateManager,
blockchain: opts.blockchain,
profiler: {
enabled: enableProfiler,
},
...evmOpts,
})
}

Expand Down Expand Up @@ -241,9 +247,9 @@ export class VM {
const stateManager = this.stateManager.shallowCopy(downlevelCaches)
const evmOpts = {
...(this.evm as any)._optsCached,
common,
blockchain,
stateManager,
common: this._opts.evmOpts?.common?.copy() ?? common,
blockchain: this._opts.evmOpts?.blockchain?.shallowCopy() ?? blockchain,
stateManager: this._opts.evmOpts?.stateManager?.shallowCopy(downlevelCaches) ?? stateManager,
}
const evmCopy = await EVM.create(evmOpts) // TODO fixme (should copy the EVMInterface, not default EVM)
return VM.create({
Expand Down
52 changes: 52 additions & 0 deletions packages/vm/test/api/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,58 @@ describe('VM -> basic instantiation / boolean switches', () => {
})
})

describe('VM -> Default EVM / Custom EVM Opts', () => {
it('Default EVM should have correct default EVM opts', async () => {
const vm = await VM.create()
assert.isFalse((vm.evm as EVM).allowUnlimitedContractSize, 'allowUnlimitedContractSize=false')
})

it('should throw if evm and evmOpts are both used', async () => {
try {
await VM.create({ evmOpts: {}, evm: await EVM.create() })
assert.fail('should throw')
} catch (e: any) {
assert.ok('correctly thrown')
}
})

it('Default EVM should use custom EVM opts', async () => {
const vm = await VM.create({ evmOpts: { allowUnlimitedContractSize: true } })
assert.isTrue((vm.evm as EVM).allowUnlimitedContractSize, 'allowUnlimitedContractSize=true')
const copiedVM = await vm.shallowCopy()
assert.isTrue(
(copiedVM.evm as EVM).allowUnlimitedContractSize,
'allowUnlimitedContractSize=true (for shallowCopied VM)'
)
})

it('Default EVM should use VM common', async () => {
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium })
const vm = await VM.create({ common })
assert.equal((vm.evm as EVM).common.hardfork(), 'byzantium', 'use modfied HF from VM common')

const copiedVM = await vm.shallowCopy()
assert.equal(
(copiedVM.evm as EVM).common.hardfork(),
'byzantium',
'use modfied HF from VM common (for shallowCopied VM)'
)
})

it('Default EVM should prefer common from evmOpts if provided (same logic for blockchain, statemanager)', async () => {
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium })
const vm = await VM.create({ evmOpts: { common } })
assert.equal((vm.evm as EVM).common.hardfork(), 'byzantium', 'use modfied HF from evmOpts')

const copiedVM = await vm.shallowCopy()
assert.equal(
(copiedVM.evm as EVM).common.hardfork(),
'byzantium',
'use modfied HF from evmOpts (for shallowCopied VM)'
)
})
})

describe('VM -> supportedHardforks', () => {
it('should throw when common is set to an unsupported hardfork', async () => {
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai })
Expand Down
Loading