Merge pull request #22414 from karalabe/unship-2315
core, eth: unship EIP 2315
This commit is contained in:
commit
658cb9fc4f
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()]))
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user