From 54ab362cc14b4cba3c05e4f4036df648751e214c Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 14 Dec 2016 22:24:47 +0100 Subject: [PATCH] core, core/vm, tests: improved array tables & env context handling --- core/evm.go | 3 + core/vm/environment.go | 87 ++++-- core/vm/gas_table.go | 130 +++++---- core/vm/instructions.go | 6 + core/vm/jump_table.go | 541 ++++++++++++++++++------------------- core/vm/jump_table_test.go | 38 --- core/vm/logger_test.go | 5 +- core/vm/runtime/env.go | 2 + core/vm/vm.go | 37 +-- tests/util.go | 3 + 10 files changed, 421 insertions(+), 431 deletions(-) delete mode 100644 core/vm/jump_table_test.go diff --git a/core/evm.go b/core/evm.go index 6a5713075b..a2fe671d9b 100644 --- a/core/evm.go +++ b/core/evm.go @@ -17,6 +17,7 @@ package core import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" @@ -33,6 +34,8 @@ type HeaderFetcher interface { // NewEVMContext creates a new context for use in the EVM. func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { return vm.Context{ + Context: context.TODO(), + CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), diff --git a/core/vm/environment.go b/core/vm/environment.go index b2ac286f4f..c39e1dbbe4 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -17,8 +17,10 @@ package vm import ( + "context" "fmt" "math/big" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -35,6 +37,7 @@ type ( // Context provides the EVM with auxilary information. Once provided it shouldn't be modified. type Context struct { + context.Context // CanTransfer returns whether the account contains // sufficient ether to transfer the value CanTransfer CanTransferFunc @@ -66,29 +69,55 @@ type Environment struct { // Depth is the current call stack Depth int - // evm is the ethereum virtual machine - evm Vm // chainConfig contains information about the current chain chainConfig *params.ChainConfig - vmConfig Config + // virtual machine configuration options used to initialise the + // evm. + vmConfig Config + // global (to this context) ethereum virtual machine + // used throughout the execution of the tx. + evm Vm + // abort is used to abort the EVM calling operations + // NOTE: must be set atomically + abort int32 + + quit chan struct{} } // NewEnvironment retutrns a new EVM environment. -func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment { +func NewEnvironment(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment { env := &Environment{ - Context: context, + Context: ctx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, } + env.evm = New(env, vmConfig) return env } +func (env *Environment) init() { + env.quit = make(chan struct{}) + go func() { + select { + case <-env.quit: + return + case <-env.Context.Done(): + atomic.StoreInt32(&env.abort, 1) + } + }() +} + // Call executes the contract associated with the addr with the given input as paramaters. It also handles any // necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // case of an execution error or failed value transfer. -func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) { +func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + if env.Depth == 0 { + env.init() + defer close(env.quit) + } + if env.vmConfig.NoRecursion && env.Depth > 0 { caller.ReturnGas(gas) @@ -131,7 +160,7 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = env.EVM().Run(contract, input) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. @@ -148,7 +177,12 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by // case of an execution error or failed value transfer. // // CallCode differs from Call in the sense that it executes the given address' code with the caller as context. -func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) { +func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + if env.Depth == 0 { + env.init() + defer close(env.quit) + } + if env.vmConfig.NoRecursion && env.Depth > 0 { caller.ReturnGas(gas) @@ -179,7 +213,7 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, input contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = env.EVM().Run(contract, input) if err != nil { contract.UseGas(contract.Gas) @@ -194,7 +228,12 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, input // // DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context // and the caller is set to the caller of the caller. -func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) { +func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { + if env.Depth == 0 { + env.init() + defer close(env.quit) + } + if env.vmConfig.NoRecursion && env.Depth > 0 { caller.ReturnGas(gas) @@ -218,7 +257,7 @@ func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, in contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = env.EVM().Run(contract, input) if err != nil { contract.UseGas(contract.Gas) @@ -229,7 +268,12 @@ func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, in } // Create creates a new contract using code as deployment code. -func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) { +func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) { + if env.Depth == 0 { + env.init() + defer close(env.quit) + } + if env.vmConfig.NoRecursion && env.Depth > 0 { caller.ReturnGas(gas) @@ -254,12 +298,10 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. env.StateDB.SetNonce(caller.Address(), nonce+1) snapshot := env.StateDB.Snapshot() - var ( - addr = crypto.CreateAddress(caller.Address(), nonce) - to = env.StateDB.CreateAccount(addr) - ) + contractAddr = crypto.CreateAddress(caller.Address(), nonce) + to := env.StateDB.CreateAccount(contractAddr) if env.ChainConfig().IsEIP158(env.BlockNumber) { - env.StateDB.SetNonce(addr, 1) + env.StateDB.SetNonce(contractAddr, 1) } env.Transfer(env.StateDB, caller.Address(), to.Address(), value) @@ -267,10 +309,11 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. // E The contract is a scoped environment for this execution context // only. contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) + contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) defer contract.Finalise() - ret, err := env.EVM().Run(contract, nil) + ret, err = env.EVM().Run(contract, nil) + // check whether the max code size has been exceeded maxCodeSizeExceeded := len(ret) > params.MaxCodeSize // if the contract creation ran successfully and no errors were returned @@ -281,7 +324,7 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) if contract.UseGas(dataGas) { - env.StateDB.SetCode(addr, ret) + env.StateDB.SetCode(contractAddr, ret) } else { err = ErrCodeStoreOutOfGas } @@ -296,7 +339,7 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. env.StateDB.RevertToSnapshot(snapshot) // Nothing should be returned when an error is thrown. - return nil, addr, err + return nil, contractAddr, err } // If the vm returned with an error the return value should be set to nil. // This isn't consensus critical but merely to for behaviour reasons such as @@ -305,7 +348,7 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. ret = nil } - return ret, addr, err + return ret, contractAddr, err } // ChainConfig returns the environment's chain configuration diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index c3e90e9ca7..b9b6096550 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -245,70 +245,68 @@ func gasDup(gt params.GasTable, env *Environment, contract *Contract, stack *Sta return GasFastestStep } -var gasTable [256]*big.Int - -func init() { - gasTable[ADD] = GasFastestStep - gasTable[LT] = GasFastestStep - gasTable[GT] = GasFastestStep - gasTable[SLT] = GasFastestStep - gasTable[SGT] = GasFastestStep - gasTable[EQ] = GasFastestStep - gasTable[ISZERO] = GasFastestStep - gasTable[SUB] = GasFastestStep - gasTable[AND] = GasFastestStep - gasTable[OR] = GasFastestStep - gasTable[XOR] = GasFastestStep - gasTable[NOT] = GasFastestStep - gasTable[BYTE] = GasFastestStep - gasTable[CALLDATALOAD] = GasFastestStep - gasTable[CALLDATACOPY] = GasFastestStep - gasTable[MLOAD] = GasFastestStep - gasTable[MSTORE] = GasFastestStep - gasTable[MSTORE8] = GasFastestStep - gasTable[CODECOPY] = GasFastestStep - gasTable[MUL] = GasFastStep - gasTable[DIV] = GasFastStep - gasTable[SDIV] = GasFastStep - gasTable[MOD] = GasFastStep - gasTable[SMOD] = GasFastStep - gasTable[SIGNEXTEND] = GasFastStep - gasTable[ADDMOD] = GasMidStep - gasTable[MULMOD] = GasMidStep - gasTable[JUMP] = GasMidStep - gasTable[JUMPI] = GasSlowStep - gasTable[EXP] = GasSlowStep - gasTable[ADDRESS] = GasQuickStep - gasTable[ORIGIN] = GasQuickStep - gasTable[CALLER] = GasQuickStep - gasTable[CALLVALUE] = GasQuickStep - gasTable[CODESIZE] = GasQuickStep - gasTable[GASPRICE] = GasQuickStep - gasTable[COINBASE] = GasQuickStep - gasTable[TIMESTAMP] = GasQuickStep - gasTable[NUMBER] = GasQuickStep - gasTable[CALLDATASIZE] = GasQuickStep - gasTable[DIFFICULTY] = GasQuickStep - gasTable[GASLIMIT] = GasQuickStep - gasTable[POP] = GasQuickStep - gasTable[PC] = GasQuickStep - gasTable[MSIZE] = GasQuickStep - gasTable[GAS] = GasQuickStep - gasTable[BLOCKHASH] = GasExtStep - gasTable[BALANCE] = Zero - gasTable[EXTCODESIZE] = Zero - gasTable[EXTCODECOPY] = Zero - gasTable[SLOAD] = params.SloadGas - gasTable[SSTORE] = Zero - gasTable[SHA3] = params.Sha3Gas - gasTable[CREATE] = params.CreateGas - gasTable[CALL] = Zero - gasTable[CALLCODE] = Zero - gasTable[DELEGATECALL] = Zero - gasTable[SUICIDE] = Zero - gasTable[JUMPDEST] = params.JumpdestGas - gasTable[RETURN] = Zero - gasTable[PUSH1] = GasFastestStep - gasTable[DUP1] = Zero - gasTable[STOP] = Zero +var gasTable = [256]*big.Int{ + ADD: GasFastestStep, + LT: GasFastestStep, + GT: GasFastestStep, + SLT: GasFastestStep, + SGT: GasFastestStep, + EQ: GasFastestStep, + ISZERO: GasFastestStep, + SUB: GasFastestStep, + AND: GasFastestStep, + OR: GasFastestStep, + XOR: GasFastestStep, + NOT: GasFastestStep, + BYTE: GasFastestStep, + CALLDATALOAD: GasFastestStep, + CALLDATACOPY: GasFastestStep, + MLOAD: GasFastestStep, + MSTORE: GasFastestStep, + MSTORE8: GasFastestStep, + CODECOPY: GasFastestStep, + MUL: GasFastStep, + DIV: GasFastStep, + SDIV: GasFastStep, + MOD: GasFastStep, + SMOD: GasFastStep, + SIGNEXTEND: GasFastStep, + ADDMOD: GasMidStep, + MULMOD: GasMidStep, + JUMP: GasMidStep, + JUMPI: GasSlowStep, + EXP: GasSlowStep, + ADDRESS: GasQuickStep, + ORIGIN: GasQuickStep, + CALLER: GasQuickStep, + CALLVALUE: GasQuickStep, + CODESIZE: GasQuickStep, + GASPRICE: GasQuickStep, + COINBASE: GasQuickStep, + TIMESTAMP: GasQuickStep, + NUMBER: GasQuickStep, + CALLDATASIZE: GasQuickStep, + DIFFICULTY: GasQuickStep, + GASLIMIT: GasQuickStep, + POP: GasQuickStep, + PC: GasQuickStep, + MSIZE: GasQuickStep, + GAS: GasQuickStep, + BLOCKHASH: GasExtStep, + BALANCE: Zero, + EXTCODESIZE: Zero, + EXTCODECOPY: Zero, + SLOAD: params.SloadGas, + SSTORE: Zero, + SHA3: params.Sha3Gas, + CREATE: params.CreateGas, + CALL: Zero, + CALLCODE: Zero, + DELEGATECALL: Zero, + SUICIDE: Zero, + JUMPDEST: params.JumpdestGas, + RETURN: Zero, + PUSH1: GasFastestStep, + DUP1: Zero, + STOP: Zero, } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5d4c42b2fc..e4ba1139f3 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -553,6 +553,12 @@ func opCallCode(pc *uint64, env *Environment, contract *Contract, memory *Memory } func opDelegateCall(pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // if not homestead return an error. DELEGATECALL is not supported + // during pre-homestead. + if !env.ChainConfig().IsHomestead(env.BlockNumber) { + return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) + } + gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 646143aad8..5f78624393 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -48,817 +48,808 @@ type operation struct { valid bool } -type vmJumpTable [256]operation - -func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable { - var jumpTable vmJumpTable - - jumpTable[ADD] = operation{ +var jumpTable = [256]operation{ + ADD: operation{ execute: opAdd, gasCost: makeGenericGasFunc(ADD), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SUB] = operation{ + }, + SUB: operation{ execute: opSub, gasCost: makeGenericGasFunc(SUB), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[MUL] = operation{ + }, + MUL: operation{ execute: opMul, gasCost: makeGenericGasFunc(MUL), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[DIV] = operation{ + }, + DIV: operation{ execute: opDiv, gasCost: makeGenericGasFunc(DIV), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SDIV] = operation{ + }, + SDIV: operation{ execute: opSdiv, gasCost: makeGenericGasFunc(SDIV), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[MOD] = operation{ + }, + MOD: operation{ execute: opMod, gasCost: makeGenericGasFunc(MOD), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SMOD] = operation{ + }, + SMOD: operation{ execute: opSmod, gasCost: makeGenericGasFunc(SMOD), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[EXP] = operation{ + }, + EXP: operation{ execute: opExp, gasCost: gasExp, validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SIGNEXTEND] = operation{ + }, + SIGNEXTEND: operation{ execute: opSignExtend, gasCost: makeGenericGasFunc(SIGNEXTEND), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[NOT] = operation{ + }, + NOT: operation{ execute: opNot, gasCost: makeGenericGasFunc(NOT), validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[LT] = operation{ + }, + LT: operation{ execute: opLt, gasCost: makeGenericGasFunc(LT), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[GT] = operation{ + }, + GT: operation{ execute: opGt, gasCost: makeGenericGasFunc(GT), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SLT] = operation{ + }, + SLT: operation{ execute: opSlt, gasCost: makeGenericGasFunc(SLT), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[SGT] = operation{ + }, + SGT: operation{ execute: opSgt, gasCost: makeGenericGasFunc(SGT), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[EQ] = operation{ + }, + EQ: operation{ execute: opEq, gasCost: makeGenericGasFunc(EQ), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[ISZERO] = operation{ + }, + ISZERO: operation{ execute: opIszero, gasCost: makeGenericGasFunc(ISZERO), validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[AND] = operation{ + }, + AND: operation{ execute: opAnd, gasCost: makeGenericGasFunc(AND), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[OR] = operation{ + }, + OR: operation{ execute: opOr, gasCost: makeGenericGasFunc(OR), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[XOR] = operation{ + }, + XOR: operation{ execute: opXor, gasCost: makeGenericGasFunc(XOR), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[BYTE] = operation{ + }, + BYTE: operation{ execute: opByte, gasCost: makeGenericGasFunc(BYTE), validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[ADDMOD] = operation{ + }, + ADDMOD: operation{ execute: opAddmod, gasCost: makeGenericGasFunc(ADDMOD), validateStack: makeStackFunc(3, 1), valid: true, - } - jumpTable[MULMOD] = operation{ + }, + MULMOD: operation{ execute: opMulmod, gasCost: makeGenericGasFunc(MULMOD), validateStack: makeStackFunc(3, 1), valid: true, - } - jumpTable[SHA3] = operation{ + }, + SHA3: operation{ execute: opSha3, gasCost: gasSha3, validateStack: makeStackFunc(2, 1), memorySize: memorySha3, valid: true, - } - jumpTable[ADDRESS] = operation{ + }, + ADDRESS: operation{ execute: opAddress, gasCost: makeGenericGasFunc(ADDRESS), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[BALANCE] = operation{ + }, + BALANCE: operation{ execute: opBalance, gasCost: gasBalance, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[ORIGIN] = operation{ + }, + ORIGIN: operation{ execute: opOrigin, gasCost: makeGenericGasFunc(ORIGIN), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[CALLER] = operation{ + }, + CALLER: operation{ execute: opCaller, gasCost: makeGenericGasFunc(CALLER), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[CALLVALUE] = operation{ + }, + CALLVALUE: operation{ execute: opCallValue, gasCost: makeGenericGasFunc(CALLVALUE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[CALLDATALOAD] = operation{ + }, + CALLDATALOAD: operation{ execute: opCalldataLoad, gasCost: makeGenericGasFunc(CALLDATALOAD), validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[CALLDATASIZE] = operation{ + }, + CALLDATASIZE: operation{ execute: opCalldataSize, gasCost: makeGenericGasFunc(CALLDATASIZE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[CALLDATACOPY] = operation{ + }, + CALLDATACOPY: operation{ execute: opCalldataCopy, gasCost: gasCalldataCopy, validateStack: makeStackFunc(3, 1), memorySize: memoryCalldataCopy, valid: true, - } - jumpTable[CODESIZE] = operation{ + }, + CODESIZE: operation{ execute: opCodeSize, gasCost: makeGenericGasFunc(CODESIZE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[EXTCODESIZE] = operation{ + }, + EXTCODESIZE: operation{ execute: opExtCodeSize, gasCost: gasExtCodeSize, validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[CODECOPY] = operation{ + }, + CODECOPY: operation{ execute: opCodeCopy, gasCost: gasCodeCopy, validateStack: makeStackFunc(3, 0), memorySize: memoryCodeCopy, valid: true, - } - jumpTable[EXTCODECOPY] = operation{ + }, + EXTCODECOPY: operation{ execute: opExtCodeCopy, gasCost: gasExtCodeCopy, validateStack: makeStackFunc(4, 0), memorySize: memoryExtCodeCopy, valid: true, - } - jumpTable[GASPRICE] = operation{ + }, + GASPRICE: operation{ execute: opGasprice, gasCost: makeGenericGasFunc(GASPRICE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[BLOCKHASH] = operation{ + }, + BLOCKHASH: operation{ execute: opBlockhash, gasCost: makeGenericGasFunc(BLOCKHASH), validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[COINBASE] = operation{ + }, + COINBASE: operation{ execute: opCoinbase, gasCost: makeGenericGasFunc(COINBASE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[TIMESTAMP] = operation{ + }, + TIMESTAMP: operation{ execute: opTimestamp, gasCost: makeGenericGasFunc(TIMESTAMP), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[NUMBER] = operation{ + }, + NUMBER: operation{ execute: opNumber, gasCost: makeGenericGasFunc(NUMBER), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[DIFFICULTY] = operation{ + }, + DIFFICULTY: operation{ execute: opDifficulty, gasCost: makeGenericGasFunc(DIFFICULTY), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[GASLIMIT] = operation{ + }, + GASLIMIT: operation{ execute: opGasLimit, gasCost: makeGenericGasFunc(GASLIMIT), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[POP] = operation{ + }, + POP: operation{ execute: opPop, gasCost: makeGenericGasFunc(TIMESTAMP), validateStack: makeStackFunc(1, 0), valid: true, - } - jumpTable[MLOAD] = operation{ + }, + MLOAD: operation{ execute: opMload, gasCost: gasMLoad, validateStack: makeStackFunc(1, 1), memorySize: memoryMLoad, valid: true, - } - jumpTable[MSTORE] = operation{ + }, + MSTORE: operation{ execute: opMstore, gasCost: gasMStore, validateStack: makeStackFunc(2, 0), memorySize: memoryMStore, valid: true, - } - jumpTable[MSTORE8] = operation{ + }, + MSTORE8: operation{ execute: opMstore8, gasCost: gasMStore8, memorySize: memoryMStore8, validateStack: makeStackFunc(2, 0), valid: true, - } - jumpTable[SLOAD] = operation{ + }, + SLOAD: operation{ execute: opSload, gasCost: gasSLoad, validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[SSTORE] = operation{ + }, + SSTORE: operation{ execute: opSstore, gasCost: gasSStore, validateStack: makeStackFunc(2, 0), valid: true, - } - jumpTable[JUMPDEST] = operation{ + }, + JUMPDEST: operation{ execute: opJumpdest, gasCost: makeGenericGasFunc(JUMPDEST), validateStack: makeStackFunc(0, 0), valid: true, - } - jumpTable[PC] = operation{ + }, + PC: operation{ execute: opPc, gasCost: makeGenericGasFunc(PC), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[MSIZE] = operation{ + }, + MSIZE: operation{ execute: opMsize, gasCost: makeGenericGasFunc(MSIZE), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[GAS] = operation{ + }, + GAS: operation{ execute: opGas, gasCost: makeGenericGasFunc(GAS), validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[CREATE] = operation{ + }, + CREATE: operation{ execute: opCreate, gasCost: gasCreate, validateStack: makeStackFunc(3, 1), memorySize: memoryCreate, valid: true, - } - jumpTable[CALL] = operation{ + }, + CALL: operation{ execute: opCall, gasCost: gasCall, validateStack: makeStackFunc(7, 1), memorySize: memoryCall, valid: true, - } - jumpTable[CALLCODE] = operation{ + }, + CALLCODE: operation{ execute: opCallCode, gasCost: gasCallCode, validateStack: makeStackFunc(7, 1), memorySize: memoryCall, valid: true, - } - if ruleset.IsHomestead(blockNumber) { - jumpTable[DELEGATECALL] = operation{ - execute: opDelegateCall, - gasCost: gasDelegateCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryDelegateCall, - valid: true, - } - } - - jumpTable[RETURN] = operation{ + }, + DELEGATECALL: operation{ + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: makeStackFunc(6, 1), + memorySize: memoryDelegateCall, + valid: true, + }, + RETURN: operation{ execute: opReturn, gasCost: gasReturn, validateStack: makeStackFunc(2, 0), memorySize: memoryReturn, halts: true, valid: true, - } - jumpTable[SUICIDE] = operation{ + }, + SUICIDE: operation{ execute: opSuicide, gasCost: gasSuicide, validateStack: makeStackFunc(1, 0), halts: true, valid: true, - } - jumpTable[JUMP] = operation{ + }, + JUMP: operation{ execute: opJump, gasCost: makeGenericGasFunc(JUMP), validateStack: makeStackFunc(1, 0), jumps: true, valid: true, - } - jumpTable[JUMPI] = operation{ + }, + JUMPI: operation{ execute: opJumpi, gasCost: makeGenericGasFunc(JUMPI), validateStack: makeStackFunc(2, 0), jumps: true, valid: true, - } - jumpTable[STOP] = operation{ + }, + STOP: operation{ execute: opStop, gasCost: makeGenericGasFunc(STOP), validateStack: makeStackFunc(0, 0), halts: true, valid: true, - } - jumpTable[LOG0] = operation{ + }, + LOG0: operation{ execute: makeLog(0), gasCost: makeGasLog(0), validateStack: makeStackFunc(2, 0), memorySize: memoryLog, valid: true, - } - jumpTable[LOG1] = operation{ + }, + LOG1: operation{ execute: makeLog(1), gasCost: makeGasLog(1), validateStack: makeStackFunc(3, 0), memorySize: memoryLog, valid: true, - } - jumpTable[LOG2] = operation{ + }, + LOG2: operation{ execute: makeLog(2), gasCost: makeGasLog(2), validateStack: makeStackFunc(4, 0), memorySize: memoryLog, valid: true, - } - jumpTable[LOG3] = operation{ + }, + LOG3: operation{ execute: makeLog(3), gasCost: makeGasLog(3), validateStack: makeStackFunc(5, 0), memorySize: memoryLog, valid: true, - } - jumpTable[LOG4] = operation{ + }, + LOG4: operation{ execute: makeLog(4), gasCost: makeGasLog(4), validateStack: makeStackFunc(6, 0), memorySize: memoryLog, valid: true, - } - jumpTable[SWAP1] = operation{ + }, + SWAP1: operation{ execute: makeSwap(1), gasCost: gasSwap, validateStack: makeStackFunc(2, 0), valid: true, - } - jumpTable[SWAP2] = operation{ + }, + SWAP2: operation{ execute: makeSwap(2), gasCost: gasSwap, validateStack: makeStackFunc(3, 0), valid: true, - } - jumpTable[SWAP3] = operation{ + }, + SWAP3: operation{ execute: makeSwap(3), gasCost: gasSwap, validateStack: makeStackFunc(4, 0), valid: true, - } - jumpTable[SWAP4] = operation{ + }, + SWAP4: operation{ execute: makeSwap(4), gasCost: gasSwap, validateStack: makeStackFunc(5, 0), valid: true, - } - jumpTable[SWAP5] = operation{ + }, + SWAP5: operation{ execute: makeSwap(5), gasCost: gasSwap, validateStack: makeStackFunc(6, 0), valid: true, - } - jumpTable[SWAP6] = operation{ + }, + SWAP6: operation{ execute: makeSwap(6), gasCost: gasSwap, validateStack: makeStackFunc(7, 0), valid: true, - } - jumpTable[SWAP7] = operation{ + }, + SWAP7: operation{ execute: makeSwap(7), gasCost: gasSwap, validateStack: makeStackFunc(8, 0), valid: true, - } - jumpTable[SWAP8] = operation{ + }, + SWAP8: operation{ execute: makeSwap(8), gasCost: gasSwap, validateStack: makeStackFunc(9, 0), valid: true, - } - jumpTable[SWAP9] = operation{ + }, + SWAP9: operation{ execute: makeSwap(9), gasCost: gasSwap, validateStack: makeStackFunc(10, 0), valid: true, - } - jumpTable[SWAP10] = operation{ + }, + SWAP10: operation{ execute: makeSwap(10), gasCost: gasSwap, validateStack: makeStackFunc(11, 0), valid: true, - } - jumpTable[SWAP11] = operation{ + }, + SWAP11: operation{ execute: makeSwap(11), gasCost: gasSwap, validateStack: makeStackFunc(12, 0), valid: true, - } - jumpTable[SWAP12] = operation{ + }, + SWAP12: operation{ execute: makeSwap(12), gasCost: gasSwap, validateStack: makeStackFunc(13, 0), valid: true, - } - jumpTable[SWAP13] = operation{ + }, + SWAP13: operation{ execute: makeSwap(13), gasCost: gasSwap, validateStack: makeStackFunc(14, 0), valid: true, - } - jumpTable[SWAP14] = operation{ + }, + SWAP14: operation{ execute: makeSwap(14), gasCost: gasSwap, validateStack: makeStackFunc(15, 0), valid: true, - } - jumpTable[SWAP15] = operation{ + }, + SWAP15: operation{ execute: makeSwap(15), gasCost: gasSwap, validateStack: makeStackFunc(16, 0), valid: true, - } - jumpTable[SWAP16] = operation{ + }, + SWAP16: operation{ execute: makeSwap(16), gasCost: gasSwap, validateStack: makeStackFunc(17, 0), valid: true, - } - jumpTable[PUSH1] = operation{ + }, + PUSH1: operation{ execute: makePush(1, big.NewInt(1)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH2] = operation{ + }, + PUSH2: operation{ execute: makePush(2, big.NewInt(2)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH3] = operation{ + }, + PUSH3: operation{ execute: makePush(3, big.NewInt(3)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH4] = operation{ + }, + PUSH4: operation{ execute: makePush(4, big.NewInt(4)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH5] = operation{ + }, + PUSH5: operation{ execute: makePush(5, big.NewInt(5)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH6] = operation{ + }, + PUSH6: operation{ execute: makePush(6, big.NewInt(6)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH7] = operation{ + }, + PUSH7: operation{ execute: makePush(7, big.NewInt(7)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH8] = operation{ + }, + PUSH8: operation{ execute: makePush(8, big.NewInt(8)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH9] = operation{ + }, + PUSH9: operation{ execute: makePush(9, big.NewInt(9)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH10] = operation{ + }, + PUSH10: operation{ execute: makePush(10, big.NewInt(10)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH11] = operation{ + }, + PUSH11: operation{ execute: makePush(11, big.NewInt(11)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH12] = operation{ + }, + PUSH12: operation{ execute: makePush(12, big.NewInt(12)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH13] = operation{ + }, + PUSH13: operation{ execute: makePush(13, big.NewInt(13)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH14] = operation{ + }, + PUSH14: operation{ execute: makePush(14, big.NewInt(14)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH15] = operation{ + }, + PUSH15: operation{ execute: makePush(15, big.NewInt(15)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH16] = operation{ + }, + PUSH16: operation{ execute: makePush(16, big.NewInt(16)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH17] = operation{ + }, + PUSH17: operation{ execute: makePush(17, big.NewInt(17)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH18] = operation{ + }, + PUSH18: operation{ execute: makePush(18, big.NewInt(18)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH19] = operation{ + }, + PUSH19: operation{ execute: makePush(19, big.NewInt(19)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH20] = operation{ + }, + PUSH20: operation{ execute: makePush(20, big.NewInt(20)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH21] = operation{ + }, + PUSH21: operation{ execute: makePush(21, big.NewInt(21)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH22] = operation{ + }, + PUSH22: operation{ execute: makePush(22, big.NewInt(22)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH23] = operation{ + }, + PUSH23: operation{ execute: makePush(23, big.NewInt(23)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH24] = operation{ + }, + PUSH24: operation{ execute: makePush(24, big.NewInt(24)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH25] = operation{ + }, + PUSH25: operation{ execute: makePush(25, big.NewInt(25)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH26] = operation{ + }, + PUSH26: operation{ execute: makePush(26, big.NewInt(26)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH27] = operation{ + }, + PUSH27: operation{ execute: makePush(27, big.NewInt(27)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH28] = operation{ + }, + PUSH28: operation{ execute: makePush(28, big.NewInt(28)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH29] = operation{ + }, + PUSH29: operation{ execute: makePush(29, big.NewInt(29)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH30] = operation{ + }, + PUSH30: operation{ execute: makePush(30, big.NewInt(30)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH31] = operation{ + }, + PUSH31: operation{ execute: makePush(31, big.NewInt(31)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[PUSH32] = operation{ + }, + PUSH32: operation{ execute: makePush(32, big.NewInt(32)), gasCost: gasPush, validateStack: makeStackFunc(0, 1), valid: true, - } - jumpTable[DUP1] = operation{ + }, + DUP1: operation{ execute: makeDup(1), gasCost: gasDup, validateStack: makeStackFunc(1, 1), valid: true, - } - jumpTable[DUP2] = operation{ + }, + DUP2: operation{ execute: makeDup(2), gasCost: gasDup, validateStack: makeStackFunc(2, 1), valid: true, - } - jumpTable[DUP3] = operation{ + }, + DUP3: operation{ execute: makeDup(3), gasCost: gasDup, validateStack: makeStackFunc(3, 1), valid: true, - } - jumpTable[DUP4] = operation{ + }, + DUP4: operation{ execute: makeDup(4), gasCost: gasDup, validateStack: makeStackFunc(4, 1), valid: true, - } - jumpTable[DUP5] = operation{ + }, + DUP5: operation{ execute: makeDup(5), gasCost: gasDup, validateStack: makeStackFunc(5, 1), valid: true, - } - jumpTable[DUP6] = operation{ + }, + DUP6: operation{ execute: makeDup(6), gasCost: gasDup, validateStack: makeStackFunc(6, 1), valid: true, - } - jumpTable[DUP7] = operation{ + }, + DUP7: operation{ execute: makeDup(7), gasCost: gasDup, validateStack: makeStackFunc(7, 1), valid: true, - } - jumpTable[DUP8] = operation{ + }, + DUP8: operation{ execute: makeDup(8), gasCost: gasDup, validateStack: makeStackFunc(8, 1), valid: true, - } - jumpTable[DUP9] = operation{ + }, + DUP9: operation{ execute: makeDup(9), gasCost: gasDup, validateStack: makeStackFunc(9, 1), valid: true, - } - jumpTable[DUP10] = operation{ + }, + DUP10: operation{ execute: makeDup(10), gasCost: gasDup, validateStack: makeStackFunc(10, 1), valid: true, - } - jumpTable[DUP11] = operation{ + }, + DUP11: operation{ execute: makeDup(11), gasCost: gasDup, validateStack: makeStackFunc(11, 1), valid: true, - } - jumpTable[DUP12] = operation{ + }, + DUP12: operation{ execute: makeDup(12), gasCost: gasDup, validateStack: makeStackFunc(12, 1), valid: true, - } - jumpTable[DUP13] = operation{ + }, + DUP13: operation{ execute: makeDup(13), gasCost: gasDup, validateStack: makeStackFunc(13, 1), valid: true, - } - jumpTable[DUP14] = operation{ + }, + DUP14: operation{ execute: makeDup(14), gasCost: gasDup, validateStack: makeStackFunc(14, 1), valid: true, - } - jumpTable[DUP15] = operation{ + }, + DUP15: operation{ execute: makeDup(15), gasCost: gasDup, validateStack: makeStackFunc(15, 1), valid: true, - } - jumpTable[DUP16] = operation{ + }, + DUP16: operation{ execute: makeDup(16), gasCost: gasDup, validateStack: makeStackFunc(16, 1), valid: true, - } - - return jumpTable + }, } diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go deleted file mode 100644 index 6a6bb5cf42..0000000000 --- a/core/vm/jump_table_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package vm - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/params" -) - -func TestInit(t *testing.T) { - jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(0)) - if jumpTable[DELEGATECALL].valid { - t.Error("Expected DELEGATECALL not to be present") - } - - for _, n := range []int64{1, 2, 100} { - jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(n)) - if !jumpTable[DELEGATECALL].valid { - t.Error("Expected DELEGATECALL to be present for block", n) - } - } -} diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 05ad32fd85..d8c43a0d2a 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -17,6 +17,7 @@ package vm import ( + "context" "math/big" "testing" @@ -52,7 +53,7 @@ func (d dummyStateDB) GetAccount(common.Address) Account { func TestStoreCapture(t *testing.T) { var ( - env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) + env = NewEnvironment(Context{Context: context.TODO()}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() @@ -79,7 +80,7 @@ func TestStorageCapture(t *testing.T) { var ( ref = &dummyContractRef{} contract = NewContract(ref, ref, new(big.Int), new(big.Int)) - env = NewEnvironment(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) + env = NewEnvironment(Context{Context: context.TODO()}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 3cf0dd024a..678b0a55fa 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -17,6 +17,7 @@ package runtime import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" @@ -27,6 +28,7 @@ import ( func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment { context := vm.Context{ + Context: context.TODO(), CanTransfer: core.CanTransfer, Transfer: core.Transfer, GetHash: func(uint64) common.Hash { return common.Hash{} }, diff --git a/core/vm/vm.go b/core/vm/vm.go index 2281daecdb..a7cf98a954 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -17,7 +17,6 @@ package vm import ( - "context" "fmt" "math/big" "sync/atomic" @@ -52,38 +51,20 @@ type Config struct { // The EVM will run the byte code VM or JIT VM based on the passed // configuration. type EVM struct { - env *Environment - jumpTable vmJumpTable - cfg Config - gasTable params.GasTable - - // done is an atomic int and is used for - // cancellation during RunWithContext. - done int32 + env *Environment + cfg Config + gasTable params.GasTable } // New returns a new instance of the EVM. func New(env *Environment, cfg Config) *EVM { return &EVM{ - env: env, - jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber), - cfg: cfg, - gasTable: env.ChainConfig().GasTable(env.BlockNumber), + env: env, + cfg: cfg, + gasTable: env.ChainConfig().GasTable(env.BlockNumber), } } -// RunWithContext allows the EVM to be ran with a cancellation method by passing in a context.Context. The EVM -// behaves exactly the same as an EVM without a context. -// -// RunWithContext is only used for the initial call and shouldn't be called more than once. -func (evm *EVM) RunWithContext(ctx context.Context, contract *Contract, input []byte) (ret []byte, err error) { - go func() { - <-ctx.Done() - atomic.StoreInt32(&evm.done, 1) - }() - return evm.Run(contract, input) -} - // Run loops and evaluates the contract's code with the given input data func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { evm.env.Depth++ @@ -135,16 +116,16 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // explicit STOP, RETURN or SUICIDE is executed, an error accured during // the execution of one of the operations or until the evm.done is set by // the parent context.Context. - for atomic.LoadInt32(&evm.done) == 0 { + for atomic.LoadInt32(&evm.env.abort) == 0 { // Get the memory location of pc op = contract.GetOp(pc) // get the operation from the jump table matching the opcode - operation := evm.jumpTable[op] + operation := jumpTable[op] // if the op is invalid abort the process and return an error if !operation.valid { - return nil, fmt.Errorf("Invalid opcode %x", op) + return nil, fmt.Errorf("invalid opcode %x", op) } // validate the stack and make sure there enough stack items available diff --git a/tests/util.go b/tests/util.go index b545a0cc8c..a7ce25d518 100644 --- a/tests/util.go +++ b/tests/util.go @@ -18,6 +18,7 @@ package tests import ( "bytes" + "context" "encoding/hex" "fmt" "math/big" @@ -190,6 +191,8 @@ func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *st } context := vm.Context{ + Context: context.TODO(), + CanTransfer: canTransfer, Transfer: transfer, GetHash: func(n uint64) common.Hash {