diff --git a/execution/tracing/tracers/js/tracer_test.go b/execution/tracing/tracers/js/tracer_test.go index 0857ddf9bf3..351f122696e 100644 --- a/execution/tracing/tracers/js/tracer_test.go +++ b/execution/tracing/tracers/js/tracer_test.go @@ -63,7 +63,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *chain.Config, tracer.OnTxStart(env.GetVMContext(), types.NewTransaction(0, accounts.ZeroAddress.Value(), nil, gasLimit, nil, nil), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), false, []byte{}, startGas, value, contractCode) - ret, endGas, err := env.Interpreter().Run(contract, startGas, []byte{}, false) + ret, endGas, err := env.Run(contract, startGas, []byte{}, false) tracer.OnExit(0, ret, startGas-endGas, err, true) // Rest gas assumes no refund tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - endGas}, nil) diff --git a/execution/tracing/tracers/logger/logger_test.go b/execution/tracing/tracers/logger/logger_test.go index 17b335c27e3..0d858a6f69d 100644 --- a/execution/tracing/tracers/logger/logger_test.go +++ b/execution/tracing/tracers/logger/logger_test.go @@ -63,7 +63,7 @@ func TestStoreCapture(t *testing.T) { contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash logger.OnTxStart(evm.GetVMContext(), nil, accounts.ZeroAddress) - _, _, err := evm.Interpreter().Run(contract, 100000, []byte{}, false) + _, _, err := evm.Run(contract, 100000, []byte{}, false) if err != nil { t.Fatal(err) } diff --git a/execution/vm/eips.go b/execution/vm/eips.go index 32326b03d0c..34afa7623ab 100644 --- a/execution/vm/eips.go +++ b/execution/vm/eips.go @@ -94,8 +94,8 @@ func enable1884(jt *JumpTable) { } } -func opSelfBalance(pc uint64, interpreter *EVMInterpreter, callContext *CallContext) (uint64, []byte, error) { - balance, err := interpreter.evm.IntraBlockState().GetBalance(callContext.Contract.Address()) +func opSelfBalance(pc uint64, evm *EVM, callContext *CallContext) (uint64, []byte, error) { + balance, err := evm.IntraBlockState().GetBalance(callContext.Contract.Address()) if err != nil { return pc, nil, err } @@ -116,8 +116,8 @@ func enable1344(jt *JumpTable) { } // opChainID implements CHAINID opcode -func opChainID(pc uint64, interpreter *EVMInterpreter, callContext *CallContext) (uint64, []byte, error) { - chainId, _ := uint256.FromBig(interpreter.evm.ChainRules().ChainID) +func opChainID(pc uint64, evm *EVM, callContext *CallContext) (uint64, []byte, error) { + chainId, _ := uint256.FromBig(evm.ChainRules().ChainID) callContext.Stack.push(*chainId) return pc, nil, nil } @@ -203,28 +203,28 @@ func enable1153(jt *JumpTable) { } // opTload implements TLOAD opcode -func opTload(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opTload(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { loc := scope.Stack.peek() key := accounts.InternKey(loc.Bytes32()) - val := interpreter.evm.IntraBlockState().GetTransientState(scope.Contract.Address(), key) + val := evm.IntraBlockState().GetTransientState(scope.Contract.Address(), key) loc.SetBytes(val.Bytes()) return pc, nil, nil } // opTstore implements TSTORE opcode -func opTstore(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opTstore(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } loc := scope.Stack.pop() val := scope.Stack.pop() - interpreter.evm.IntraBlockState().SetTransientState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32()), val) + evm.IntraBlockState().SetTransientState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32()), val) return pc, nil, nil } // opBaseFee implements BASEFEE opcode -func opBaseFee(pc uint64, interpreter *EVMInterpreter, callContext *CallContext) (uint64, []byte, error) { - baseFee := interpreter.evm.Context.BaseFee +func opBaseFee(pc uint64, evm *EVM, callContext *CallContext) (uint64, []byte, error) { + baseFee := evm.Context.BaseFee callContext.Stack.push(baseFee) return pc, nil, nil } @@ -241,7 +241,7 @@ func enable3855(jt *JumpTable) { } // opPush0 implements the PUSH0 opcode -func opPush0(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opPush0(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(uint256.Int{}) return pc, nil, nil } @@ -265,10 +265,10 @@ func enable4844(jt *JumpTable) { } // opBlobHash implements the BLOBHASH opcode -func opBlobHash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opBlobHash(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { idx := scope.Stack.peek() - if idx.LtUint64(uint64(len(interpreter.evm.BlobHashes))) { - hash := interpreter.evm.BlobHashes[idx.Uint64()] + if idx.LtUint64(uint64(len(evm.BlobHashes))) { + hash := evm.BlobHashes[idx.Uint64()] idx.SetBytes(hash.Bytes()) } else { idx.Clear() @@ -276,7 +276,7 @@ func opBlobHash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin return pc, nil, nil } -func opCLZ(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCLZ(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x := scope.Stack.peek() // count leading zero bits in x x.SetUint64(256 - uint64(x.BitLen())) @@ -297,7 +297,7 @@ func enable5656(jt *JumpTable) { } // opMcopy implements the MCOPY opcode (https://eips.ethereum.org/EIPS/eip-5656) -func opMcopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMcopy(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( dst = scope.Stack.pop() src = scope.Stack.pop() @@ -315,8 +315,8 @@ func enable6780(jt *JumpTable) { } // opBlobBaseFee implements the BLOBBASEFEE opcode -func opBlobBaseFee(pc uint64, interpreter *EVMInterpreter, callContext *CallContext) (uint64, []byte, error) { - blobBaseFee := interpreter.evm.Context.BlobBaseFee +func opBlobBaseFee(pc uint64, evm *EVM, callContext *CallContext) (uint64, []byte, error) { + blobBaseFee := evm.Context.BlobBaseFee callContext.Stack.push(blobBaseFee) return pc, nil, nil } diff --git a/execution/vm/evm.go b/execution/vm/evm.go index 8298af66037..eabcba9249f 100644 --- a/execution/vm/evm.go +++ b/execution/vm/evm.go @@ -26,6 +26,7 @@ import ( "github.com/holiman/uint256" + "github.com/erigontech/erigon/common" "github.com/erigontech/erigon/common/crypto" "github.com/erigontech/erigon/common/dbg" "github.com/erigontech/erigon/common/u256" @@ -64,6 +65,12 @@ type EVM struct { // IntraBlockState gives access to the underlying state intraBlockState *state.IntraBlockState + // table holds the opcode specific handlers + jt *JumpTable + + // depth is the current call stack + depth int + // chainConfig contains information about the current chain chainConfig *chain.Config // chain rules contains the chain rules for the current epoch @@ -71,9 +78,6 @@ type EVM struct { // virtual machine configuration options used to initialise the // evm. config 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 abort atomic.Bool // callGasTemp holds the gas available for the current call. This is needed because the @@ -82,6 +86,12 @@ type EVM struct { callGasTemp uint64 // optional overridden set of precompiled contracts precompiles PrecompiledContracts + + hasher keccakState // Keccak256 hasher instance shared across opcodes + hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes + + readOnly bool // Whether to throw on stateful modifications + returnData []byte // Last CALL's return data for subsequent reuse } // NewEVM returns a new EVM. The returned EVM is not thread safe and should @@ -100,7 +110,7 @@ func NewEVM(blockCtx evmtypes.BlockContext, txCtx evmtypes.TxContext, ibs *state chainConfig: chainConfig, chainRules: blockCtx.Rules(chainConfig), } - evm.interpreter = NewEVMInterpreter(evm, vmConfig) + evm.jt = jumpTable(evm.chainRules, vmConfig) return evm } @@ -127,7 +137,9 @@ func (evm *EVM) ResetBetweenBlocks(blockCtx evmtypes.BlockContext, txCtx evmtype evm.config = vmConfig evm.chainRules = chainRules - evm.interpreter = NewEVMInterpreter(evm, vmConfig) + evm.depth = 0 + evm.returnData = nil + evm.jt = jumpTable(chainRules, vmConfig) // ensure the evm is reset to be used again evm.abort.Store(false) @@ -155,17 +167,12 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { evm.precompiles = precompiles } -// Interpreter returns the current interpreter -func (evm *EVM) Interpreter() Interpreter { - return evm.interpreter -} - func (evm *EVM) call(typ OpCode, caller accounts.Address, callerAddress accounts.Address, addr accounts.Address, input []byte, gas uint64, value uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) { if evm.abort.Load() { return ret, leftOverGas, nil } - depth := evm.interpreter.Depth() + depth := evm.depth version := evm.intraBlockState.Version() if (dbg.TraceTransactionIO && !dbg.TraceInstructions) && (evm.intraBlockState.Trace() || dbg.TraceAccount(caller.Handle())) { @@ -283,7 +290,7 @@ func (evm *EVM) call(typ OpCode, caller accounts.Address, callerAddress accounts if typ == STATICCALL { readOnly = true } - ret, gas, err = evm.interpreter.Run(contract, gas, input, readOnly) + ret, gas, err = evm.Run(contract, gas, input, readOnly) } // 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 @@ -370,7 +377,7 @@ func (evm *EVM) create(caller accounts.Address, codeAndHash *codeAndHash, gasRem }() } - depth := evm.interpreter.Depth() + depth := evm.depth // BAL: record target address even on failed CREATE/CREATE2 calls evm.intraBlockState.MarkAddressAccess(address) @@ -458,7 +465,7 @@ func (evm *EVM) create(caller accounts.Address, codeAndHash *codeAndHash, gasRem return nil, address, gasRemaining, nil } - ret, gasRemaining, err = evm.interpreter.Run(contract, gasRemaining, nil, false) + ret, gasRemaining, err = evm.Run(contract, gasRemaining, nil, false) // EIP-170: Contract code size limit if err == nil && evm.chainRules.IsSpuriousDragon && len(ret) > evm.maxCodeSize() { @@ -605,8 +612,3 @@ func (evm *EVM) captureEnd(depth int, typ OpCode, startGas uint64, leftOverGas u tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } } - -// Depth returns the current depth -func (evm *EVM) Depth() int { - return evm.interpreter.Depth() -} diff --git a/execution/vm/evm_test.go b/execution/vm/evm_test.go deleted file mode 100644 index 82ea3721fb3..00000000000 --- a/execution/vm/evm_test.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package vm - -import ( - "fmt" - "strings" - "testing" - - "github.com/holiman/uint256" - "github.com/stretchr/testify/require" - "pgregory.net/rapid" - - "github.com/erigontech/erigon/execution/chain" - "github.com/erigontech/erigon/execution/types/accounts" - "github.com/erigontech/erigon/execution/vm/evmtypes" -) - -func TestEVMWithNoBaseFeeAndNoTxGasPrice(t *testing.T) { - t.Parallel() - vmConfig := Config{NoBaseFee: true} - evm := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, vmConfig) - require.NotNil(t, evm) -} - -func TestInterpreterReadonly(t *testing.T) { - t.Parallel() - //c := NewJumpDestCache(128) - rapid.Check(t, func(t *rapid.T) { - env := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - - isEVMSliceTest := rapid.SliceOfN(rapid.Bool(), 1, -1).Draw(t, "tevm") - readOnlySliceTest := rapid.SliceOfN(rapid.Bool(), len(isEVMSliceTest), len(isEVMSliceTest)).Draw(t, "readonly") - - isEVMCalled := make([]bool, len(isEVMSliceTest)) - readOnlies := make([]*readOnlyState, len(readOnlySliceTest)) - currentIdx := new(int) - *currentIdx = -1 - - evmInterpreter := &testVM{ - readonlyGetSetter: env.interpreter.(*EVMInterpreter), - - recordedReadOnlies: &readOnlies, - recordedIsEVMCalled: &isEVMCalled, - - env: env, - isEVMSliceTest: isEVMSliceTest, - readOnlySliceTest: readOnlySliceTest, - currentIdx: currentIdx, - } - - env.interpreter = evmInterpreter - - dummyContract := NewContract( - accounts.ZeroAddress, - accounts.ZeroAddress, - accounts.ZeroAddress, - uint256.Int{}, - //c, - ) - - newTestSequential(env, currentIdx, readOnlySliceTest, isEVMSliceTest).Run(dummyContract, nil, false) - - var gotReadonly bool - var firstReadOnly int - - // properties-invariants - - if len(readOnlies) != len(readOnlySliceTest) { - t.Fatalf("expected static calls the same stack length as generated. got %d, expected %d", len(readOnlies), len(readOnlySliceTest)) - } - - if len(isEVMCalled) != len(isEVMSliceTest) { - t.Fatalf("expected VM calls the same stack length as generated. got %d, expected %d", len(isEVMCalled), len(isEVMSliceTest)) - } - - if *currentIdx != len(readOnlies) { - t.Fatalf("expected VM calls the same amount of calls as generated calls. got %d, expected %d", *currentIdx, len(readOnlies)) - } - - for i, readOnly := range readOnlies { - - if readOnly.outer != readOnlySliceTest[i] { - t.Fatalf("outer readOnly appeared in %d index, got readOnly %t, expected %t", - i, readOnly.outer, readOnlySliceTest[i]) - } - - if i > 0 { - if readOnly.before != readOnlies[i-1].in { - t.Fatalf("before readOnly appeared in %d index, got readOnly %t, expected %t", - i, readOnly.before, readOnlies[i-1].in) - } - } - - if readOnly.in && !gotReadonly { - gotReadonly = true - firstReadOnly = i - } - - if gotReadonly { - if !readOnly.in { - t.Fatalf("readOnly appeared in %d index, got non-readOnly in %d: %v", - firstReadOnly, i, trace(isEVMCalled, readOnlies)) - } - - switch { - case i < firstReadOnly: - if readOnly.after != false { - t.Fatalf("after readOnly appeared in %d index(first readonly %d, case firstReadOnly: - if readOnly.after != true { - t.Fatalf("after readOnly appeared in %d index(first readonly %d, case >firstReadOnly), got readOnly %t, expected %t", - i, firstReadOnly, readOnly.after, true) - } - } - } else { - if readOnly.after != false { - t.Fatalf("after readOnly didn't appear. %d index, got readOnly %t, expected %t", - i, readOnly.after, false) - } - } - } - }) -} - -func TestReadonlyBasicCases(t *testing.T) { - t.Parallel() - // c := NewJumpDestCache(128) - - cases := []struct { - testName string - readonlySliceTest []bool - - expectedReadonlySlice []readOnlyState - }{ - { - "simple non-readonly", - []bool{false}, - - []readOnlyState{ - { - false, - false, - false, - false, - }, - }, - }, - { - "simple readonly", - []bool{true}, - - []readOnlyState{ - { - true, - true, - true, - false, - }, - }, - }, - - { - "2 calls non-readonly", - []bool{false, false}, - - []readOnlyState{ - { - false, - false, - false, - false, - }, - { - false, - false, - false, - false, - }, - }, - }, - - { - "2 calls true,false", - []bool{true, false}, - - []readOnlyState{ - { - true, - true, - true, - false, - }, - { - true, - true, - true, - true, - }, - }, - }, - { - "2 calls false,true", - []bool{false, true}, - - []readOnlyState{ - { - false, - false, - false, - false, - }, - { - true, - true, - true, - false, - }, - }, - }, - { - "2 calls readonly", - []bool{true, true}, - - []readOnlyState{ - { - true, - true, - true, - true, - }, - { - true, - true, - true, - true, - }, - }, - }, - } - - type evmsParamsTest struct { - emvs []bool - suffix string - } - - evmsTest := make([]evmsParamsTest, len(cases)) - - // fill all possible isEVM combinations - for i, testCase := range cases { - isEVMSliceTest := make([]bool, len(testCase.readonlySliceTest)) - - copy(isEVMSliceTest, testCase.readonlySliceTest) - evmsTest[i].emvs = isEVMSliceTest - - var suffix strings.Builder - suffix.WriteString("-isEVMSliceTest") - for _, evmParam := range testCase.readonlySliceTest { - if evmParam { - suffix.WriteString("-true") - } else { - suffix.WriteString("-false") - } - } - - evmsTest[i].suffix = suffix.String() - } - - for _, testCase := range cases { - for _, evmsParams := range evmsTest { - - testcase := testCase - evmsTestcase := evmsParams - - if len(testcase.readonlySliceTest) != len(evmsTestcase.emvs) { - continue - } - - t.Run(testcase.testName+evmsTestcase.suffix, func(t *testing.T) { - t.Parallel() - readonlySliceTest := testcase.readonlySliceTest - - env := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - - readonliesGot := make([]*readOnlyState, len(testcase.readonlySliceTest)) - isEVMGot := make([]bool, len(evmsTestcase.emvs)) - - currentIdx := new(int) - *currentIdx = -1 - - evmInterpreter := &testVM{ - readonlyGetSetter: env.interpreter.(*EVMInterpreter), - - recordedReadOnlies: &readonliesGot, - recordedIsEVMCalled: &isEVMGot, - - env: env, - isEVMSliceTest: evmsTestcase.emvs, - readOnlySliceTest: testcase.readonlySliceTest, - currentIdx: currentIdx, - } - - env.interpreter = evmInterpreter - - dummyContract := NewContract( - accounts.ZeroAddress, - accounts.ZeroAddress, - accounts.ZeroAddress, - uint256.Int{}, - //c, - ) - - newTestSequential(env, currentIdx, readonlySliceTest, evmsTestcase.emvs).Run(dummyContract, nil, false) - - if len(readonliesGot) != len(readonlySliceTest) { - t.Fatalf("expected static calls the same stack length as generated. got %d, expected %d - %v", len(readonliesGot), len(readonlySliceTest), readonlySliceTest) - } - - if len(isEVMGot) != len(evmsTestcase.emvs) { - t.Fatalf("expected VM calls the same stack length as generated. got %d, expected %d - %v, readonly %v", len(isEVMGot), len(evmsTestcase.emvs), evmsTestcase.emvs, readonlySliceTest) - } - - if *currentIdx != len(readonlySliceTest) { - t.Fatalf("expected VM calls the same amount of calls as generated calls. got %d, expected %d", *currentIdx, len(readonlySliceTest)) - } - - var gotReadonly bool - var firstReadOnly int - - for callIndex, readOnly := range readonliesGot { - if readOnly.outer != readonlySliceTest[callIndex] { - t.Fatalf("outer readOnly appeared in %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v", - callIndex, readOnly.outer, readonlySliceTest[callIndex], evmsTestcase.emvs, readonlySliceTest) - } - - if callIndex > 0 { - if readOnly.before != readonliesGot[callIndex-1].in { - t.Fatalf("before readOnly appeared in %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v", - callIndex, readOnly.before, readonliesGot[callIndex-1].in, evmsTestcase.emvs, readonlySliceTest) - } - } - - if readOnly.in && !gotReadonly { - gotReadonly = true - firstReadOnly = callIndex - } - - if gotReadonly { - if !readOnly.in { - t.Fatalf("readOnly appeared in %d index, got non-readOnly in %d: %v. Test EVMs %v; test readonly %v", - firstReadOnly, callIndex, trace(isEVMGot, readonliesGot), evmsTestcase.emvs, readonlySliceTest) - } - - switch { - case callIndex < firstReadOnly: - if readOnly.after != false { - t.Fatalf("after readOnly appeared in %d index(first readonly %d, case firstReadOnly: - if readOnly.after != true { - t.Fatalf("after readOnly appeared in %d index(first readonly %d, case >firstReadOnly), got readOnly %t, expected %t. Test EVMs %v; test readonly %v", - callIndex, firstReadOnly, readOnly.after, true, evmsTestcase.emvs, readonlySliceTest) - } - } - } else { - if readOnly.after != false { - t.Fatalf("after readOnly didn't appear. %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v", - callIndex, readOnly.after, false, evmsTestcase.emvs, readonlySliceTest) - } - } - } - }) - } - } -} - -type testSequential struct { - env *EVM - currentIdx *int - readOnlys []bool - isEVMCalled []bool -} - -func newTestSequential(env *EVM, currentIdx *int, readonlies []bool, isEVMCalled []bool) *testSequential { - return &testSequential{env, currentIdx, readonlies, isEVMCalled} -} - -func (st *testSequential) Run(_ *Contract, _ []byte, _ bool) ([]byte, uint64, error) { - *st.currentIdx++ - //c := NewJumpDestCache(16) - nextContract := *NewContract( - accounts.ZeroAddress, - accounts.ZeroAddress, - accounts.ZeroAddress, - uint256.Int{}, - //c, - ) - - return st.env.interpreter.Run(nextContract, 0, nil, st.readOnlys[*st.currentIdx]) -} - -func trace(isEVMSlice []bool, readOnlySlice []*readOnlyState) string { - var res strings.Builder - res.WriteString("trace:\n") - for i := 0; i < len(isEVMSlice); i++ { - res.WriteString(fmt.Sprintf("%d: EVM %t, readonly %t\n", i, isEVMSlice[i], readOnlySlice[i].in)) - } - return res.String() -} diff --git a/execution/vm/instructions.go b/execution/vm/instructions.go index 51995a68bda..29fb8bea0c6 100644 --- a/execution/vm/instructions.go +++ b/execution/vm/instructions.go @@ -36,7 +36,7 @@ import ( "github.com/erigontech/erigon/execution/types/accounts" ) -func opAdd(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opAdd(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Add(&x, y) return pc, nil, nil @@ -47,7 +47,7 @@ func stAdd(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", ADD, &x, &y) } -func opSub(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSub(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Sub(&x, y) return pc, nil, nil @@ -58,7 +58,7 @@ func stSub(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", SUB, &x, &y) } -func opMul(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMul(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Mul(&x, y) return pc, nil, nil @@ -69,7 +69,7 @@ func stMul(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", MUL, &x, &y) } -func opDiv(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opDiv(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Div(&x, y) return pc, nil, nil @@ -80,7 +80,7 @@ func stDiv(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", DIV, &x, &y) } -func opSdiv(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSdiv(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.SDiv(&x, y) return pc, nil, nil @@ -91,7 +91,7 @@ func stSdiv(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", SDIV, &x, &y) } -func opMod(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMod(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Mod(&x, y) return pc, nil, nil @@ -102,7 +102,7 @@ func stMod(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", MOD, &x, &y) } -func opSmod(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSmod(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.SMod(&x, y) return pc, nil, nil @@ -113,7 +113,7 @@ func stSmod(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", SMOD, &x, &y) } -func opExp(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opExp(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { base, exponent := scope.Stack.pop(), scope.Stack.peek() switch { case exponent.IsZero(): @@ -142,13 +142,13 @@ func opExp(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, return pc, nil, nil } -func opSignExtend(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSignExtend(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { back, num := scope.Stack.pop(), scope.Stack.peek() num.ExtendSign(num, &back) return pc, nil, nil } -func opNot(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opNot(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x := scope.Stack.peek() x.Not(x) return pc, nil, nil @@ -159,7 +159,7 @@ func stNot(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", NOT.String(), x) } -func opLt(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opLt(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() if x.Lt(y) { y.SetOne() @@ -174,7 +174,7 @@ func stLt(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", LT, &x, &y) } -func opGt(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opGt(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() if x.Gt(y) { y.SetOne() @@ -189,7 +189,7 @@ func stGt(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", GT, &x, &y) } -func opSlt(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSlt(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() if x.Slt(y) { y.SetOne() @@ -204,7 +204,7 @@ func stSlt(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", SLT, &x, &y) } -func opSgt(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSgt(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() if x.Sgt(y) { y.SetOne() @@ -219,7 +219,7 @@ func stSgt(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", SGT, &x, &y) } -func opEq(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opEq(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() if x.Eq(y) { y.SetOne() @@ -234,7 +234,7 @@ func stEq(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", EQ, &x, &y) } -func opIszero(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opIszero(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x := scope.Stack.peek() if x.IsZero() { x.SetOne() @@ -249,7 +249,7 @@ func stIsZero(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", ISZERO, &x) } -func opAnd(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opAnd(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.And(&x, y) return pc, nil, nil @@ -260,7 +260,7 @@ func stAnd(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", AND, &x, &y) } -func opOr(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opOr(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Or(&x, y) return pc, nil, nil @@ -271,7 +271,7 @@ func stOr(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", OR, &x, &y) } -func opXor(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opXor(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y := scope.Stack.pop(), scope.Stack.peek() y.Xor(&x, y) return pc, nil, nil @@ -282,13 +282,13 @@ func stXor(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", XOR, &x, &y) } -func opByte(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opByte(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { th, val := scope.Stack.pop(), scope.Stack.peek() val.Byte(&th) return pc, nil, nil } -func opAddmod(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opAddmod(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() z.AddMod(&x, &y, z) return pc, nil, nil @@ -299,7 +299,7 @@ func stAddmod(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d %d", ADDMOD, &x, &y, &z) } -func opMulmod(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMulmod(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() z.MulMod(&x, &y, z) return pc, nil, nil @@ -313,7 +313,7 @@ func stMulmod(_ uint64, scope *CallContext) string { // opSHL implements Shift Left // The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the left by arg1 number of bits. -func opSHL(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSHL(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.LtUint64(256) { @@ -327,7 +327,7 @@ func opSHL(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, // opSHR implements Logical Shift Right // The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. -func opSHR(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSHR(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.LtUint64(256) { @@ -341,7 +341,7 @@ func opSHR(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, // opSAR implements Arithmetic Shift Right // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. -func opSAR(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSAR(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.GtUint64(255) { if value.Sign() >= 0 { @@ -357,33 +357,34 @@ func opSAR(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, return pc, nil, nil } -func opKeccak256(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opKeccak256(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - if interpreter.hasher == nil { - interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) + if evm.hasher == nil { + evm.hasher = sha3.NewLegacyKeccak256().(keccakState) } else { - interpreter.hasher.Reset() + evm.hasher.Reset() } - interpreter.hasher.Write(data) - if _, err := interpreter.hasher.Read(interpreter.hasherBuf[:]); err != nil { + evm.hasher.Write(data) + if _, err := evm.hasher.Read(evm.hasherBuf[:]); err != nil { panic(err) } - size.SetBytes(interpreter.hasherBuf[:]) + size.SetBytes(evm.hasherBuf[:]) return pc, nil, nil } -func opAddress(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { + +func opAddress(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { addrVal := scope.Contract.Address().Value() scope.Stack.push(*new(uint256.Int).SetBytes(addrVal[:])) return pc, nil, nil } -func opBalance(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opBalance(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { slot := scope.Stack.peek() address := accounts.InternAddress(slot.Bytes20()) - balance, err := interpreter.evm.IntraBlockState().GetBalance(address) + balance, err := evm.IntraBlockState().GetBalance(address) if err != nil { return pc, nil, fmt.Errorf("%w: %w", ErrIntraBlockStateFailed, err) } @@ -391,8 +392,8 @@ func opBalance(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint return pc, nil, nil } -func opOrigin(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if origin := interpreter.evm.Origin; origin.IsNil() { +func opOrigin(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if origin := evm.Origin; origin.IsNil() { scope.Stack.push(uint256.Int{}) } else { originVal := origin.Value() @@ -400,7 +401,7 @@ func opOrigin(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint6 } return pc, nil, nil } -func opCaller(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCaller(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { if caller := scope.Contract.Caller(); caller.IsNil() { scope.Stack.push(uint256.Int{}) } else { @@ -415,12 +416,12 @@ func stCaller(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s (%d)", CALLER, new(uint256.Int).SetBytes(caller[:])) } -func opCallValue(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCallValue(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(scope.Contract.value) return pc, nil, nil } -func opCallDataLoad(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCallDataLoad(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { x := scope.Stack.peek() if offset, overflow := x.Uint64WithOverflow(); !overflow { data := getData(scope.input, offset, 32) @@ -441,7 +442,7 @@ func stCallDataLoad(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d (%x)", CALLDATALOAD, &x, data) } -func opCallDataSize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCallDataSize(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(*new(uint256.Int).SetUint64(uint64(len(scope.input)))) return pc, nil, nil } @@ -450,7 +451,7 @@ func stCallDataSize(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s (%d)", CALLDATASIZE, new(uint256.Int).SetUint64(uint64(len(scope.input)))) } -func opCallDataCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCallDataCopy(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( memOffset = scope.Stack.pop() dataOffset = scope.Stack.pop() @@ -480,12 +481,12 @@ func stCallDataCopy(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d %d (%x)", CALLDATACOPY, memOffset.Uint64(), dataOffset64, length.Uint64(), getData(scope.input, dataOffset64, length.Uint64())) } -func opReturnDataSize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - scope.Stack.push(*new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) +func opReturnDataSize(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + scope.Stack.push(*new(uint256.Int).SetUint64(uint64(len(evm.returnData)))) return pc, nil, nil } -func opReturnDataCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opReturnDataCopy(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( memOffset = scope.Stack.pop() dataOffset = scope.Stack.pop() @@ -504,10 +505,10 @@ func opReturnDataCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext } end64, overflow := end.Uint64WithOverflow() - if overflow || uint64(len(interpreter.returnData)) < end64 { + if overflow || uint64(len(evm.returnData)) < end64 { return pc, nil, ErrReturnDataOutOfBounds } - scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), evm.returnData[offset64:end64]) return pc, nil, nil } @@ -538,10 +539,10 @@ func stReturnDataCopy(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d %d", RETURNDATACOPY, memOffset.Uint64(), offset64, length.Uint64()) } -func opExtCodeSize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opExtCodeSize(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { slot := scope.Stack.peek() addr := accounts.InternAddress(slot.Bytes20()) - codeSize, err := interpreter.evm.IntraBlockState().GetCodeSize(addr) + codeSize, err := evm.IntraBlockState().GetCodeSize(addr) if err != nil { return pc, nil, fmt.Errorf("%w: %w", ErrIntraBlockStateFailed, err) } @@ -549,14 +550,14 @@ func opExtCodeSize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ( return pc, nil, nil } -func opCodeSize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCodeSize(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { l := new(uint256.Int) l.SetUint64(uint64(len(scope.Contract.Code))) scope.Stack.push(*l) return pc, nil, nil } -func opCodeCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCodeCopy(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( memOffset = scope.Stack.pop() codeOffset = scope.Stack.pop() @@ -571,7 +572,7 @@ func opCodeCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin return pc, nil, nil } -func opExtCodeCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opExtCodeCopy(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( stack = &scope.Stack a = stack.pop() @@ -582,7 +583,7 @@ func opExtCodeCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ( addr := accounts.InternAddress(a.Bytes20()) len64 := length.Uint64() - code, err := interpreter.evm.IntraBlockState().GetCode(addr) + code, err := evm.IntraBlockState().GetCode(addr) if err != nil { return pc, nil, fmt.Errorf("%w: %w", ErrIntraBlockStateFailed, err) } @@ -629,11 +630,11 @@ func opExtCodeCopy(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ( // (7) Caller tries to get the code hash of a delegated account, the result should be // // equal the result of calling extcodehash on the account directly. -func opExtCodeHash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opExtCodeHash(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { slot := scope.Stack.peek() address := accounts.InternAddress(slot.Bytes20()) - empty, err := interpreter.evm.IntraBlockState().Empty(address) + empty, err := evm.IntraBlockState().Empty(address) if err != nil { return pc, nil, err } @@ -641,7 +642,7 @@ func opExtCodeHash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ( slot.Clear() } else { var codeHash accounts.CodeHash - codeHash, err = interpreter.evm.IntraBlockState().GetCodeHash(address) + codeHash, err = evm.IntraBlockState().GetCodeHash(address) if err != nil { return pc, nil, fmt.Errorf("%w: %w", ErrIntraBlockStateFailed, err) } @@ -651,13 +652,13 @@ func opExtCodeHash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ( return pc, nil, nil } -func opGasprice(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - scope.Stack.push(interpreter.evm.GasPrice) +func opGasprice(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + scope.Stack.push(evm.GasPrice) return pc, nil, nil } // opBlockhash executes the BLOCKHASH opcode -func opBlockhash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opBlockhash(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { arg := scope.Stack.peek() arg64, overflow := arg.Uint64WithOverflow() if overflow { @@ -665,14 +666,14 @@ func opBlockhash(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (ui return pc, nil, nil } var upper, lower uint64 - upper = interpreter.evm.Context.BlockNumber + upper = evm.Context.BlockNumber if upper <= params.BlockHashOldWindow { lower = 0 } else { lower = upper - params.BlockHashOldWindow } if arg64 >= lower && arg64 < upper { - hash, err := interpreter.evm.Context.GetHash(arg64) + hash, err := evm.Context.GetHash(arg64) if err != nil { arg.Clear() return pc, nil, err @@ -690,8 +691,8 @@ func stBlockhash(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", BLOCKHASH, &x) } -func opCoinbase(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if coinbase := interpreter.evm.Context.Coinbase; coinbase.IsNil() { +func opCoinbase(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if coinbase := evm.Context.Coinbase; coinbase.IsNil() { scope.Stack.push(uint256.Int{}) } else { coinbaseValue := coinbase.Value() @@ -700,49 +701,49 @@ func opCoinbase(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin return pc, nil, nil } -func opTimestamp(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - v := new(uint256.Int).SetUint64(interpreter.evm.Context.Time) +func opTimestamp(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + v := new(uint256.Int).SetUint64(evm.Context.Time) scope.Stack.push(*v) return pc, nil, nil } -func opNumber(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - v := new(uint256.Int).SetUint64(interpreter.evm.Context.BlockNumber) +func opNumber(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + v := new(uint256.Int).SetUint64(evm.Context.BlockNumber) scope.Stack.push(*v) return pc, nil, nil } -func opDifficulty(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opDifficulty(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var v *uint256.Int - if interpreter.evm.Context.PrevRanDao != nil { + if evm.Context.PrevRanDao != nil { // EIP-4399: Supplant DIFFICULTY opcode with PREVRANDAO - v = new(uint256.Int).SetBytes(interpreter.evm.Context.PrevRanDao.Bytes()) + v = new(uint256.Int).SetBytes(evm.Context.PrevRanDao.Bytes()) } else { var overflow bool - v, overflow = uint256.FromBig(interpreter.evm.Context.Difficulty) + v, overflow = uint256.FromBig(evm.Context.Difficulty) if overflow { - return pc, nil, errors.New("interpreter.evm.Context.Difficulty higher than 2^256-1") + return pc, nil, errors.New("evm.Context.Difficulty higher than 2^256-1") } } scope.Stack.push(*v) return pc, nil, nil } -func opGasLimit(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.evm.Context.MaxGasLimit { +func opGasLimit(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.Context.MaxGasLimit { scope.Stack.push(*new(uint256.Int).SetAllOne()) } else { - scope.Stack.push(*new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) + scope.Stack.push(*new(uint256.Int).SetUint64(evm.Context.GasLimit)) } return pc, nil, nil } -func opPop(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opPop(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.pop() return pc, nil, nil } -func opMload(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMload(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { v := scope.Stack.peek() offset := v.Uint64() v.SetBytes(scope.Memory.GetPtr(offset, 32)) @@ -755,7 +756,7 @@ func stMload(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d (%d)", MLOAD, offset, (&uint256.Int{}).SetBytes(scope.Memory.GetPtr(offset, 32))) } -func opMstore(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMstore(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { mStart, val := scope.Stack.pop(), scope.Stack.pop() scope.Memory.Set32(mStart.Uint64(), &val) return pc, nil, nil @@ -766,15 +767,15 @@ func stMstore(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d", MSTORE, mStart.Uint64(), &val) } -func opMstore8(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMstore8(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { off, val := scope.Stack.pop(), scope.Stack.pop() scope.Memory.store[off.Uint64()] = byte(val.Uint64()) return pc, nil, nil } -func opSload(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (_ uint64, _ []byte, err error) { +func opSload(pc uint64, evm *EVM, scope *CallContext) (_ uint64, _ []byte, err error) { loc := scope.Stack.peek() - *loc, err = interpreter.evm.IntraBlockState().GetState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32())) + *loc, err = evm.IntraBlockState().GetState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32())) return pc, nil, err } @@ -783,13 +784,13 @@ func stSload(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x", SLOAD, loc) } -func opSstore(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opSstore(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } loc := scope.Stack.pop() val := scope.Stack.pop() - return pc, nil, interpreter.evm.IntraBlockState().SetState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32()), val) + return pc, nil, evm.IntraBlockState().SetState(scope.Contract.Address(), accounts.InternKey(loc.Bytes32()), val) } func stSstore(_ uint64, scope *CallContext) string { @@ -797,19 +798,19 @@ func stSstore(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x %d", SSTORE, loc.Bytes32(), &val) } -func opJump(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opJump(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { pos := scope.Stack.pop() if valid, usedBitmap := scope.Contract.validJumpdest(pos); !valid { if usedBitmap { - if interpreter.cfg.TraceJumpDest { + if evm.config.TraceJumpDest { log.Debug("Code Bitmap used for detecting invalid jump", - "tx", fmt.Sprintf("0x%x", interpreter.evm.TxHash), - "block_num", interpreter.evm.Context.BlockNumber, + "tx", fmt.Sprintf("0x%x", evm.TxHash), + "block_num", evm.Context.BlockNumber, ) } else { // This is "cheaper" version because it does not require calculation of txHash for each transaction log.Debug("Code Bitmap used for detecting invalid jump", - "block_num", interpreter.evm.Context.BlockNumber, + "block_num", evm.Context.BlockNumber, ) } } @@ -824,20 +825,20 @@ func stJump(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", JUMP, pos) } -func opJumpi(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opJumpi(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { pos, cond := scope.Stack.pop(), scope.Stack.pop() if !cond.IsZero() { if valid, usedBitmap := scope.Contract.validJumpdest(pos); !valid { if usedBitmap { - if interpreter.cfg.TraceJumpDest { + if evm.config.TraceJumpDest { log.Warn("Code Bitmap used for detecting invalid jump", - "tx", fmt.Sprintf("0x%x", interpreter.evm.TxHash), - "block_num", interpreter.evm.Context.BlockNumber, + "tx", fmt.Sprintf("0x%x", evm.TxHash), + "block_num", evm.Context.BlockNumber, ) } else { // This is "cheaper" version because it does not require calculation of txHash for each transaction log.Warn("Code Bitmap used for detecting invalid jump", - "block_num", interpreter.evm.Context.BlockNumber, + "block_num", evm.Context.BlockNumber, ) } } @@ -853,11 +854,11 @@ func stJumpi(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %v %d", JUMPI, !cond.IsZero(), &pos) } -func opJumpdest(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opJumpdest(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { return pc, nil, nil } -func opPc(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opPc(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(*new(uint256.Int).SetUint64(pc)) return pc, nil, nil } @@ -866,12 +867,12 @@ func stPc(pc uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", PC, pc) } -func opMsize(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opMsize(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(*new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) return pc, nil, nil } -func opGas(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opGas(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.push(*new(uint256.Int).SetUint64(scope.gas)) return pc, nil, nil } @@ -880,88 +881,88 @@ func stGas(pc uint64, scope *CallContext) string { return fmt.Sprintf("%s %d", GAS, scope.gas) } -func opSwap1(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap1(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(1) return pc, nil, nil } -func opSwap2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap2(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(2) return pc, nil, nil } -func opSwap3(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap3(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(3) return pc, nil, nil } -func opSwap4(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap4(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(4) return pc, nil, nil } -func opSwap5(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap5(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(5) return pc, nil, nil } -func opSwap6(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap6(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(6) return pc, nil, nil } -func opSwap7(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap7(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(7) return pc, nil, nil } -func opSwap8(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap8(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(8) return pc, nil, nil } -func opSwap9(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap9(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(9) return pc, nil, nil } -func opSwap10(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap10(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(10) return pc, nil, nil } -func opSwap11(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap11(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(11) return pc, nil, nil } -func opSwap12(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap12(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(12) return pc, nil, nil } -func opSwap13(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap13(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(13) return pc, nil, nil } -func opSwap14(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap14(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(14) return pc, nil, nil } -func opSwap15(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap15(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(15) return pc, nil, nil } -func opSwap16(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwap16(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.swap(16) return pc, nil, nil } -func opCreate(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opCreate(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } var ( @@ -971,21 +972,21 @@ func opCreate(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint6 input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) gas = scope.gas ) - if interpreter.evm.ChainRules().IsTangerineWhistle { + if evm.ChainRules().IsTangerineWhistle { gas -= gas / 64 } // reuse size int for stackvalue stackvalue := size - scope.useGas(gas, interpreter.evm.Config().Tracer, tracing.GasChangeCallContractCreation) + scope.useGas(gas, evm.Config().Tracer, tracing.GasChangeCallContractCreation) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, value, false) + res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, gas, value, false) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if interpreter.evm.ChainRules().IsHomestead && suberr == ErrCodeStoreOutOfGas { + if evm.ChainRules().IsHomestead && suberr == ErrCodeStoreOutOfGas { stackvalue.Clear() } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { stackvalue.Clear() @@ -994,13 +995,13 @@ func opCreate(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint6 stackvalue.SetBytes(addrVal[:]) } - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { - interpreter.returnData = res // set REVERT data to return data buffer + evm.returnData = res // set REVERT data to return data buffer return pc, res, nil } - interpreter.returnData = nil // clear dirty return data buffer + evm.returnData = nil // clear dirty return data buffer return pc, nil, nil } @@ -1016,8 +1017,8 @@ func stCreate(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %x %d", CREATE.String(), &value, input, &scope.gas) } -func opCreate2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opCreate2(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } var ( @@ -1030,10 +1031,10 @@ func opCreate2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint // Apply EIP150 gas -= gas / 64 - scope.useGas(gas, interpreter.evm.Config().Tracer, tracing.GasChangeCallContractCreation2) + scope.useGas(gas, evm.Config().Tracer, tracing.GasChangeCallContractCreation2) // reuse size int for stackvalue stackValue := size - res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract.Address(), input, gas, endowment, &salt, false) + res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, gas, endowment, &salt, false) // Push item on the stack based on the returned error. if suberr != nil { @@ -1044,13 +1045,13 @@ func opCreate2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint } scope.Stack.push(stackValue) - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { - interpreter.returnData = res // set REVERT data to return data buffer + evm.returnData = res // set REVERT data to return data buffer return pc, res, nil } - interpreter.returnData = nil // clear dirty return data buffer + evm.returnData = nil // clear dirty return data buffer return pc, nil, nil } @@ -1066,12 +1067,12 @@ func stCreate2(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %d %d %x %d", CREATE2.String(), &endowment, &salt, input, &scope.gas) } -func opCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opCall(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { stack := &scope.Stack - // Pop gas. The actual gas in interpreter.evm.callGasTemp. + // Pop gas. The actual gas in evm.callGasTemp. // We can use this as a temporary value temp := stack.pop() - gas := interpreter.evm.CallGasTemp() + gas := evm.CallGasTemp() // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := accounts.InternAddress(addr.Bytes20()) @@ -1079,13 +1080,13 @@ func opCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) if !value.IsZero() { - if interpreter.readOnly { + if evm.readOnly { return pc, nil, ErrWriteProtection } gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(scope.Contract.Address(), toAddr, args, gas, value, false /* bailout */) + ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, gas, value, false /* bailout */) if err != nil { temp.Clear() @@ -1098,9 +1099,9 @@ func opCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) - interpreter.returnData = ret + evm.returnData = ret return pc, ret, nil } @@ -1114,12 +1115,12 @@ func stCall(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x %x", CALL.String(), toAddr, args) } -func opCallCode(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. +func opCallCode(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + // Pop gas. The actual gas is in evm.callGasTemp. stack := &scope.Stack // We use it as a temporary value temp := stack.pop() - gas := interpreter.evm.CallGasTemp() + gas := evm.CallGasTemp() // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := accounts.InternAddress(addr.Bytes20()) @@ -1130,7 +1131,7 @@ func opCallCode(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract.Address(), toAddr, args, gas, value) + ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, gas, value) if err != nil { temp.Clear() } else { @@ -1142,9 +1143,9 @@ func opCallCode(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) - interpreter.returnData = ret + evm.returnData = ret return pc, ret, nil } @@ -1158,19 +1159,19 @@ func stCallCode(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x %x", CALLCODE.String(), toAddr, args) } -func opDelegateCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opDelegateCall(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { stack := &scope.Stack - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + // Pop gas. The actual gas is in evm.callGasTemp. // We use it as a temporary value temp := stack.pop() - gas := interpreter.evm.CallGasTemp() + gas := evm.CallGasTemp() // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := accounts.InternAddress(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) - ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract.addr, scope.Contract.caller, toAddr, args, scope.Contract.value, gas) + ret, returnGas, err := evm.DelegateCall(scope.Contract.addr, scope.Contract.caller, toAddr, args, scope.Contract.value, gas) if err != nil { temp.Clear() } else { @@ -1182,9 +1183,9 @@ func opDelegateCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) - interpreter.returnData = ret + evm.returnData = ret return pc, ret, nil } @@ -1198,19 +1199,19 @@ func stDelegateCall(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x %x", DELEGATECALL.String(), toAddr, args) } -func opStaticCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. +func opStaticCall(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + // Pop gas. The actual gas is in evm.callGasTemp. stack := &scope.Stack // We use it as a temporary value temp := stack.pop() - gas := interpreter.evm.CallGasTemp() + gas := evm.CallGasTemp() // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := accounts.InternAddress(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) - ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract.Address(), toAddr, args, gas) + ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, gas) if err != nil { temp.Clear() } else { @@ -1221,9 +1222,9 @@ func opStaticCall(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (u scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - scope.refundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) + scope.refundGas(returnGas, evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded) - interpreter.returnData = ret + evm.returnData = ret return pc, ret, nil } @@ -1237,35 +1238,35 @@ func stStaticCall(_ uint64, scope *CallContext) string { return fmt.Sprintf("%s %x %x", STATICCALL.String(), toAddr, args) } -func opReturn(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opReturn(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { offset, size := scope.Stack.pop(), scope.Stack.pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) return pc, ret, errStopToken } -func opRevert(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opRevert(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { offset, size := scope.Stack.pop(), scope.Stack.pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) - interpreter.returnData = ret + evm.returnData = ret return pc, ret, ErrExecutionReverted } -func opUndefined(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opUndefined(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { return pc, nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.Code[pc])} } -func opStop(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opStop(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { return pc, nil, errStopToken } -func opSelfdestruct(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opSelfdestruct(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } beneficiary := scope.Stack.pop() self := scope.Contract.Address() beneficiaryAddr := accounts.InternAddress(beneficiary.Bytes20()) - ibs := interpreter.evm.IntraBlockState() + ibs := evm.IntraBlockState() balance, err := ibs.GetBalance(self) if err != nil { return pc, nil, err @@ -1273,23 +1274,24 @@ func opSelfdestruct(pc uint64, interpreter *EVMInterpreter, scope *CallContext) ibs.AddBalance(beneficiaryAddr, balance, tracing.BalanceIncreaseSelfdestruct) ibs.Selfdestruct(self) - if interpreter.evm.Config().Tracer != nil && interpreter.evm.Config().Tracer.OnEnter != nil { - interpreter.evm.Config().Tracer.OnEnter(interpreter.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiaryAddr, false, []byte{}, 0, balance, nil) + tracer := evm.Config().Tracer + if tracer != nil && tracer.OnEnter != nil { + tracer.OnEnter(evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiaryAddr, false, []byte{}, 0, balance, nil) } - if interpreter.evm.Config().Tracer != nil && interpreter.evm.Config().Tracer.OnExit != nil { - interpreter.evm.Config().Tracer.OnExit(interpreter.depth, []byte{}, 0, nil, false) + if tracer != nil && tracer.OnExit != nil { + tracer.OnExit(evm.depth, []byte{}, 0, nil, false) } return pc, nil, errStopToken } -func opSelfdestruct6780(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { +func opSelfdestruct6780(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } beneficiary := scope.Stack.pop() self := scope.Contract.Address() beneficiaryAddr := accounts.InternAddress(beneficiary.Bytes20()) - ibs := interpreter.evm.IntraBlockState() + ibs := evm.IntraBlockState() balance, err := ibs.GetBalance(self) if err != nil { return pc, nil, err @@ -1311,18 +1313,19 @@ func opSelfdestruct6780(pc uint64, interpreter *EVMInterpreter, scope *CallConte ibs.SubBalance(self, balance, tracing.BalanceDecreaseSelfdestruct) ibs.AddBalance(beneficiaryAddr, balance, tracing.BalanceIncreaseSelfdestruct) } - if interpreter.evm.ChainRules().IsAmsterdam && !balance.IsZero() { // EIP-7708 + if evm.ChainRules().IsAmsterdam && !balance.IsZero() { // EIP-7708 if self != beneficiaryAddr { ibs.AddLog(misc.EthTransferLog(self.Value(), beneficiaryAddr.Value(), balance)) } else if newContract { ibs.AddLog(misc.EthTransferLog(self.Value(), common.Address{}, balance)) } } - if interpreter.evm.Config().Tracer != nil && interpreter.evm.Config().Tracer.OnEnter != nil { - interpreter.cfg.Tracer.OnEnter(interpreter.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiaryAddr, false, []byte{}, 0, balance, nil) + tracer := evm.Config().Tracer + if tracer != nil && tracer.OnEnter != nil { + tracer.OnEnter(evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiaryAddr, false, []byte{}, 0, balance, nil) } - if interpreter.evm.Config().Tracer != nil && interpreter.evm.Config().Tracer.OnExit != nil { - interpreter.cfg.Tracer.OnExit(interpreter.depth, []byte{}, 0, nil, false) + if tracer != nil && tracer.OnExit != nil { + tracer.OnExit(evm.depth, []byte{}, 0, nil, false) } return pc, nil, errStopToken } @@ -1348,7 +1351,7 @@ func decodePair(x byte) (int, int) { return r + 1, 29 - q } -func opDupN(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opDupN(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { code := scope.Contract.Code pc++ x := byte(0) // see https://github.com/ethereum/EIPs/pull/11085 @@ -1372,7 +1375,7 @@ func opDupN(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, return pc, nil, nil } -func opSwapN(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opSwapN(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { code := scope.Contract.Code pc++ x := byte(0) // see https://github.com/ethereum/EIPs/pull/11085 @@ -1396,7 +1399,7 @@ func opSwapN(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64 return pc, nil, nil } -func opExchange(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opExchange(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { code := scope.Contract.Code pc++ x := byte(0) // see https://github.com/ethereum/EIPs/pull/11085 @@ -1429,8 +1432,8 @@ func opExchange(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uin // make log instruction function func makeLog(size int) executionFunc { - return func(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { - if interpreter.readOnly { + return func(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { + if evm.readOnly { return pc, nil, ErrWriteProtection } topics := make([]common.Hash, size) @@ -1442,13 +1445,13 @@ func makeLog(size int) executionFunc { } d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64()) - interpreter.evm.IntraBlockState().AddLog(&types.Log{ + evm.IntraBlockState().AddLog(&types.Log{ Address: scope.Contract.Address().Value(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // execution/state doesn't know the current block number. - BlockNumber: interpreter.evm.Context.BlockNumber, + BlockNumber: evm.Context.BlockNumber, }) return pc, nil, nil @@ -1456,7 +1459,7 @@ func makeLog(size int) executionFunc { } // opPush1 is a specialized version of pushN -func opPush1(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opPush1(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( codeLen = uint64(len(scope.Contract.Code)) integer = new(uint256.Int) @@ -1484,7 +1487,7 @@ func stPush1(pc uint64, scope *CallContext) string { } // opPush2 is a specialized version of pushN -func opPush2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { +func opPush2(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { var ( codeLen = uint64(len(scope.Contract.Code)) integer = new(uint256.Int) @@ -1502,7 +1505,7 @@ func opPush2(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64 // make push instruction function func makePush(size uint64, pushByteSize int) executionFunc { - return func(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { + return func(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { codeLen := len(scope.Contract.Code) startMin := min(int(pc+1), codeLen) @@ -1535,7 +1538,7 @@ func makePushStringer(size uint64, pushByteSize int) stringer { // make dup instruction function func makeDup(size int) executionFunc { - return func(pc uint64, interpreter *EVMInterpreter, scope *CallContext) (uint64, []byte, error) { + return func(pc uint64, evm *EVM, scope *CallContext) (uint64, []byte, error) { scope.Stack.dup(size) return pc, nil, nil } diff --git a/execution/vm/instructions_test.go b/execution/vm/instructions_test.go index eee1a5cec5f..c72ae43fdea 100644 --- a/execution/vm/instructions_test.go +++ b/execution/vm/instructions_test.go @@ -111,10 +111,9 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - pc = uint64(0) - evmInterpreter = env.interpreter.(*EVMInterpreter) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} + pc = uint64(0) ) for i, test := range tests { @@ -123,7 +122,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) callContext.Stack.push(x) callContext.Stack.push(y) - opFn(pc, evmInterpreter, callContext) + opFn(pc, evm, callContext) if len(callContext.Stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(callContext.Stack.data)) } @@ -215,10 +214,9 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { t.Parallel() var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) - pc = uint64(0) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} + pc = uint64(0) ) tests := []struct { x string @@ -243,7 +241,7 @@ func TestAddMod(t *testing.T) { callContext.Stack.push(z) callContext.Stack.push(y) callContext.Stack.push(x) - opAddmod(pc, evmInterpreter, callContext) + opAddmod(pc, evm, callContext) actual := callContext.Stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -254,10 +252,9 @@ 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, chain.TestChainConfig, Config{}) +// evm = NewEVM(BlockContext{}, TxContext{}, nil, chain.TestChainConfig, Config{}) // stack = stack.New() // pc = uint64(0) -// interpreter = env.interpreter.(*EVMInterpreter) // ) // result := make([]TwoOperandTestcase, len(args)) // for i, param := range args { @@ -265,7 +262,7 @@ func TestAddMod(t *testing.T) { // y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) // stack.push(x) // stack.push(y) -// opFn(&pc, interpreter, &callCtx{nil, stack, nil}) +// opFn(&pc, evm, &callCtx{nil, stack, nil}) // actual := stack.pop() // result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} // } @@ -303,12 +300,10 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(b *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} ) - env.interpreter = evmInterpreter // convert args byteArgs := make([][]byte, len(args)) for i, arg := range args { @@ -321,7 +316,7 @@ func opBenchmark(b *testing.B, op executionFunc, args ...string) { a := *new(uint256.Int).SetBytes(arg) callContext.Stack.push(a) } - op(pc, evmInterpreter, callContext) + op(pc, evm, callContext) callContext.Stack.pop() } } @@ -537,24 +532,22 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { t.Parallel() var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} ) - env.interpreter = evmInterpreter callContext.Memory.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" callContext.Stack.push(*new(uint256.Int).SetBytes(common.Hex2Bytes(v))) callContext.Stack.push(*new(uint256.Int)) - opMstore(pc, evmInterpreter, callContext) + opMstore(pc, evm, callContext) if got := common.Bytes2Hex(callContext.Memory.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } callContext.Stack.push(*new(uint256.Int).SetOne()) callContext.Stack.push(*new(uint256.Int)) - opMstore(pc, evmInterpreter, callContext) + opMstore(pc, evm, callContext) if common.Bytes2Hex(callContext.Memory.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -562,12 +555,10 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} ) - env.interpreter = evmInterpreter callContext.Memory.Resize(64) pc := uint64(0) memStart := *new(uint256.Int) @@ -576,36 +567,34 @@ func BenchmarkOpMstore(bench *testing.B) { for bench.Loop() { callContext.Stack.push(value) callContext.Stack.push(memStart) - opMstore(pc, evmInterpreter, callContext) + opMstore(pc, evm, callContext) } } func TestOpTstore(t *testing.T) { t.Parallel() var ( - state = state.New(nil) - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, state, chain.TestChainConfig, Config{}) - evmInterpreter = NewEVMInterpreter(env, env.Config()) - caller = accounts.ZeroAddress - to = accounts.InternAddress(common.Address{1}) - callContext = &CallContext{Contract: *NewContract(caller, caller, to, uint256.Int{})} - value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") + state = state.New(nil) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, state, chain.TestChainConfig, Config{}) + caller = accounts.ZeroAddress + to = accounts.InternAddress(common.Address{1}) + callContext = &CallContext{Contract: *NewContract(caller, caller, to, uint256.Int{})} + value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) - env.interpreter = evmInterpreter pc := uint64(0) // push the value to the stack callContext.Stack.push(*new(uint256.Int).SetBytes(value)) // push the location to the stack callContext.Stack.push(*new(uint256.Int)) - opTstore(pc, evmInterpreter, callContext) + opTstore(pc, evm, callContext) // there should be no elements on the stack after TSTORE if callContext.Stack.len() != 0 { t.Fatal("stack wrong size") } // push the location to the stack callContext.Stack.push(*new(uint256.Int)) - opTload(pc, evmInterpreter, callContext) + opTload(pc, evm, callContext) // there should be one element on the stack after TLOAD if callContext.Stack.len() != 1 { t.Fatal("stack wrong size") @@ -618,11 +607,9 @@ func TestOpTstore(t *testing.T) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} ) - env.interpreter = evmInterpreter callContext.Memory.Resize(32) pc := uint64(0) start := uint256.Int{} @@ -630,7 +617,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { for bench.Loop() { callContext.Stack.push(*uint256.NewInt(32)) callContext.Stack.push(start) - opKeccak256(pc, evmInterpreter, callContext) + opKeccak256(pc, evm, callContext) } } @@ -792,10 +779,9 @@ func TestOpMCopy(t *testing.T) { }, } { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - pc = uint64(0) - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} + pc = uint64(0) ) data := common.FromHex(strings.ReplaceAll(tc.pre, " ", "")) // Set pre @@ -825,7 +811,7 @@ func TestOpMCopy(t *testing.T) { } // and the dynamic cost var haveGas uint64 - if dynamicCost, err := gasMcopy(env, callContext, 0, memorySize); err != nil { + if dynamicCost, err := gasMcopy(evm, callContext, 0, memorySize); err != nil { t.Error(err) } else { haveGas = GasFastestStep + dynamicCost @@ -835,7 +821,7 @@ func TestOpMCopy(t *testing.T) { callContext.Memory.Resize(memorySize) } // Do the copy - opMcopy(pc, evmInterpreter, callContext) + opMcopy(pc, evm, callContext) want := common.FromHex(strings.ReplaceAll(tc.want, " ", "")) if have := callContext.Memory.store; !bytes.Equal(want, have) { t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have) @@ -862,9 +848,8 @@ func TestOpCLZ(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var ( - env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - callContext = &CallContext{} - evmInterpreter = NewEVMInterpreter(env, env.Config()) + evm = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + callContext = &CallContext{} ) pc := uint64(0) @@ -877,7 +862,7 @@ func TestOpCLZ(t *testing.T) { } callContext.Stack.push(*val) - opCLZ(pc, evmInterpreter, callContext) + opCLZ(pc, evm, callContext) if gotLen := callContext.Stack.len(); gotLen != 1 { t.Fatalf("stack length = %d; want 1", gotLen) @@ -897,10 +882,9 @@ func TestPush(t *testing.T) { code := common.FromHex("0011223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121") push32 := makePush(32, 32) - env := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) + evm := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) callContext := &CallContext{} callContext.Contract.Code = code - evmInterpreter := NewEVMInterpreter(env, env.Config()) for i, want := range []string{ "0x11223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1", @@ -951,7 +935,7 @@ func TestPush(t *testing.T) { "0x0", } { pc := uint64(i) - push32(pc, evmInterpreter, callContext) + push32(pc, evm, callContext) res := callContext.Stack.pop() if have := res.Hex(); have != want { t.Fatalf("case %d, have %v want %v", i, have, want) @@ -960,8 +944,7 @@ func TestPush(t *testing.T) { } func TestEIP8024_Execution(t *testing.T) { - env := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) - evmInterpreter := NewEVMInterpreter(env, env.Config()) + evm := NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chain.TestChainConfig, Config{}) tests := []struct { name string @@ -1078,22 +1061,22 @@ func TestEIP8024_Execution(t *testing.T) { case 0x00: return case 0x60: - pc, _, err = opPush1(pc, evmInterpreter, callContext) + pc, _, err = opPush1(pc, evm, callContext) case 0x80: dup1 := makeDup(1) - pc, _, err = dup1(pc, evmInterpreter, callContext) + pc, _, err = dup1(pc, evm, callContext) case 0x56: - pc, _, err = opJump(pc, evmInterpreter, callContext) + pc, _, err = opJump(pc, evm, callContext) case 0x5b: - pc, _, err = opJumpdest(pc, evmInterpreter, callContext) + pc, _, err = opJumpdest(pc, evm, callContext) case 0xe6: - pc, _, err = opDupN(pc, evmInterpreter, callContext) + pc, _, err = opDupN(pc, evm, callContext) case 0x15: - pc, _, err = opIszero(pc, evmInterpreter, callContext) + pc, _, err = opIszero(pc, evm, callContext) case 0xe7: - pc, _, err = opSwapN(pc, evmInterpreter, callContext) + pc, _, err = opSwapN(pc, evm, callContext) case 0xe8: - pc, _, err = opExchange(pc, evmInterpreter, callContext) + pc, _, err = opExchange(pc, evm, callContext) default: err = &ErrInvalidOpCode{opcode: OpCode(op)} } diff --git a/execution/vm/interpreter.go b/execution/vm/interpreter.go index c07ddc16516..c6794fc4774 100644 --- a/execution/vm/interpreter.go +++ b/execution/vm/interpreter.go @@ -28,7 +28,6 @@ import ( "github.com/holiman/uint256" - "github.com/erigontech/erigon/common" "github.com/erigontech/erigon/common/dbg" "github.com/erigontech/erigon/common/log/v3" "github.com/erigontech/erigon/common/math" @@ -55,19 +54,6 @@ func (vmConfig *Config) HasEip3860(rules *chain.Rules) bool { return slices.Contains(vmConfig.ExtraEips, 3860) || rules.IsShanghai } -// 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, gas uint64, input []byte, static bool) ([]byte, uint64, error) - Depth() int // `Depth` returns the current call stack's depth. - IncDepth() // Increments the current call stack's depth. - DecDepth() // Decrements the current call stack's depth -} - // CallContext contains the things that are per-call, such as stack and memory, // but not transients like pc and gas type CallContext struct { @@ -193,34 +179,6 @@ type keccakState interface { Read([]byte) (int, error) } -// structcheck doesn't see embedding -// -//nolint:structcheck -type VM struct { - evm *EVM - cfg Config - - hasher keccakState // Keccak256 hasher instance shared across opcodes - hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes - - readOnly bool // Whether to throw on stateful modifications - returnData []byte // Last CALL's return data for subsequent reuse -} - -func (vm *VM) setReadonly(outerReadonly bool) func() { - if outerReadonly && !vm.readOnly { - vm.readOnly = true - return func() { - vm.readOnly = false - } - } - return func() {} -} - -func (vm *VM) getReadonly() bool { - return vm.readOnly -} - func copyJumpTable(jt *JumpTable) *JumpTable { var copy JumpTable for i, op := range jt { @@ -232,46 +190,38 @@ func copyJumpTable(jt *JumpTable) *JumpTable { return © } -// EVMInterpreter represents an EVM interpreter -type EVMInterpreter struct { - *VM - jt *JumpTable // EVM instruction table - depth int -} - -// NewEVMInterpreter returns a new instance of the Interpreter. -func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { +func jumpTable(chainRules *chain.Rules, cfg Config) *JumpTable { var jt *JumpTable switch { - case evm.chainRules.IsAmsterdam: + case chainRules.IsAmsterdam: jt = &amsterdamInstructionSet - case evm.chainRules.IsOsaka: + case chainRules.IsOsaka: jt = &osakaInstructionSet - case evm.ChainRules().IsBhilai: + case chainRules.IsBhilai: jt = &bhilaiInstructionSet - case evm.ChainRules().IsPrague: + case chainRules.IsPrague: jt = &pragueInstructionSet - case evm.ChainRules().IsCancun: + case chainRules.IsCancun: jt = &cancunInstructionSet - case evm.ChainRules().IsNapoli: + case chainRules.IsNapoli: jt = &napoliInstructionSet - case evm.ChainRules().IsShanghai: + case chainRules.IsShanghai: jt = &shanghaiInstructionSet - case evm.ChainRules().IsLondon: + case chainRules.IsLondon: jt = &londonInstructionSet - case evm.ChainRules().IsBerlin: + case chainRules.IsBerlin: jt = &berlinInstructionSet - case evm.ChainRules().IsIstanbul: + case chainRules.IsIstanbul: jt = &istanbulInstructionSet - case evm.ChainRules().IsConstantinople: + case chainRules.IsConstantinople: jt = &constantinopleInstructionSet - case evm.ChainRules().IsByzantium: + case chainRules.IsByzantium: jt = &byzantiumInstructionSet - case evm.ChainRules().IsSpuriousDragon: + case chainRules.IsSpuriousDragon: jt = &spuriousDragonInstructionSet - case evm.ChainRules().IsTangerineWhistle: + case chainRules.IsTangerineWhistle: jt = &tangerineWhistleInstructionSet - case evm.ChainRules().IsHomestead: + case chainRules.IsHomestead: jt = &homesteadInstructionSet default: jt = &frontierInstructionSet @@ -287,13 +237,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { } } - return &EVMInterpreter{ - VM: &VM{ - evm: evm, - cfg: cfg, - }, - jt: jt, - } + return jt } // Run loops and evaluates the contract's code with the given input data and returns @@ -302,7 +246,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // 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, gas uint64, input []byte, readOnly bool) (_ []byte, _ uint64, err error) { +func (evm *EVM) Run(contract Contract, gas uint64, input []byte, readOnly bool) (_ []byte, _ uint64, err error) { // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { return nil, gas, nil @@ -310,7 +254,7 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO // 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 + evm.returnData = nil var ( op OpCode // current opcode @@ -326,36 +270,37 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO callGas uint64 logged bool // deferred Tracer should ignore already logged steps res []byte // result of the opcode execution function - debug = in.cfg.Tracer != nil && (in.cfg.Tracer.OnOpcode != nil || in.cfg.Tracer.OnGasChange != nil || in.cfg.Tracer.OnFault != nil) - trace = dbg.TraceInstructions && in.evm.intraBlockState.Trace() + tracer = evm.config.Tracer + debug = tracer != nil && (tracer.OnOpcode != nil || tracer.OnGasChange != nil || tracer.OnFault != nil) + trace = dbg.TraceInstructions && evm.intraBlockState.Trace() blockNum uint64 txIndex, txIncarnation int ) // 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. - restoreReadonly := readOnly && !in.readOnly + restoreReadonly := readOnly && !evm.readOnly if restoreReadonly { - in.readOnly = true + evm.readOnly = true } // Increment the call depth which is restricted to 1024 - in.depth++ + evm.depth++ defer func() { // first: capture data/memory/state/depth/etc... then clenup them if debug && err != nil { - if !logged && in.cfg.Tracer.OnOpcode != nil { - in.cfg.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) + if !logged && tracer.OnOpcode != nil { + tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err)) } - if logged && in.cfg.Tracer.OnFault != nil { - in.cfg.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.depth, VMErrorFromErr(err)) + if logged && tracer.OnFault != nil { + tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, evm.depth, VMErrorFromErr(err)) } } // this function must execute _after_: the `CaptureState` needs the stacks before callContext.put() if restoreReadonly { - in.readOnly = false + evm.readOnly = false } - in.depth-- + evm.depth-- }() // The Interpreter main run loop (contextual). This loop runs until either an @@ -375,18 +320,18 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO for { steps++ - if steps%5000 == 0 && in.evm.Cancelled() { + if steps%5000 == 0 && evm.Cancelled() { break } if dbg.TraceDyanmicGas || debug || trace { // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, callContext.gas - blockNum, txIndex, txIncarnation = in.evm.intraBlockState.BlockNumber(), in.evm.intraBlockState.TxIndex(), in.evm.intraBlockState.Incarnation() + blockNum, txIndex, txIncarnation = evm.intraBlockState.BlockNumber(), evm.intraBlockState.TxIndex(), evm.intraBlockState.Incarnation() } // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. op = contract.GetOp(pc) - operation := in.jt[op] + operation := evm.jt[op] cost = operation.constantGas // For tracing // Validate stack if sLen := callContext.Stack.len(); sLen < operation.numPop { @@ -422,12 +367,12 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO // Consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method can get the proper cost var dynamicCost uint64 - dynamicCost, err = operation.dynamicGas(in.evm, callContext, callContext.gas, memorySize) + dynamicCost, err = operation.dynamicGas(evm, callContext, callContext.gas, memorySize) if err != nil { return nil, callContext.gas, fmt.Errorf("%w: %v", ErrOutOfGas, err) } cost += dynamicCost // for tracing - callGas = operation.constantGas + dynamicCost - in.evm.CallGasTemp() + callGas = operation.constantGas + dynamicCost - evm.CallGasTemp() if dbg.TraceDyanmicGas && dynamicCost > 0 { fmt.Printf("%d (%d.%d) Dynamic Gas: %d (%s)\n", blockNum, txIndex, txIncarnation, traceGas(op, callGas, cost), op) } @@ -441,12 +386,12 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO } // Do gas tracing before memory expansion - if in.cfg.Tracer != nil { - if in.cfg.Tracer.OnGasChange != nil { - in.cfg.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) + if tracer != nil { + if tracer.OnGasChange != nil { + tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } - if in.cfg.Tracer.OnOpcode != nil { - in.cfg.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) + if tracer.OnOpcode != nil { + tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err)) logged = true } } @@ -469,7 +414,7 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO } // execute the operation - pc, res, err = operation.execute(pc, in, callContext) + pc, res, err = operation.execute(pc, evm, callContext) if err != nil { break @@ -483,12 +428,3 @@ func (in *EVMInterpreter) Run(contract Contract, gas uint64, input []byte, readO return res, callContext.gas, err } - -// Depth returns the current call stack depth. -func (in *EVMInterpreter) Depth() int { return in.depth } - -// Increments the current call stack's depth. -func (in *EVMInterpreter) IncDepth() { in.depth++ } - -// Decrements the current call stack's depth -func (in *EVMInterpreter) DecDepth() { in.depth-- } diff --git a/execution/vm/jump_table.go b/execution/vm/jump_table.go index 6b0902f8e60..12f44208131 100644 --- a/execution/vm/jump_table.go +++ b/execution/vm/jump_table.go @@ -26,7 +26,7 @@ import ( ) type ( - executionFunc func(pc uint64, interpreter *EVMInterpreter, callContext *CallContext) (uint64, []byte, error) + executionFunc func(pc uint64, evm *EVM, callContext *CallContext) (uint64, []byte, error) gasFunc func(*EVM, *CallContext, uint64, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 memorySizeFunc func(*CallContext) (size uint64, overflow bool) diff --git a/execution/vm/mock_vm.go b/execution/vm/mock_vm.go deleted file mode 100644 index 07146751c85..00000000000 --- a/execution/vm/mock_vm.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package vm - -import ( - "fmt" - - "github.com/erigontech/erigon/execution/types/accounts" - "github.com/holiman/uint256" -) - -type readonlyGetSetter interface { - setReadonly(outerReadonly bool) func() - getReadonly() bool -} - -type testVM struct { - readonlyGetSetter - - recordedReadOnlies *[]*readOnlyState - recordedIsEVMCalled *[]bool - - env *EVM - isEVMSliceTest []bool - readOnlySliceTest []bool - currentIdx *int - - depth int -} - -func (evm *testVM) Run(_ Contract, _ uint64, _ []byte, readOnly bool) (ret []byte, gas uint64, err error) { - currentReadOnly := new(readOnlyState) - - currentReadOnly.outer = readOnly - currentReadOnly.before = evm.getReadonly() - - currentIndex := *evm.currentIdx - - callback := evm.setReadonly(readOnly) - defer func() { - callback() - currentReadOnly.after = evm.getReadonly() - }() - - currentReadOnly.in = evm.getReadonly() - - (*evm.recordedReadOnlies)[currentIndex] = currentReadOnly - (*evm.recordedIsEVMCalled)[currentIndex] = true - - *evm.currentIdx++ - - if *evm.currentIdx < len(evm.readOnlySliceTest) { - res, _, err := evm.env.interpreter.Run(*NewContract( - accounts.ZeroAddress, - accounts.ZeroAddress, - accounts.ZeroAddress, - uint256.Int{}, - ), 0, nil, evm.readOnlySliceTest[*evm.currentIdx]) - return res, 0, err - } - - return -} - -func (evm *testVM) Depth() int { return evm.depth } - -func (evm *testVM) IncDepth() { evm.depth++ } -func (evm *testVM) DecDepth() { evm.depth-- } - -type readOnlyState struct { - outer bool - before bool - in bool - after bool -} - -func (r *readOnlyState) String() string { - return fmt.Sprintf("READONLY Status: outer %t; before %t; in %t; after %t", r.outer, r.before, r.in, r.after) -} diff --git a/go.mod b/go.mod index 956576c7c1c..8c635846388 100644 --- a/go.mod +++ b/go.mod @@ -127,7 +127,6 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - pgregory.net/rapid v1.2.0 sigs.k8s.io/yaml v1.6.0 ) diff --git a/go.sum b/go.sum index 725f236a3d2..8e534892169 100644 --- a/go.sum +++ b/go.sum @@ -1528,8 +1528,6 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= -pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=