diff --git a/core/vm/evm.go b/core/vm/evm.go index be90bece714c..609ad525c050 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,7 +17,6 @@ package vm import ( - "errors" "math/big" "sync/atomic" "time" @@ -61,29 +60,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { return p, ok } -// 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, readOnly bool) ([]byte, error) { - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { - for _, interpreter := range evm.interpreters { - if interpreter.CanRun(contract.Code) { - if evm.interpreter != interpreter { - // Ensure that the interpreter pointer is set back - // to its current value upon return. - defer func(i Interpreter) { - evm.interpreter = i - }(evm.interpreter) - evm.interpreter = interpreter - } - return interpreter.Run(contract, input, readOnly) - } - } - } else { - return evm.interpreter.Run(contract, input, false) - } - - return nil, errors.New("no compatible interpreter") -} - // BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. type BlockContext struct { @@ -143,8 +119,7 @@ type EVM struct { Config Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. - interpreters []Interpreter - interpreter Interpreter + interpreter *EVMInterpreter // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 @@ -165,14 +140,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber), - interpreters: make([]Interpreter, 0, 1), } - // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here - // as we always want to have the built-in EVM as the failover option. - evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config)) - evm.interpreter = evm.interpreters[0] - + evm.interpreter = NewEVMInterpreter(evm, config) return evm } @@ -195,7 +165,7 @@ func (evm *EVM) Cancelled() bool { } // Interpreter returns the current interpreter -func (evm *EVM) Interpreter() Interpreter { +func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } @@ -264,7 +234,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } } @@ -321,7 +291,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } if err != nil { @@ -361,7 +331,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } if err != nil { @@ -418,7 +388,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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. readOnly := evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) - ret, err = run(evm, contract, input, readOnly) + ret, err = evm.interpreter.Run(contract, input, readOnly) gas = contract.Gas } if err != nil { @@ -489,7 +459,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } start := time.Now() - ret, err := run(evm, contract, nil, false) + ret, err := evm.interpreter.Run(contract, nil, false) // Check whether the max code size has been exceeded, assign err if the case. if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 73c1139152c8..98ae3e81ba85 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -96,7 +96,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = env.interpreter.(*EVMInterpreter) + evmInterpreter = env.interpreter ) for i, test := range tests { @@ -228,6 +228,30 @@ func TestAddMod(t *testing.T) { } } +// getResult is a convenience function to generate the expected values +func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + var ( + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter + ) + result := make([]TwoOperandTestcase, len(args)) + for i, param := range args { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) + stack.push(x) + stack.push(y) + _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) + if err != nil { + log.Fatalln(err) + } + actual := stack.pop() + result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} + } + return result +} + // utility function to fill the json-file with testcases // Enable this test to generate the 'testcases_xx.json' files func TestWriteExpectedValues(t *testing.T) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0df6c427e41c..a7aa8dde094c 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,34 +32,9 @@ type Config struct { JumpTable *JumpTable // EVM instruction table, automatically populated if unset - EWASMInterpreter string // External EWASM interpreter options - EVMInterpreter string // External EVM interpreter options - ExtraEips []int // Additional EIPS that are to be enabled } -// Interpreter is used to run Ethereum based contracts and will utilise the -// passed environment to query external sources for state information. -// The Interpreter will run the byte code VM based on the passed -// configuration. -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, 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: - // - // ```golang - // for _, interpreter := range interpreters { - // if interpreter.CanRun(contract.code) { - // interpreter.Run(contract.code, input) - // } - // } - // ``` - CanRun([]byte) bool -} - // ScopeContext contains the things that are per-call, such as stack and memory, // but not transients like pc and gas type ScopeContext struct { @@ -274,9 +249,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return res, err } - -// CanRun tells if the contract, passed as an argument, can be -// run by the current interpreter. -func (in *EVMInterpreter) CanRun(code []byte) bool { - return true -} diff --git a/tests/state_test.go b/tests/state_test.go index 1b77c6df5265..8bf60af26407 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -74,17 +74,21 @@ func TestState(t *testing.T) { const traceErrorLimit = 400000 func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { - err := test(vm.Config{}) + // Use config from command line arguments. + config := vm.Config{} + err := test(config) if err == nil { return } - t.Error(err) + + // Test failed, re-run with tracing enabled. if gasLimit > traceErrorLimit { t.Log("gas limit too high for EVM trace") return } tracer := vm.NewStructLogger(nil) - err2 := test(vm.Config{Debug: true, Tracer: tracer}) + config.Debug, config.Tracer = true, tracer + err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) }