diff --git a/core/vm/evm.go b/core/vm/evm.go index 009bb6aaf3..143a984ac6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -226,7 +226,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, false) gas = contract.Gas } } @@ -290,7 +290,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, } code := evm.StateDB.GetCode(addrCopy) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, false) gas = contract.Gas } if err != nil { @@ -341,7 +341,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by } code := evm.StateDB.GetCode(addrCopy) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, false) gas = contract.Gas } if err != nil { @@ -403,7 +403,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. - ret, err = evm.interpreter.Run(contract, input, true) + ret, err = evm.interpreter.Run(contract, input, true, false) gas = contract.Gas } if err != nil { @@ -533,7 +533,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // initNewContract runs a new contract's creation code, performs checks on the // resulting code that is to be deployed, and consumes necessary gas. func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) { - ret, err := evm.interpreter.Run(contract, nil, false) + // Charge the contract creation init gas in verkle mode + if evm.chainRules.IsEIP4762 { + if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) { + return nil, ErrOutOfGas + } + } + + ret, err := evm.interpreter.Run(contract, nil, false, true) if err != nil { return ret, err } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index a3f9ee81d1..a7ed102db2 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -116,7 +116,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) - opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) } @@ -231,7 +231,7 @@ func TestAddMod(t *testing.T) { stack.push(z) stack.push(y) stack.push(x) - opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false}) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -258,7 +258,7 @@ func TestWriteExpectedValues(t *testing.T) { y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) - opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, interpreter, &ScopeContext{nil, stack, nil, 0, nil, false}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } @@ -294,7 +294,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() - scope = &ScopeContext{nil, stack, nil} + scope = &ScopeContext{nil, stack, nil, 0, nil, false} evmInterpreter = NewEVMInterpreter(env) ) @@ -545,13 +545,13 @@ func TestOpMstore(t *testing.T) { v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) stack.push(new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } stack.push(new(uint256.Int).SetUint64(0x1)) stack.push(new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -575,7 +575,7 @@ func BenchmarkOpMstore(bench *testing.B) { for i := 0; i < bench.N; i++ { stack.push(value) stack.push(memStart) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false}) } } @@ -590,7 +590,7 @@ func TestOpTstore(t *testing.T) { to = common.Address{1} contractRef = contractRef{caller} contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) - scopeContext = ScopeContext{mem, stack, contract} + scopeContext = ScopeContext{mem, stack, contract, 0, nil, false} value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) @@ -638,7 +638,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { for i := 0; i < bench.N; i++ { stack.push(uint256.NewInt(32)) stack.push(start) - opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false}) } } @@ -705,7 +705,7 @@ func TestCreate2Addresses(t *testing.T) { stack.push(big.NewInt(int64(len(code)))) //size stack.push(big.NewInt(0)) // memstart stack.push(big.NewInt(0)) // value - gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) + gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0, nil, false, 0) fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) */ expected := common.BytesToAddress(common.FromHex(tt.expected)) @@ -733,7 +733,7 @@ func TestRandom(t *testing.T) { pc = uint64(0) evmInterpreter = env.interpreter ) - opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) } @@ -775,7 +775,7 @@ func TestBlobHash(t *testing.T) { evmInterpreter = env.interpreter ) stack.push(uint256.NewInt(tt.idx)) - opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) } @@ -916,7 +916,7 @@ func TestOpMCopy(t *testing.T) { mem.Resize(memorySize) } // Do the copy - opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false}) want := common.FromHex(strings.ReplaceAll(tc.want, " ", "")) if have := mem.store; !bytes.Equal(want, have) { t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 793f398367..42b2483ac1 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -43,6 +43,10 @@ type ScopeContext struct { Memory *Memory Stack *Stack Contract *Contract + + CodeSection uint64 + ReturnStack ReturnStack + InitCodeMode bool } // MemoryData returns the underlying memory slice. Callers must not modify the contents @@ -89,10 +93,33 @@ func (ctx *ScopeContext) ContractCode() []byte { return ctx.Contract.Code } +type ReturnStack []*ReturnContext + +// Pop removes an element from the return stack +// Panics if the return stack is empty, which should +// never happen, since EOF code is verified for that. +func (ctx *ReturnStack) Pop() *ReturnContext { + item := (*ctx)[ctx.Len()-1] + *ctx = (*ctx)[:ctx.Len()-1] + return item +} + +// Len returns the length of the return stack +func (ctx *ReturnStack) Len() int { + return len(*ctx) +} + +type ReturnContext struct { + Section uint64 + Pc uint64 + StackHeight int +} + // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { - evm *EVM - table *JumpTable + evm *EVM + table *JumpTable + tableEOF *JumpTable hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes @@ -148,7 +175,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { } } evm.Config.ExtraEips = extraEips - return &EVMInterpreter{evm: evm, table: table} + return &EVMInterpreter{evm: evm, table: table, tableEOF: &pragueEOFInstructionSet} } // Run loops and evaluates the contract's code with the given input data and returns @@ -157,7 +184,7 @@ func NewEVMInterpreter(evm *EVM) *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, input []byte, readOnly bool) (ret []byte, err error) { +func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, isInitCode bool) (ret []byte, err error) { // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() @@ -179,13 +206,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } var ( + jt *JumpTable // current jump table op OpCode // current opcode mem = NewMemory() // bound memory stack = newstack() // local stack callContext = &ScopeContext{ - Memory: mem, - Stack: stack, - Contract: contract, + Memory: mem, + Stack: stack, + Contract: contract, + CodeSection: 0, + ReturnStack: []*ReturnContext{{Section: 0, Pc: 0, StackHeight: 0}}, + InitCodeMode: isInitCode, } // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC @@ -208,6 +239,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( }() contract.Input = input + if contract.IsEOF() { + jt = in.tableEOF + } else { + jt = in.table + } + if debug { defer func() { // this deferred method handles exit-with-error if err == nil { @@ -240,8 +277,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // 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.table[op] + op = contract.GetOp(pc, callContext.CodeSection) + operation := jt[op] cost = operation.constantGas // For tracing // Validate stack if sLen := stack.len(); sLen < operation.minStack { diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 7122b3c90e..616e923ae4 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -77,7 +77,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) - ret, err := env.Interpreter().Run(contract, []byte{}, false) + ret, err := env.Interpreter().Run(contract, []byte{}, false, false) tracer.OnExit(0, ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 137608f884..29308cec43 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -62,7 +62,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(env.GetVMContext(), nil, common.Address{}) - _, err := env.Interpreter().Run(contract, []byte{}, false) + _, err := env.Interpreter().Run(contract, []byte{}, false, false) if err != nil { t.Fatal(err) }