Merge pull request #22414 from karalabe/unship-2315

core, eth: unship EIP 2315
This commit is contained in:
Péter Szilágyi 2021-03-07 19:38:49 +02:00 committed by GitHub
commit 658cb9fc4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 50 additions and 428 deletions

View File

@ -96,19 +96,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
return c.isCode(udest) 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 // isCode returns true if the provided PC location is an actual opcode, as
// opposed to a data-segment following a PUSHN operation. // opposed to a data-segment following a PUSHN operation.
func (c *Contract) isCode(udest uint64) bool { func (c *Contract) isCode(udest uint64) bool {

View File

@ -29,7 +29,6 @@ var activators = map[int]func(*JumpTable){
2200: enable2200, 2200: enable2200,
1884: enable1884, 1884: enable1884,
1344: enable1344, 1344: enable1344,
2315: enable2315,
} }
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
@ -108,34 +107,6 @@ func enable2200(jt *JumpTable) {
jt[SSTORE].dynamicGas = gasSStoreEIP2200 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" // enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
// https://eips.ethereum.org/EIPS/eip-2929 // https://eips.ethereum.org/EIPS/eip-2929
func enable2929(jt *JumpTable) { func enable2929(jt *JumpTable) {

View File

@ -45,12 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Stack[k] = (*math.HexOrDecimal256)(v) 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.ReturnData = s.ReturnData
enc.Storage = s.Storage enc.Storage = s.Storage
enc.Depth = s.Depth enc.Depth = s.Depth
@ -71,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
Memory *hexutil.Bytes `json:"memory"` Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"` MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"` Stack []*math.HexOrDecimal256 `json:"stack"`
ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
ReturnData *hexutil.Bytes `json:"returnData"` ReturnData *hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"` Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"` Depth *int `json:"depth"`
@ -106,12 +99,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.Stack[k] = (*big.Int)(v) 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 { if dec.ReturnData != nil {
s.ReturnData = *dec.ReturnData s.ReturnData = *dec.ReturnData
} }

View File

@ -547,38 +547,6 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
return nil, nil 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) { func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(*pc)) callContext.stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil return nil, nil

View File

@ -94,7 +94,6 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack() stack = newstack()
rstack = newReturnStack()
pc = uint64(0) pc = uint64(0)
evmInterpreter = env.interpreter.(*EVMInterpreter) 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)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
if len(stack.data) != 1 { if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) 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(z)
stack.push(y) stack.push(y)
stack.push(x) stack.push(x)
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil}) opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
actual := stack.pop() actual := stack.pop()
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) 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 // getResult is a convenience function to generate the expected values
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack, rstack = newstack(), newReturnStack() stack = newstack()
pc = uint64(0) pc = uint64(0)
interpreter = env.interpreter.(*EVMInterpreter) interpreter = env.interpreter.(*EVMInterpreter)
) )
result := make([]TwoOperandTestcase, len(args)) result := make([]TwoOperandTestcase, len(args))
for i, param := range 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)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil}) opFn(&pc, interpreter, &callCtx{nil, stack, nil})
actual := stack.pop() actual := stack.pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} 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) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack, rstack = newstack(), newReturnStack() stack = newstack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
@ -300,7 +299,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
a.SetBytes(arg) a.SetBytes(arg)
stack.push(a) stack.push(a)
} }
op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
stack.pop() stack.pop()
} }
} }
@ -516,7 +515,7 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) { func TestOpMstore(t *testing.T) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack, rstack = newstack(), newReturnStack() stack = newstack()
mem = NewMemory() mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
@ -526,12 +525,12 @@ func TestOpMstore(t *testing.T) {
pc := uint64(0) pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) 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 { if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v) t.Fatalf("Mstore fail, got %v, expected %v", got, v)
} }
stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) 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" { if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value") t.Fatalf("Mstore failed to overwrite previous value")
} }
@ -540,7 +539,7 @@ func TestOpMstore(t *testing.T) {
func BenchmarkOpMstore(bench *testing.B) { func BenchmarkOpMstore(bench *testing.B) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack, rstack = newstack(), newReturnStack() stack = newstack()
mem = NewMemory() mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
@ -554,14 +553,14 @@ func BenchmarkOpMstore(bench *testing.B) {
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.pushN(*value, *memStart) stack.pushN(*value, *memStart)
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
} }
} }
func BenchmarkOpSHA3(bench *testing.B) { func BenchmarkOpSHA3(bench *testing.B) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack, rstack = newstack(), newReturnStack() stack = newstack()
mem = NewMemory() mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
@ -573,7 +572,7 @@ func BenchmarkOpSHA3(bench *testing.B) {
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.pushN(*uint256.NewInt().SetUint64(32), *start) stack.pushN(*uint256.NewInt().SetUint64(32), *start)
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
} }
} }

View File

@ -67,7 +67,6 @@ type Interpreter interface {
type callCtx struct { type callCtx struct {
memory *Memory memory *Memory
stack *Stack stack *Stack
rstack *ReturnStack
contract *Contract contract *Contract
} }
@ -161,14 +160,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
var ( var (
op OpCode // current opcode op OpCode // current opcode
mem = NewMemory() // bound memory mem = NewMemory() // bound memory
stack = newstack() // local stack stack = newstack() // local stack
returns = newReturnStack() // local returns stack
callContext = &callCtx{ callContext = &callCtx{
memory: mem, memory: mem,
stack: stack, stack: stack,
rstack: returns,
contract: contract, contract: contract,
} }
// For optimisation reason we're using uint64 as the program counter. // 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 // they are returned to the pools
defer func() { defer func() {
returnStack(stack) returnStack(stack)
returnRStack(returns)
}() }()
contract.Input = input contract.Input = input
@ -195,9 +191,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
defer func() { defer func() {
if err != nil { if err != nil {
if !logged { 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 { } 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 { 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 logged = true
} }

View File

@ -66,7 +66,6 @@ type JumpTable [256]*operation
// contantinople, istanbul, petersburg and berlin instructions. // contantinople, istanbul, petersburg and berlin instructions.
func newBerlinInstructionSet() JumpTable { func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet() 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 enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
return instructionSet return instructionSet
} }

View File

@ -70,7 +70,6 @@ type StructLog struct {
Memory []byte `json:"memory"` Memory []byte `json:"memory"`
MemorySize int `json:"memSize"` MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"` Stack []*big.Int `json:"stack"`
ReturnStack []uint32 `json:"returnStack"`
ReturnData []byte `json:"returnData"` ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"` Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"` Depth int `json:"depth"`
@ -81,7 +80,6 @@ type StructLog struct {
// overrides for gencodec // overrides for gencodec
type structLogMarshaling struct { type structLogMarshaling struct {
Stack []*math.HexOrDecimal256 Stack []*math.HexOrDecimal256
ReturnStack []math.HexOrDecimal64
Gas math.HexOrDecimal64 Gas math.HexOrDecimal64
GasCost math.HexOrDecimal64 GasCost math.HexOrDecimal64
Memory hexutil.Bytes Memory hexutil.Bytes
@ -110,8 +108,8 @@ func (s *StructLog) ErrorString() string {
// if you need to retain them beyond the current call. // if you need to retain them beyond the current call.
type Tracer interface { type Tracer interface {
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error 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 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, rStack *ReturnStack, 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 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 logs a new structured log message and pushes it out to the environment
// //
// CaptureState also tracks SLOAD/SSTORE ops to track storage change. // 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 // check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return errTraceLimitReached 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()) 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 // Copy a snapshot of the current storage to a new container
var storage Storage var storage Storage
if !l.cfg.DisableStorage { if !l.cfg.DisableStorage {
@ -204,14 +197,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
copy(rdata, rData) copy(rdata, rData)
} }
// create a new snapshot of the EVM. // 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) l.logs = append(l.logs, log)
return nil return nil
} }
// CaptureFault implements the Tracer interface to trace an execution fault // CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode. // 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 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)) 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 { if len(log.Memory) > 0 {
fmt.Fprintln(writer, "Memory:") fmt.Fprintln(writer, "Memory:")
fmt.Fprint(writer, hex.Dump(log.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 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) fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
if !t.cfg.DisableStack { 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, ",")) b := fmt.Sprintf("[%v]", strings.Join(a, ","))
fmt.Fprintf(t.out, "%10v |", b) 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.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
fmt.Fprintln(t.out, "") fmt.Fprintln(t.out, "")
@ -351,7 +330,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
return nil 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) fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)

View File

@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
} }
// CaptureState outputs state information on the logger. // 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{ log := StructLog{
Pc: pc, Pc: pc,
Op: op, Op: op,
@ -68,7 +68,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
logstack[i] = item.ToBig() logstack[i] = item.ToBig()
} }
log.Stack = logstack log.Stack = logstack
log.ReturnStack = rStack.data
} }
if !l.cfg.DisableReturnData { if !l.cfg.DisableReturnData {
log.ReturnData = rData 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. // 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 return nil
} }

View File

@ -55,13 +55,12 @@ func TestStoreCapture(t *testing.T) {
logger = NewStructLogger(nil) logger = NewStructLogger(nil)
mem = NewMemory() mem = NewMemory()
stack = newstack() stack = newstack()
rstack = newReturnStack()
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
) )
stack.push(uint256.NewInt().SetUint64(1)) stack.push(uint256.NewInt().SetUint64(1))
stack.push(uint256.NewInt()) stack.push(uint256.NewInt())
var index common.Hash 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 { 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()])) t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
} }

View File

@ -107,21 +107,18 @@ const (
// 0x50 range - 'storage' and execution. // 0x50 range - 'storage' and execution.
const ( const (
POP OpCode = 0x50 POP OpCode = 0x50
MLOAD OpCode = 0x51 MLOAD OpCode = 0x51
MSTORE OpCode = 0x52 MSTORE OpCode = 0x52
MSTORE8 OpCode = 0x53 MSTORE8 OpCode = 0x53
SLOAD OpCode = 0x54 SLOAD OpCode = 0x54
SSTORE OpCode = 0x55 SSTORE OpCode = 0x55
JUMP OpCode = 0x56 JUMP OpCode = 0x56
JUMPI OpCode = 0x57 JUMPI OpCode = 0x57
PC OpCode = 0x58 PC OpCode = 0x58
MSIZE OpCode = 0x59 MSIZE OpCode = 0x59
GAS OpCode = 0x5a GAS OpCode = 0x5a
JUMPDEST OpCode = 0x5b JUMPDEST OpCode = 0x5b
BEGINSUB OpCode = 0x5c
RETURNSUB OpCode = 0x5d
JUMPSUB OpCode = 0x5e
) )
// 0x60 range. // 0x60 range.
@ -300,10 +297,6 @@ var opCodeToString = map[OpCode]string{
GAS: "GAS", GAS: "GAS",
JUMPDEST: "JUMPDEST", JUMPDEST: "JUMPDEST",
BEGINSUB: "BEGINSUB",
JUMPSUB: "JUMPSUB",
RETURNSUB: "RETURNSUB",
// 0x60 range - push. // 0x60 range - push.
PUSH1: "PUSH1", PUSH1: "PUSH1",
PUSH2: "PUSH2", PUSH2: "PUSH2",
@ -468,9 +461,6 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE, "MSIZE": MSIZE,
"GAS": GAS, "GAS": GAS,
"JUMPDEST": JUMPDEST, "JUMPDEST": JUMPDEST,
"BEGINSUB": BEGINSUB,
"RETURNSUB": RETURNSUB,
"JUMPSUB": JUMPSUB,
"PUSH1": PUSH1, "PUSH1": PUSH1,
"PUSH2": PUSH2, "PUSH2": PUSH2,
"PUSH3": PUSH3, "PUSH3": PUSH3,

View File

@ -330,14 +330,14 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat
return nil 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++ s.steps++
// Enable this for more output // Enable this for more output
//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
return nil 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 return nil
} }
@ -345,227 +345,6 @@ func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
return nil 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 // benchmarkNonModifyingCode benchmarks code, but if the code modifies the
// state, this should not be used, since it does not reset the state between runs. // 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) { func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {

View File

@ -98,34 +98,3 @@ func (st *Stack) Print() {
} }
fmt.Println("#############") 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
}

View File

@ -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. // 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 { if jst.err == nil {
// Initialize the context if it wasn't done yet // Initialize the context if it wasn't done yet
if !jst.inited { 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 // CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode. // 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 { if jst.err == nil {
// Apart from the error, everything matches the previous invocation // Apart from the error, everything matches the previous invocation
jst.errorValue = new(string) jst.errorValue = new(string)

View File

@ -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}) 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) 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") timeout := errors.New("stahp")
tracer.Stop(timeout) 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() { if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
t.Errorf("Expected timeout error, got %v", err) t.Errorf("Expected timeout error, got %v", err)