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
This commit is contained in:
Péter Szilágyi 2017-08-15 12:56:09 +03:00
parent 3d123bcde6
commit 3df7142b3e
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
2 changed files with 33 additions and 37 deletions

View File

@ -123,19 +123,20 @@ func (evm *EVM) Cancel() {
atomic.StoreInt32(&evm.abort, 1) atomic.StoreInt32(&evm.abort, 1)
} }
// Call executes the contract associated with the addr with the given input as parameters. It also handles any // Call executes the contract associated with the addr with the given input as
// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // parameters. It also handles any necessary value transfer required and takes
// case of an execution error or failed value transfer. // 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) { 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 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil return nil, gas, nil
} }
// Depth check execution. Fail if we're trying to execute above the // Fail if we're trying to execute above the call depth limit
// limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth 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) { if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance 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 return ret, contract.Gas, err
} }
// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any // CallCode executes the contract associated with the addr with the given input
// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // as parameters. It also handles any necessary value transfer required and takes
// case of an execution error or failed value transfer. // 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) { 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 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil return nil, gas, nil
} }
// Depth check execution. Fail if we're trying to execute above the // Fail if we're trying to execute above the call depth limit
// limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
} }
// Fail if we're trying to transfer more than the available balance
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance return nil, gas, ErrInsufficientBalance
} }
@ -211,18 +214,16 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
return ret, contract.Gas, err return ret, contract.Gas, err
} }
// DelegateCall executes the contract associated with the addr with the given input as parameters. // DelegateCall executes the contract associated with the addr with the given input
// It reverses the state in case of an execution error. // 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 // DelegateCall differs from CallCode in the sense that it executes the given address'
// and the caller is set to the caller of the caller. // 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) { 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 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil return nil, gas, nil
} }
// Fail if we're trying to execute above the call depth limit
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
} }
@ -232,7 +233,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
to = AccountRef(caller.Address()) 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 := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) 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 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) { 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 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil return nil, gas, nil
} }
// Fail if we're trying to execute above the call depth limit
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth 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 // this makes also sure that the readonly flag isn't removed for
// child calls. // child calls.
if !evm.interpreter.readonly { if !evm.interpreter.readonly {
@ -268,23 +270,18 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
to = AccountRef(addr) to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot() snapshot = evm.StateDB.Snapshot()
) )
if !evm.StateDB.Exist(addr) { // Initialise a new contract and set the code that is to be used by the
return nil, gas, nil // EVM. The contract is a scoped environment 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 evmironment for this execution context
// only. // only.
contract := NewContract(caller, to, new(big.Int), gas) contract := NewContract(caller, to, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) 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 // 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 // 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 { if err != nil {
contract.UseGas(contract.Gas) contract.UseGas(contract.Gas)
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
} }
return ret, contract.Gas, err return ret, contract.Gas, err

View File

@ -89,13 +89,12 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsMetropolis { if in.evm.chainRules.IsMetropolis {
if in.readonly { if in.readonly {
// if the interpreter is operating in readonly mode, make sure no // If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 4th stack item // state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transfering value from one // for a call operation is the value. Transfering value from one
// account to the others means the state is modified and should also // account to the others means the state is modified and should also
// return with an error. // return with an error.
if operation.writes || if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
((op == CALL || op == CALLCODE) && stack.Back(3).BitLen() > 0) {
return errWriteProtection return errWriteProtection
} }
} }