diff options
Diffstat (limited to 'core/vm/environment.go')
-rw-r--r-- | core/vm/environment.go | 171 |
1 files changed, 92 insertions, 79 deletions
diff --git a/core/vm/environment.go b/core/vm/environment.go index 50a09d444..b74b3a795 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -19,6 +19,7 @@ package vm import ( "fmt" "math/big" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -55,41 +56,54 @@ type Context struct { Difficulty *big.Int // Provides information for DIFFICULTY } -// Environment provides information about external sources for the EVM +// EVM provides information about external sources for the EVM // -// The Environment should never be reused and is not thread safe. -type Environment struct { +// The EVM should never be reused and is not thread safe. +type EVM struct { // Context provides auxiliary blockchain related information Context // StateDB gives access to the underlying state StateDB StateDB // Depth is the current call stack - Depth int + 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. + interpreter *Interpreter + // abort is used to abort the EVM calling operations + // NOTE: must be set atomically + abort int32 } -// NewEnvironment retutrns a new EVM environment. -func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment { - env := &Environment{ - Context: context, +// NewEVM retutrns a new EVM evmironment. +func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { + evm := &EVM{ + Context: ctx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, } - env.evm = New(env, vmConfig) - return env + + evm.interpreter = NewInterpreter(evm, vmConfig) + return evm +} + +// Cancel cancels any running EVM operation. This may be called concurrently and it's safe to be +// called multiple times. +func (evm *EVM) Cancel() { + atomic.StoreInt32(&evm.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) { - if env.vmConfig.NoRecursion && env.Depth > 0 { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { caller.ReturnGas(gas) return nil, nil @@ -97,48 +111,48 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth > int(params.CallCreateDepth.Int64()) { + if evm.depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas) - return nil, DepthError + return nil, ErrDepth } - if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { caller.ReturnGas(gas) return nil, ErrInsufficientBalance } var ( - to Account - snapshotPreTransfer = env.StateDB.Snapshot() + to Account + snapshot = evm.StateDB.Snapshot() ) - if !env.StateDB.Exist(addr) { - if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 { + if !evm.StateDB.Exist(addr) { + if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 { caller.ReturnGas(gas) return nil, nil } - to = env.StateDB.CreateAccount(addr) + to = evm.StateDB.CreateAccount(addr) } else { - to = env.StateDB.GetAccount(addr) + to = evm.StateDB.GetAccount(addr) } - env.Transfer(env.StateDB, caller.Address(), to.Address(), value) + evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // initialise a new contract and set the code that is to be used by the - // E The contract is a scoped environment for this execution context + // E The contract is a scoped evmironment for this execution context // only. contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = evm.interpreter.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. if err != nil { contract.UseGas(contract.Gas) - env.StateDB.RevertToSnapshot(snapshotPreTransfer) + evm.StateDB.RevertToSnapshot(snapshot) } return ret, err } @@ -148,8 +162,8 @@ 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) { - if env.vmConfig.NoRecursion && env.Depth > 0 { +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { caller.ReturnGas(gas) return nil, nil @@ -157,33 +171,33 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, input // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth > int(params.CallCreateDepth.Int64()) { + if evm.depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas) - return nil, DepthError + return nil, ErrDepth } - if !env.CanTransfer(env.StateDB, caller.Address(), value) { + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { caller.ReturnGas(gas) - return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address())) + return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address())) } var ( - snapshotPreTransfer = env.StateDB.Snapshot() - to = env.StateDB.GetAccount(caller.Address()) + snapshot = evm.StateDB.Snapshot() + to = evm.StateDB.GetAccount(caller.Address()) ) // initialise a new contract and set the code that is to be used by the - // E The contract is a scoped environment for this execution context + // E The contract is a scoped evmironment for this execution context // only. contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = evm.interpreter.Run(contract, input) if err != nil { contract.UseGas(contract.Gas) - env.StateDB.RevertToSnapshot(snapshotPreTransfer) + evm.StateDB.RevertToSnapshot(snapshot) } return ret, err @@ -194,8 +208,8 @@ 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) { - if env.vmConfig.NoRecursion && env.Depth > 0 { +func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { caller.ReturnGas(gas) return nil, nil @@ -203,34 +217,34 @@ func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, in // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth > int(params.CallCreateDepth.Int64()) { + if evm.depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas) - return nil, DepthError + return nil, ErrDepth } var ( - snapshot = env.StateDB.Snapshot() - to = env.StateDB.GetAccount(caller.Address()) + snapshot = evm.StateDB.Snapshot() + to = evm.StateDB.GetAccount(caller.Address()) ) // Iinitialise a new contract and make initialise the delegate values contract := NewContract(caller, to, caller.Value(), gas).AsDelegate() - contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) defer contract.Finalise() - ret, err := env.EVM().Run(contract, input) + ret, err = evm.interpreter.Run(contract, input) if err != nil { contract.UseGas(contract.Gas) - env.StateDB.RevertToSnapshot(snapshot) + evm.StateDB.RevertToSnapshot(snapshot) } return ret, err } // 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) { - if env.vmConfig.NoRecursion && env.Depth > 0 { +func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { caller.ReturnGas(gas) return nil, common.Address{}, nil @@ -238,39 +252,38 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth > int(params.CallCreateDepth.Int64()) { + if evm.depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas) - return nil, common.Address{}, DepthError + return nil, common.Address{}, ErrDepth } - if !env.CanTransfer(env.StateDB, caller.Address(), value) { + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { caller.ReturnGas(gas) return nil, common.Address{}, ErrInsufficientBalance } // Create a new account on the state - nonce := env.StateDB.GetNonce(caller.Address()) - env.StateDB.SetNonce(caller.Address(), nonce+1) - - snapshotPreTransfer := env.StateDB.Snapshot() - var ( - addr = crypto.CreateAddress(caller.Address(), nonce) - to = env.StateDB.CreateAccount(addr) - ) - if env.ChainConfig().IsEIP158(env.BlockNumber) { - env.StateDB.SetNonce(addr, 1) + nonce := evm.StateDB.GetNonce(caller.Address()) + evm.StateDB.SetNonce(caller.Address(), nonce+1) + + snapshot := evm.StateDB.Snapshot() + contractAddr = crypto.CreateAddress(caller.Address(), nonce) + to := evm.StateDB.CreateAccount(contractAddr) + if evm.ChainConfig().IsEIP158(evm.BlockNumber) { + evm.StateDB.SetNonce(contractAddr, 1) } - env.Transfer(env.StateDB, caller.Address(), to.Address(), value) + evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // initialise a new contract and set the code that is to be used by the - // E The contract is a scoped environment for this execution context + // E The contract is a scoped evmironment 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 = evm.interpreter.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,9 +294,9 @@ 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) + evm.StateDB.SetCode(contractAddr, ret) } else { - err = CodeStoreOutOfGasError + err = ErrCodeStoreOutOfGas } } @@ -291,12 +304,12 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big. // 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. if maxCodeSizeExceeded || - (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) { + (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { contract.UseGas(contract.Gas) - env.StateDB.RevertToSnapshot(snapshotPreTransfer) + evm.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,11 +318,11 @@ 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 -func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig } +// ChainConfig returns the evmironment's chain configuration +func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } -// EVM returns the environments EVM -func (env *Environment) EVM() Vm { return env.evm } +// Interpreter returns the EVM interpreter +func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } |