From 7bf8fb274b8fa1316eabda8f39c8d3561c790a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 7 Sep 2018 18:13:25 +0200 Subject: [PATCH] core/vm: Hide read only flag from Interpreter interface (#17461) Makes Interface interface a bit more stateless and abstract. Obviously this change is dictated by EVMC design. The EVMC tries to keep the responsibility for EVM features totally inside the VMs, if feasible. This makes VM "stateless" because VM does not need to pass any information between executions, all information is included in parameters of the execute function. --- core/vm/evm.go | 21 +++++++-------------- core/vm/interpreter.go | 33 +++++++++++++++++---------------- eth/tracers/tracer_test.go | 2 +- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index b835d1886962..8b7a7cc95ddf 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -41,7 +41,7 @@ type ( ) // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. -func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { +func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { precompiles := PrecompiledContractsHomestead if evm.ChainConfig().IsByzantium(evm.BlockNumber) { @@ -61,7 +61,7 @@ func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { }(evm.interpreter) evm.interpreter = interpreter } - return interpreter.Run(contract, input) + return interpreter.Run(contract, input, readOnly) } } return nil, ErrNoCompatibleInterpreter @@ -217,7 +217,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) }() } - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, false) // 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 @@ -262,7 +262,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, false) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -295,7 +295,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by contract := NewContract(caller, to, nil, gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, false) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -317,13 +317,6 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - // 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.IsReadOnly() { - evm.interpreter.SetReadOnly(true) - defer func() { evm.interpreter.SetReadOnly(false) }() - } var ( to = AccountRef(addr) @@ -338,7 +331,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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. - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, true) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -391,7 +384,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I } start := time.Now() - ret, err = run(evm, contract, nil) + ret, err = run(evm, contract, nil, false) // check whether the max code size has been exceeded maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 3e5503855804..35480b83d88e 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -53,7 +53,7 @@ type Config struct { type Interpreter interface { // Run loops and evaluates the contract's code with the given input data and returns // the return byte-slice and an error if one occurred. - Run(contract *Contract, input []byte) ([]byte, error) + Run(contract *Contract, input []byte, static bool) ([]byte, error) // CanRun tells if the contract, passed as an argument, can be // run by the current interpreter. This is meant so that the // caller can do something like: @@ -66,10 +66,6 @@ type Interpreter interface { // } // ``` CanRun([]byte) bool - // IsReadOnly reports if the interpreter is in read only mode. - IsReadOnly() bool - // SetReadOnly sets (or unsets) read only mode in the interpreter. - SetReadOnly(bool) } // EVMInterpreter represents an EVM interpreter @@ -131,11 +127,26 @@ func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, st // It's important to note that any errors returned by the interpreter should be // considered a revert-and-consume-all-gas operation except for // errExecutionReverted which means revert-and-keep-gas-left. -func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { +func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { + if in.intPool == nil { + in.intPool = poolOfIntPools.get() + defer func() { + poolOfIntPools.put(in.intPool) + in.intPool = nil + }() + } + // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() + // 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 readOnly && !in.readOnly { + in.readOnly = true + defer func() { in.readOnly = false }() + } + // Reset the previous call's return data. It's unimportant to preserve the old buffer // as every returning call will return new data anyway. in.returnData = nil @@ -258,13 +269,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err func (in *EVMInterpreter) CanRun(code []byte) bool { return true } - -// IsReadOnly reports if the interpreter is in read only mode. -func (in *EVMInterpreter) IsReadOnly() bool { - return in.readOnly -} - -// SetReadOnly sets (or unsets) read only mode in the interpreter. -func (in *EVMInterpreter) SetReadOnly(ro bool) { - in.readOnly = ro -} \ No newline at end of file diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index 117c376b8165..58b62472451c 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -49,7 +49,7 @@ func runTrace(tracer *Tracer) (json.RawMessage, error) { contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} - _, err := env.Interpreter().Run(contract, []byte{}) + _, err := env.Interpreter().Run(contract, []byte{}, false) if err != nil { return nil, err }