From 7834e4a278038e57b741ee826b3a46ff9d809fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 2 Mar 2021 23:40:57 +0200 Subject: [PATCH 1/2] core, eth: unship EIP 2315 --- core/vm/contract.go | 13 ------------- core/vm/eips.go | 29 ----------------------------- core/vm/gen_structlog.go | 13 ------------- core/vm/instructions.go | 32 -------------------------------- core/vm/instructions_test.go | 33 ++++++++++++++++----------------- core/vm/interpreter.go | 16 ++++++---------- core/vm/jump_table.go | 1 - core/vm/logger.go | 35 +++++++---------------------------- core/vm/logger_json.go | 5 ++--- core/vm/logger_test.go | 3 +-- core/vm/opcodes.go | 34 ++++++++++++---------------------- core/vm/stack.go | 31 ------------------------------- eth/tracers/tracer.go | 4 ++-- eth/tracers/tracer_test.go | 4 ++-- 14 files changed, 48 insertions(+), 205 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 915193d137..61dbd5007a 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -96,19 +96,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { return c.isCode(udest) } -func (c *Contract) validJumpSubdest(udest uint64) bool { - // PC cannot go beyond len(code) and certainly can't be bigger than 63 bits. - // Don't bother checking for BEGINSUB in that case. - if int64(udest) < 0 || udest >= uint64(len(c.Code)) { - return false - } - // Only BEGINSUBs allowed for destinations - if OpCode(c.Code[udest]) != BEGINSUB { - return false - } - return c.isCode(udest) -} - // isCode returns true if the provided PC location is an actual opcode, as // opposed to a data-segment following a PUSHN operation. func (c *Contract) isCode(udest uint64) bool { diff --git a/core/vm/eips.go b/core/vm/eips.go index 962c0f14b1..0c8bf1792e 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -29,7 +29,6 @@ var activators = map[int]func(*JumpTable){ 2200: enable2200, 1884: enable1884, 1344: enable1344, - 2315: enable2315, } // EnableEIP enables the given EIP on the config. @@ -108,34 +107,6 @@ func enable2200(jt *JumpTable) { jt[SSTORE].dynamicGas = gasSStoreEIP2200 } -// enable2315 applies EIP-2315 (Simple Subroutines) -// - Adds opcodes that jump to and return from subroutines -func enable2315(jt *JumpTable) { - // New opcode - jt[BEGINSUB] = &operation{ - execute: opBeginSub, - constantGas: GasQuickStep, - minStack: minStack(0, 0), - maxStack: maxStack(0, 0), - } - // New opcode - jt[JUMPSUB] = &operation{ - execute: opJumpSub, - constantGas: GasSlowStep, - minStack: minStack(1, 0), - maxStack: maxStack(1, 0), - jumps: true, - } - // New opcode - jt[RETURNSUB] = &operation{ - execute: opReturnSub, - constantGas: GasFastStep, - minStack: minStack(0, 0), - maxStack: maxStack(0, 0), - jumps: true, - } -} - // enable2929 enables "EIP-2929: Gas cost increases for state access opcodes" // https://eips.ethereum.org/EIPS/eip-2929 func enable2929(jt *JumpTable) { diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 44da014de9..ac04afe8b7 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -45,12 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.Stack[k] = (*math.HexOrDecimal256)(v) } } - if s.ReturnStack != nil { - enc.ReturnStack = make([]math.HexOrDecimal64, len(s.ReturnStack)) - for k, v := range s.ReturnStack { - enc.ReturnStack[k] = math.HexOrDecimal64(v) - } - } enc.ReturnData = s.ReturnData enc.Storage = s.Storage enc.Depth = s.Depth @@ -71,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { Memory *hexutil.Bytes `json:"memory"` MemorySize *int `json:"memSize"` Stack []*math.HexOrDecimal256 `json:"stack"` - ReturnStack []math.HexOrDecimal64 `json:"returnStack"` ReturnData *hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` @@ -106,12 +99,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.Stack[k] = (*big.Int)(v) } } - if dec.ReturnStack != nil { - s.ReturnStack = make([]uint32, len(dec.ReturnStack)) - for k, v := range dec.ReturnStack { - s.ReturnStack[k] = uint32(v) - } - } if dec.ReturnData != nil { s.ReturnData = *dec.ReturnData } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1137505292..f4ca0603ed 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -547,38 +547,6 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( return nil, nil } -func opBeginSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - return nil, ErrInvalidSubroutineEntry -} - -func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - if len(callContext.rstack.data) >= 1023 { - return nil, ErrReturnStackExceeded - } - pos := callContext.stack.pop() - if !pos.IsUint64() { - return nil, ErrInvalidJump - } - posU64 := pos.Uint64() - if !callContext.contract.validJumpSubdest(posU64) { - return nil, ErrInvalidJump - } - callContext.rstack.push(uint32(*pc)) - *pc = posU64 + 1 - return nil, nil -} - -func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - if len(callContext.rstack.data) == 0 { - return nil, ErrInvalidRetsub - } - // Other than the check that the return stack is not empty, there is no - // need to validate the pc from 'returns', since we only ever push valid - //values onto it via jumpsub. - *pc = uint64(callContext.rstack.pop()) + 1 - return nil, nil -} - func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { callContext.stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 985d5a5156..55d876581c 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -94,7 +94,6 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() - rstack = newReturnStack() pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) ) @@ -105,7 +104,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, &callCtx{nil, stack, rstack, nil}) + opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) } @@ -220,7 +219,7 @@ func TestAddMod(t *testing.T) { stack.push(z) stack.push(y) stack.push(x) - opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil}) + opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil}) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -231,10 +230,10 @@ 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, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() - pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) ) result := make([]TwoOperandTestcase, len(args)) for i, param := range args { @@ -242,7 +241,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) - opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil}) + opFn(&pc, interpreter, &callCtx{nil, stack, nil}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } @@ -282,7 +281,7 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -300,7 +299,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { a.SetBytes(arg) stack.push(a) } - op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) + op(&pc, evmInterpreter, &callCtx{nil, stack, nil}) stack.pop() } } @@ -516,7 +515,7 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -526,12 +525,12 @@ func TestOpMstore(t *testing.T) { pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -540,7 +539,7 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -554,14 +553,14 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*value, *memStart) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } func BenchmarkOpSHA3(bench *testing.B) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -573,7 +572,7 @@ func BenchmarkOpSHA3(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*uint256.NewInt().SetUint64(32), *start) - opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0084b7d071..06a3b962b2 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -67,7 +67,6 @@ type Interpreter interface { type callCtx struct { memory *Memory stack *Stack - rstack *ReturnStack contract *Contract } @@ -161,14 +160,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } var ( - op OpCode // current opcode - mem = NewMemory() // bound memory - stack = newstack() // local stack - returns = newReturnStack() // local returns stack + op OpCode // current opcode + mem = NewMemory() // bound memory + stack = newstack() // local stack callContext = &callCtx{ memory: mem, stack: stack, - rstack: returns, contract: contract, } // For optimisation reason we're using uint64 as the program counter. @@ -187,7 +184,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // they are returned to the pools defer func() { returnStack(stack) - returnRStack(returns) }() contract.Input = input @@ -195,9 +191,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) } } }() @@ -279,7 +275,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err) logged = true } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index d831f9300f..7b61762456 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -66,7 +66,6 @@ type JumpTable [256]*operation // contantinople, istanbul, petersburg and berlin instructions. func newBerlinInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() - enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315 enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 return instructionSet } diff --git a/core/vm/logger.go b/core/vm/logger.go index 962be6ec8e..41ce00ed01 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -70,7 +70,6 @@ type StructLog struct { Memory []byte `json:"memory"` MemorySize int `json:"memSize"` Stack []*big.Int `json:"stack"` - ReturnStack []uint32 `json:"returnStack"` ReturnData []byte `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` @@ -81,7 +80,6 @@ type StructLog struct { // overrides for gencodec type structLogMarshaling struct { Stack []*math.HexOrDecimal256 - ReturnStack []math.HexOrDecimal64 Gas math.HexOrDecimal64 GasCost math.HexOrDecimal64 Memory hexutil.Bytes @@ -110,8 +108,8 @@ func (s *StructLog) ErrorString() string { // if you need to retain them beyond the current call. type Tracer interface { CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error - CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error - CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error + CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error } @@ -148,7 +146,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error { // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { return errTraceLimitReached @@ -167,11 +165,6 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui stck[i] = new(big.Int).Set(item.ToBig()) } } - var rstack []uint32 - if !l.cfg.DisableStack && rStack != nil { - rstck := make([]uint32, len(rStack.data)) - copy(rstck, rStack.data) - } // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage { @@ -204,14 +197,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui copy(rdata, rData) } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err} l.logs = append(l.logs, log) return nil } // CaptureFault implements the Tracer interface to trace an execution fault // while running an opcode. -func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { return nil } @@ -252,12 +245,6 @@ func WriteTrace(writer io.Writer, logs []StructLog) { fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32)) } } - if len(log.ReturnStack) > 0 { - fmt.Fprintln(writer, "ReturnStack:") - for i := len(log.Stack) - 1; i >= 0; i-- { - fmt.Fprintf(writer, "%08d 0x%x (%d)\n", len(log.Stack)-i-1, log.ReturnStack[i], log.ReturnStack[i]) - } - } if len(log.Memory) > 0 { fmt.Fprintln(writer, "Memory:") fmt.Fprint(writer, hex.Dump(log.Memory)) @@ -323,7 +310,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b return nil } -func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error { fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) if !t.cfg.DisableStack { @@ -334,14 +321,6 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64 } b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) - - // format return stack - a = a[:0] - for _, elem := range rStack.data { - a = append(a, fmt.Sprintf("%2d", elem)) - } - b = fmt.Sprintf("[%v]", strings.Join(a, ",")) - fmt.Fprintf(t.out, "%10v |", b) } fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund()) fmt.Fprintln(t.out, "") @@ -351,7 +330,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64 return nil } -func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { +func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 5f3f2c42f7..a27c261ed8 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create } // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error { log := StructLog{ Pc: pc, Op: op, @@ -68,7 +68,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint logstack[i] = item.ToBig() } log.Stack = logstack - log.ReturnStack = rStack.data } if !l.cfg.DisableReturnData { log.ReturnData = rData @@ -77,7 +76,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint } // CaptureFault outputs state information on the logger. -func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { +func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { return nil } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index bf7d5358f8..5a5f42fd34 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -55,13 +55,12 @@ func TestStoreCapture(t *testing.T) { logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() - rstack = newReturnStack() contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) ) stack.push(uint256.NewInt().SetUint64(1)) stack.push(uint256.NewInt()) var index common.Hash - logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil) + logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, nil, contract, 0, nil) if len(logger.storage[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()])) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index da7b2ee4aa..b0adf37d0c 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -107,21 +107,18 @@ const ( // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 - MLOAD OpCode = 0x51 - MSTORE OpCode = 0x52 - MSTORE8 OpCode = 0x53 - SLOAD OpCode = 0x54 - SSTORE OpCode = 0x55 - JUMP OpCode = 0x56 - JUMPI OpCode = 0x57 - PC OpCode = 0x58 - MSIZE OpCode = 0x59 - GAS OpCode = 0x5a - JUMPDEST OpCode = 0x5b - BEGINSUB OpCode = 0x5c - RETURNSUB OpCode = 0x5d - JUMPSUB OpCode = 0x5e + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b ) // 0x60 range. @@ -300,10 +297,6 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", - BEGINSUB: "BEGINSUB", - JUMPSUB: "JUMPSUB", - RETURNSUB: "RETURNSUB", - // 0x60 range - push. PUSH1: "PUSH1", PUSH2: "PUSH2", @@ -468,9 +461,6 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, - "BEGINSUB": BEGINSUB, - "RETURNSUB": RETURNSUB, - "JUMPSUB": JUMPSUB, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/core/vm/stack.go b/core/vm/stack.go index af27d6552c..c71d2653a5 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -98,34 +98,3 @@ func (st *Stack) Print() { } fmt.Println("#############") } - -var rStackPool = sync.Pool{ - New: func() interface{} { - return &ReturnStack{data: make([]uint32, 0, 10)} - }, -} - -// ReturnStack is an object for basic return stack operations. -type ReturnStack struct { - data []uint32 -} - -func newReturnStack() *ReturnStack { - return rStackPool.Get().(*ReturnStack) -} - -func returnRStack(rs *ReturnStack) { - rs.data = rs.data[:0] - rStackPool.Put(rs) -} - -func (st *ReturnStack) push(d uint32) { - st.data = append(st.data, d) -} - -// A uint32 is sufficient as for code below 4.2G -func (st *ReturnStack) pop() (ret uint32) { - ret = st.data[len(st.data)-1] - st.data = st.data[:len(st.data)-1] - return -} diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 80775caa8e..4c398edf34 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -544,7 +544,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error { +func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rdata []byte, contract *vm.Contract, depth int, err error) error { if jst.err == nil { // Initialize the context if it wasn't done yet if !jst.inited { @@ -595,7 +595,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost // CaptureFault implements the Tracer interface to trace an execution fault // while running an opcode. -func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { +func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { // Apart from the error, everything matches the previous invocation jst.errorValue = new(string) diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index f28e14864b..d96030385c 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -152,10 +152,10 @@ func TestHaltBetweenSteps(t *testing.T) { env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) if _, err := tracer.GetResult(); err.Error() != timeout.Error() { t.Errorf("Expected timeout error, got %v", err) From 430f69e01eb33a1b2f4f3c9372da14d01aaba62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 2 Mar 2021 23:51:03 +0200 Subject: [PATCH 2/2] core/vm/runtime: more unshipping --- core/vm/runtime/runtime_test.go | 225 +------------------------------- 1 file changed, 2 insertions(+), 223 deletions(-) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index af69e3333f..6e0434c2ca 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -330,14 +330,14 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat return nil } -func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error { +func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rData []byte, contract *vm.Contract, depth int, err error) error { s.steps++ // Enable this for more output //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) return nil } -func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { +func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { return nil } @@ -345,227 +345,6 @@ func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, return nil } -func TestJumpSub1024Limit(t *testing.T) { - state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - address := common.HexToAddress("0x0a") - // Code is - // 0 beginsub - // 1 push 0 - // 3 jumpsub - // - // The code recursively calls itself. It should error when the returns-stack - // grows above 1023 - state.SetCode(address, []byte{ - byte(vm.PUSH1), 3, - byte(vm.JUMPSUB), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 3, - byte(vm.JUMPSUB), - }) - tracer := stepCounter{inner: vm.NewJSONLogger(nil, os.Stdout)} - // Enable 2315 - _, _, err := Call(address, nil, &Config{State: state, - GasLimit: 20000, - ChainConfig: params.AllEthashProtocolChanges, - EVMConfig: vm.Config{ - ExtraEips: []int{2315}, - Debug: true, - //Tracer: vm.NewJSONLogger(nil, os.Stdout), - Tracer: &tracer, - }}) - exp := "return stack limit reached" - if err.Error() != exp { - t.Fatalf("expected %v, got %v", exp, err) - } - if exp, got := 2048, tracer.steps; exp != got { - t.Fatalf("expected %d steps, got %d", exp, got) - } -} - -func TestReturnSubShallow(t *testing.T) { - state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - address := common.HexToAddress("0x0a") - // The code does returnsub without having anything on the returnstack. - // It should not panic, but just fail after one step - state.SetCode(address, []byte{ - byte(vm.PUSH1), 5, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - }) - tracer := stepCounter{} - - // Enable 2315 - _, _, err := Call(address, nil, &Config{State: state, - GasLimit: 10000, - ChainConfig: params.AllEthashProtocolChanges, - EVMConfig: vm.Config{ - ExtraEips: []int{2315}, - Debug: true, - Tracer: &tracer, - }}) - - exp := "invalid retsub" - if err.Error() != exp { - t.Fatalf("expected %v, got %v", exp, err) - } - if exp, got := 4, tracer.steps; exp != got { - t.Fatalf("expected %d steps, got %d", exp, got) - } -} - -// disabled -- only used for generating markdown -func DisabledTestReturnCases(t *testing.T) { - cfg := &Config{ - EVMConfig: vm.Config{ - Debug: true, - Tracer: vm.NewMarkdownLogger(nil, os.Stdout), - ExtraEips: []int{2315}, - }, - } - // This should fail at first opcode - Execute([]byte{ - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.PC), - }, nil, cfg) - - // Should also fail - Execute([]byte{ - byte(vm.PUSH1), 5, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - }, nil, cfg) - - // This should complete - Execute([]byte{ - byte(vm.PUSH1), 0x4, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 0x9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - }, nil, cfg) -} - -// DisabledTestEipExampleCases contains various testcases that are used for the -// EIP examples -// This test is disabled, as it's only used for generating markdown -func DisabledTestEipExampleCases(t *testing.T) { - cfg := &Config{ - EVMConfig: vm.Config{ - Debug: true, - Tracer: vm.NewMarkdownLogger(nil, os.Stdout), - ExtraEips: []int{2315}, - }, - } - prettyPrint := func(comment string, code []byte) { - instrs := make([]string, 0) - it := asm.NewInstructionIterator(code) - for it.Next() { - if it.Arg() != nil && 0 < len(it.Arg()) { - instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg())) - } else { - instrs = append(instrs, fmt.Sprintf("%v", it.Op())) - } - } - ops := strings.Join(instrs, ", ") - - fmt.Printf("%v\nBytecode: `0x%x` (`%v`)\n", - comment, - code, ops) - Execute(code, nil, cfg) - } - - { // First eip testcase - code := []byte{ - byte(vm.PUSH1), 4, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should jump into a subroutine, back out and stop.", code) - } - - { - code := []byte{ - byte(vm.PUSH9), 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 4 + 8, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 8 + 9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should execute fine, going into one two depths of subroutines", code) - } - // TODO(@holiman) move this test into an actual test, which not only prints - // out the trace. - { - code := []byte{ - byte(vm.PUSH9), 0x01, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 4 + 8, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 8 + 9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should fail, since the given location is outside of the "+ - "code-range. The code is the same as previous example, except that the "+ - "pushed location is `0x01000000000000000c` instead of `0x0c`.", code) - } - { - // This should fail at first opcode - code := []byte{ - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.PC), - } - prettyPrint("This should fail at first opcode, due to shallow `return_stack`", code) - - } - { - code := []byte{ - byte(vm.PUSH1), 5, // Jump past the subroutine - byte(vm.JUMP), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.JUMPDEST), - byte(vm.PUSH1), 3, // Now invoke the subroutine - byte(vm.JUMPSUB), - } - prettyPrint("In this example. the JUMPSUB is on the last byte of code. When the "+ - "subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, "+ - "and not exit with error", code) - } - - { - code := []byte{ - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.STOP), - } - prettyPrint("In this example, the code 'walks' into a subroutine, which is not "+ - "allowed, and causes an error", code) - } -} - // benchmarkNonModifyingCode benchmarks code, but if the code modifies the // state, this should not be used, since it does not reset the state between runs. func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {