Skip to content

Commit

Permalink
vm: extract environment from EEI
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed May 26, 2022
1 parent 4ac4ef2 commit 2b14275
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 53 deletions.
74 changes: 42 additions & 32 deletions packages/vm/src/eei/eei.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Account, Address, bufferToBigInt } from 'ethereumjs-util'
import { Block } from '@ethereumjs/block'
import Blockchain from '@ethereumjs/blockchain'
import Common from '@ethereumjs/common'

import { VmState } from './vmState'
Expand All @@ -12,9 +10,9 @@ import { StateManager } from '@ethereumjs/statemanager'

type CreateEIOptions = {
transientStorage: TransientStorage
env: Env
gasLeft: bigint
evm: EVM
blockchain: Blockchain
}

/**
Expand Down Expand Up @@ -47,10 +45,24 @@ export class EIFactory implements ExternalInterfaceFactory {
}

createEI(options: CreateEIOptions) {
return new EEI(options.env, this.state, options.evm, this.common, options.transientStorage)
return new EEI(
this.state,
options.evm,
this.common,
options.transientStorage,
options.blockchain
)
}
}

type Block = {
hash(): Buffer
}

type Blockchain = {
getBlock(blockId: number): Promise<Block>
}

/**
* External interface made available to EVM bytecode. Modeled after
* the ewasm EEI [spec](https://github.com/ewasm/design/blob/master/eth_interface.md).
Expand All @@ -60,37 +72,31 @@ export class EIFactory implements ExternalInterfaceFactory {
* and to-be-selfdestructed addresses.
*/
export default class EEI {
_env: Env
_state: VmState
_evm: EVM
_common: Common
_transientStorage: TransientStorage
_blockchain: Blockchain

constructor(
env: Env,
state: VmState,
evm: EVM,
common: Common,
transientStorage: TransientStorage
transientStorage: TransientStorage,
blockchain: Blockchain
) {
this._env = env
this._state = state
this._evm = evm
this._common = common
this._transientStorage = transientStorage
this._blockchain = blockchain
}

/**
* Returns balance of the given account.
* @param address - Address of account
*/
async getExternalBalance(address: Address): Promise<bigint> {
// shortcut if current account
if (address.equals(this._env.address)) {
return this._env.contract.balance
}

// otherwise load account then return balance
const account = await this._state.getAccount(address)
return account.balance
}
Expand Down Expand Up @@ -119,47 +125,51 @@ export default class EEI {
* @param num - Number of block
*/
async getBlockHash(num: bigint): Promise<bigint> {
const block = await this._env.blockchain.getBlock(Number(num))
const block = await this._blockchain.getBlock(Number(num))
return bufferToBigInt(block.hash())
}

/**
* Store 256-bit a value in memory to persistent storage.
* Storage 256-bit value into storage of an address
* @param address Address to store into
* @param key Storage key
* @param value Storage value
*/
async storageStore(key: Buffer, value: Buffer): Promise<void> {
await this._state.putContractStorage(this._env.address, key, value)
const account = await this._state.getAccount(this._env.address)
this._env.contract = account
async storageStore(address: Address, key: Buffer, value: Buffer): Promise<void> {
await this._state.putContractStorage(address, key, value)
}

/**
* Loads a 256-bit value to memory from persistent storage.
* @param key - Storage key
* @param original - If true, return the original storage value (default: false)
* @param address Address to get storage key value from
* @param key Storage key
* @param original If true, return the original storage value (default: false)
*/
async storageLoad(key: Buffer, original = false): Promise<Buffer> {
async storageLoad(address: Address, key: Buffer, original = false): Promise<Buffer> {
if (original) {
return this._state.getOriginalContractStorage(this._env.address, key)
return this._state.getOriginalContractStorage(address, key)
} else {
return this._state.getContractStorage(this._env.address, key)
return this._state.getContractStorage(address, key)
}
}

/**
* Store 256-bit a value in memory to transient storage.
* @param key - Storage key
* @param value - Storage value
* @param address Address to use
* @param key Storage key
* @param value Storage value
*/
transientStorageStore(key: Buffer, value: Buffer): void {
return this._transientStorage.put(this._env.address, key, value)
transientStorageStore(address: Address, key: Buffer, value: Buffer): void {
return this._transientStorage.put(address, key, value)
}

/**
* Loads a 256-bit value to memory from transient storage.
* @param key - Storage key
* @param address Address to use
* @param key Storage key
*/
transientStorageLoad(key: Buffer): Buffer {
return this._transientStorage.get(this._env.address, key)
transientStorageLoad(address: Address, key: Buffer): Buffer {
return this._transientStorage.get(address, key)
}

/**
Expand Down
11 changes: 6 additions & 5 deletions packages/vm/src/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import { CustomPrecompile, getActivePrecompiles, PrecompileFunc } from './precom
import { TransientStorage } from '../state'
import {
CustomOpcode,
ExternalInterfaceFactory,
/*ExternalInterfaceFactory,*/
Log,
RunCallOpts,
RunCodeOpts,
TxContext,
} from './types'
import { VmState } from '../eei/vmState'
import { EIFactory } from '../eei/eei'

const debug = createDebugLogger('vm:evm')
const debugGas = createDebugLogger('vm:evm:gas')
Expand Down Expand Up @@ -131,7 +132,7 @@ export interface EVMOpts {
/*
* The External Interface Factory, used to build an External Interface when this is necessary
*/
eiFactory: ExternalInterfaceFactory
eiFactory: EIFactory /*ExternalInterfaceFactory*/ // TODO move to abstract interface (done this for quick TypeScript debugging)
}

/**
Expand Down Expand Up @@ -247,7 +248,7 @@ export default class EVM extends AsyncEventEmitter {

_common: Common

private _eiFactory: ExternalInterfaceFactory
private _eiFactory: EIFactory /*ExternalInterfaceFactory*/

protected _blockchain: Blockchain

Expand Down Expand Up @@ -697,10 +698,10 @@ export default class EVM extends AsyncEventEmitter {
codeAddress: message.codeAddress,
}
const eei = this._eiFactory.createEI({
env: env,
evm: this,
gasLeft: message.gasLimit,
transientStorage: this._transientStorage,
blockchain: this._blockchain,
})

const interpreter = new Interpreter(this, eei, env, message.gasLimit)
Expand Down Expand Up @@ -734,7 +735,7 @@ export default class EVM extends AsyncEventEmitter {
runState: {
...interpreterRes.runState!,
...result,
...eei._env,
...interpreter._env,
},
exceptionError: interpreterRes.exceptionError,
gas: interpreter._gasLeft,
Expand Down
56 changes: 52 additions & 4 deletions packages/vm/src/evm/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,13 @@ export default class Interpreter {
},
stack: this._runState.stack._store,
returnStack: this._runState.returnStack._store,
depth: this._eei._env.depth,
address: this._eei._env.address,
account: this._eei._env.contract,
depth: this._env.depth,
address: this._env.address,
account: this._env.contract,
vmState: this._runState.vmState,
memory: this._runState.memory._store,
memoryWordCount: this._runState.memoryWordCount,
codeAddress: this._eei._env.codeAddress,
codeAddress: this._env.codeAddress,
}

if (this._evm.DEBUG) {
Expand Down Expand Up @@ -456,6 +456,54 @@ export default class Interpreter {
this._gasLeft += amount
}

/**
* Returns balance of the given account.
* @param address - Address of account
*/
async getExternalBalance(address: Address): Promise<bigint> {
// shortcut if current account
if (address.equals(this._env.address)) {
return this._env.contract.balance
}

return this._eei.getExternalBalance(address)
}

/**
* Store 256-bit a value in memory to persistent storage.
*/
async storageStore(key: Buffer, value: Buffer): Promise<void> {
await this._eei.storageStore(this._env.address, key, value)
const account = await this._eei._state.getAccount(this._env.address)
this._env.contract = account
}

/**
* Loads a 256-bit value to memory from persistent storage.
* @param key - Storage key
* @param original - If true, return the original storage value (default: false)
*/
async storageLoad(key: Buffer, original = false): Promise<Buffer> {
return this._eei.storageLoad(this._env.address, key, original)
}

/**
* Store 256-bit a value in memory to transient storage.
* @param key - Storage key
* @param value - Storage value
*/
transientStorageStore(key: Buffer, value: Buffer): void {
return this._eei.transientStorageStore(this._env.address, key, value)
}

/**
* Loads a 256-bit value to memory from transient storage.
* @param key - Storage key
*/
transientStorageLoad(key: Buffer): Buffer {
return this._eei.transientStorageLoad(this._env.address, key)
}

/**
* Set the returning output data for the execution.
* @param returnData - Output data to return
Expand Down
16 changes: 8 additions & 8 deletions packages/vm/src/evm/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export const handlers: Map<number, OpHandler> = new Map([
async function (runState) {
const addressBigInt = runState.stack.pop()
const address = new Address(addressToBuffer(addressBigInt))
const balance = await runState.eei.getExternalBalance(address)
const balance = await runState.interpreter.getExternalBalance(address)
runState.stack.push(balance)
},
],
Expand Down Expand Up @@ -693,7 +693,7 @@ export const handlers: Map<number, OpHandler> = new Map([
async function (runState) {
const key = runState.stack.pop()
const keyBuf = setLengthLeft(bigIntToBuffer(key), 32)
const value = await runState.eei.storageLoad(keyBuf)
const value = await runState.interpreter.storageLoad(keyBuf)
const valueBigInt = value.length ? bufferToBigInt(value) : BigInt(0)
runState.stack.push(valueBigInt)
},
Expand All @@ -713,7 +713,7 @@ export const handlers: Map<number, OpHandler> = new Map([
value = bigIntToBuffer(val)
}

await runState.eei.storageStore(keyBuf, value)
await runState.interpreter.storageStore(keyBuf, value)
},
],
// 0x56: JUMP
Expand Down Expand Up @@ -886,7 +886,7 @@ export const handlers: Map<number, OpHandler> = new Map([
function (runState) {
const key = runState.stack.pop()
const keyBuf = setLengthLeft(bigIntToBuffer(key), 32)
const value = runState.eei.transientStorageLoad(keyBuf)
const value = runState.interpreter.transientStorageLoad(keyBuf)
const valueBN = value.length ? bufferToBigInt(value) : BigInt(0)
runState.stack.push(valueBN)
},
Expand All @@ -909,7 +909,7 @@ export const handlers: Map<number, OpHandler> = new Map([
value = bigIntToBuffer(val)
}

runState.eei.transientStorageStore(keyBuf, value)
runState.interpreter.transientStorageStore(keyBuf, value)
},
],
// '0xf0' range - closures
Expand Down Expand Up @@ -1035,7 +1035,7 @@ export const handlers: Map<number, OpHandler> = new Map([
}

const commit = setLengthLeft(bigIntToBuffer(commitUnpadded), 32)
const paddedInvokerAddress = setLengthLeft(runState.eei._env.address.buf, 32)
const paddedInvokerAddress = setLengthLeft(runState.interpreter._env.address.buf, 32)
const chainId = setLengthLeft(bigIntToBuffer(runState.interpreter.getChainId()), 32)
const message = Buffer.concat([EIP3074MAGIC, chainId, paddedInvokerAddress, commit])
const msgHash = Buffer.from(keccak256(message))
Expand All @@ -1046,13 +1046,13 @@ export const handlers: Map<number, OpHandler> = new Map([
} catch (e) {
// Malformed signature, push 0 on stack, clear auth variable and return
runState.stack.push(BigInt(0))
runState.eei._env.auth = undefined
runState.interpreter._env.auth = undefined
return
}

const addressBuffer = publicToAddress(recover)
const address = new Address(addressBuffer)
runState.eei._env.auth = address
runState.interpreter._env.auth = address

const addressBigInt = bufferToBigInt(addressBuffer)
runState.stack.push(addressBigInt)
Expand Down
12 changes: 8 additions & 4 deletions packages/vm/src/evm/opcodes/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
value = bigIntToBuffer(val)
}

const currentStorage = setLengthLeftStorage(await runState.eei.storageLoad(keyBuf))
const originalStorage = setLengthLeftStorage(await runState.eei.storageLoad(keyBuf, true))
const currentStorage = setLengthLeftStorage(await runState.interpreter.storageLoad(keyBuf))
const originalStorage = setLengthLeftStorage(
await runState.interpreter.storageLoad(keyBuf, true)
)
if (common.hardfork() === Hardfork.Constantinople) {
gas += updateSstoreGasEIP1283(
runState,
Expand Down Expand Up @@ -443,7 +445,7 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
/* AUTHCALL */
0xf7,
async function (runState, gas, common): Promise<bigint> {
if (runState.eei._env.auth === undefined) {
if (runState.interpreter._env.auth === undefined) {
trap(ERROR.AUTHCALL_UNSET)
}

Expand Down Expand Up @@ -544,7 +546,9 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
let deductGas = false
if (common.gteHardfork(Hardfork.SpuriousDragon)) {
// EIP-161: State Trie Clearing
const balance = await runState.eei.getExternalBalance(runState.interpreter.getAddress())
const balance = await runState.interpreter.getExternalBalance(
runState.interpreter.getAddress()
)
if (balance > BigInt(0)) {
// This technically checks if account is empty or non-existent
// TODO: improve on the API here (EEI and StateManager)
Expand Down

0 comments on commit 2b14275

Please sign in to comment.