@ethereumjs/vm
Version:
An Ethereum VM implementation
145 lines (127 loc) • 4.78 kB
text/typescript
import { createEVM } from '@ethereumjs/evm'
import { EventEmitter } from 'eventemitter3'
import { createVM } from './constructors.ts'
import { paramsVM } from './params.ts'
import type { Common, StateManagerInterface } from '@ethereumjs/common'
import type { EVMInterface, EVMMockBlockchainInterface } from '@ethereumjs/evm'
import type { BigIntLike } from '@ethereumjs/util'
import type { VMEvent, VMOpts } from './types.ts'
/**
* Execution engine which can be used to run a blockchain, individual
* blocks, individual transactions, or snippets of EVM bytecode.
*/
export class VM {
/**
* The StateManager used by the VM
*/
readonly stateManager: StateManagerInterface
/**
* The blockchain the VM operates on
*/
readonly blockchain: EVMMockBlockchainInterface
readonly common: Common
readonly events: EventEmitter<VMEvent>
/**
* The EVM used for bytecode execution
*/
readonly evm: EVMInterface
protected readonly _opts: VMOpts
protected _isInitialized: boolean = false
protected readonly _setHardfork: boolean | BigIntLike
/**
* Cached emit() function, not for public usage
* set to public due to implementation internals
* @hidden
*/
public readonly _emit: (topic: string, data: any) => Promise<void>
/**
* VM is run in DEBUG mode (default: false)
* Taken from DEBUG environment variable
*
* Safeguards on debug() calls are added for
* performance reasons to avoid string literal evaluation
* @hidden
*/
readonly DEBUG: boolean = false
/**
* Instantiates a new {@link VM} Object.
*
* @deprecated The direct usage of this constructor is discouraged since
* non-finalized async initialization might lead to side effects. Please
* use the async {@link createVM} constructor instead (same API).
* @param opts
*/
constructor(opts: VMOpts = {}) {
this.common = opts.common!
this.common.updateParams(opts.params ?? paramsVM)
this.stateManager = opts.stateManager!
this.blockchain = opts.blockchain!
this.evm = opts.evm!
this.events = new EventEmitter<VMEvent>()
this._emit = async (topic: string, data: any): Promise<void> => {
const listeners = this.events.listeners(topic as keyof VMEvent)
for (const listener of listeners) {
if (listener.length === 2) {
await new Promise<void>((resolve) => {
listener(data, resolve)
})
} else {
listener(data)
}
}
}
this._opts = opts
this._setHardfork = opts.setHardfork ?? false
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables
// Additional window check is to prevent vite browser bundling (and potentially other) to break
this.DEBUG =
typeof window === 'undefined' ? (process?.env?.DEBUG?.includes('ethjs') ?? false) : false
}
/**
* Returns a copy of the {@link VM} instance.
*
* Note that the returned copy will share the same db as the original for the blockchain and the statemanager.
*
* Associated caches will be deleted and caches will be re-initialized for a more short-term focused
* usage, being less memory intense (the statemanager caches will switch to using an ORDERED_MAP cache
* data structure more suitable for short-term usage, the trie node LRU cache will not be activated at all).
* To fine-tune this behavior (if the shallow-copy-returned object has a longer life span e.g.) you can set
* the `downlevelCaches` option to `false`.
*
* @param downlevelCaches Downlevel (so: adopted for short-term usage) associated state caches (default: true)
*/
async shallowCopy(downlevelCaches = true): Promise<VM> {
const common = this.common.copy()
common.setHardfork(this.common.hardfork())
const blockchain = this.blockchain.shallowCopy()
const stateManager = this.stateManager.shallowCopy(downlevelCaches)
const evmOpts = {
...(this.evm as any)._optsCached,
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 createEVM(evmOpts) // TODO fixme (should copy the EVMInterface, not default EVM)
return createVM({
stateManager,
blockchain: this.blockchain,
common,
evm: evmCopy,
setHardfork: this._setHardfork,
profilerOpts: this._opts.profilerOpts,
})
}
/**
* Return a compact error string representation of the object
*/
errorStr() {
let hf = ''
try {
hf = this.common.hardfork()
} catch {
hf = 'error'
}
const errorStr = `vm hf=${hf}`
return errorStr
}
}