From 3df7142b3e2804aa7ccf88cc0c2663861ebfa3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 15 Aug 2017 12:56:09 +0300 Subject: [PATCH] core/vm: minor polishes, fix STATICCALL for precompiles * Fix STATICCALL so it is able to call precompiles too * Fix write detection to use the correct value argument of CALL * Fix write protection to ignore the value in CALLCODE --- core/vm/evm.go | 63 ++++++++++++++++++++---------------------- core/vm/interpreter.go | 7 ++--- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 97a9d31053..cc4214a163 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -123,19 +123,20 @@ func (evm *EVM) Cancel() { atomic.StoreInt32(&evm.abort, 1) } -// Call executes the contract associated with the addr with the given input as parameters. 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. +// Call executes the contract associated with the addr with the given input as +// parameters. 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 (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } - // Depth check execution. Fail if we're trying to execute above the - // limit. + // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } + // Fail if we're trying to transfer more than the available balance if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } @@ -173,21 +174,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return ret, contract.Gas, err } -// CallCode executes the contract associated with the addr with the given input as parameters. 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. +// CallCode executes the contract associated with the addr with the given input +// as parameters. 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. // -// CallCode differs from Call in the sense that it executes the given address' code with the caller as context. +// CallCode differs from Call in the sense that it executes the given address' +// code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } - // Depth check execution. Fail if we're trying to execute above the - // limit. + // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } + // Fail if we're trying to transfer more than the available balance if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } @@ -211,18 +214,16 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, return ret, contract.Gas, err } -// DelegateCall executes the contract associated with the addr with the given input as parameters. -// It reverses the state in case of an execution error. +// DelegateCall executes the contract associated with the addr with the given input +// as parameters. It reverses the state in case of an execution error. // -// 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. +// 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 (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } - - // Depth check execution. Fail if we're trying to execute above the - // limit. + // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } @@ -232,7 +233,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by to = AccountRef(caller.Address()) ) - // Iinitialise a new contract and make initialise the delegate values + // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, to, nil, gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) @@ -245,18 +246,19 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by return ret, contract.Gas, err } +// StaticCall executes the contract associated with the addr with the given input +// as parameters while disallowing any modifications to the state during the call. +// Opcodes that attempt to perform such modifications will result in exceptions +// instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } - - // Depth check execution. Fail if we're trying to execute above the - // limit. + // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - - // make sure the readonly is only set if we aren't in readonly yet + // Make sure the readonly is only set if we aren't in readonly yet // this makes also sure that the readonly flag isn't removed for // child calls. if !evm.interpreter.readonly { @@ -268,23 +270,18 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte to = AccountRef(addr) snapshot = evm.StateDB.Snapshot() ) - if !evm.StateDB.Exist(addr) { - return nil, gas, nil - } - - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped evmironment for this execution context + // Initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context // only. contract := NewContract(caller, to, new(big.Int), gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = evm.interpreter.Run(snapshot, 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. + // when we're in Homestead this also counts for code storage gas errors. + ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) } return ret, contract.Gas, err diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 32d764b9f4..661ada6910 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -89,13 +89,12 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter { func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { if in.evm.chainRules.IsMetropolis { if in.readonly { - // if the interpreter is operating in readonly mode, make sure no - // state-modifying operation is performed. The 4th stack item + // If the interpreter is operating in readonly mode, make sure no + // state-modifying operation is performed. The 3rd stack item // for a call operation is the value. Transfering value from one // account to the others means the state is modified and should also // return with an error. - if operation.writes || - ((op == CALL || op == CALLCODE) && stack.Back(3).BitLen() > 0) { + if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) { return errWriteProtection } }