aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm
diff options
context:
space:
mode:
Diffstat (limited to 'core/vm')
-rw-r--r--core/vm/analysis.go57
-rw-r--r--core/vm/analysis_test.go53
-rw-r--r--core/vm/evm.go18
-rw-r--r--core/vm/gas_table.go46
-rw-r--r--core/vm/instructions.go76
-rw-r--r--core/vm/interpreter.go43
-rw-r--r--core/vm/jump_table.go4
-rw-r--r--core/vm/logger.go26
-rw-r--r--core/vm/logger_test.go24
9 files changed, 170 insertions, 177 deletions
diff --git a/core/vm/analysis.go b/core/vm/analysis.go
index d5f048d1d..f9c4298d3 100644
--- a/core/vm/analysis.go
+++ b/core/vm/analysis.go
@@ -25,7 +25,7 @@ import (
// destinations stores one map per contract (keyed by hash of code).
// The maps contain an entry for each location of a JUMPDEST
// instruction.
-type destinations map[common.Hash][]byte
+type destinations map[common.Hash]bitvec
// has checks whether code has a JUMPDEST at dest.
func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool {
@@ -38,24 +38,53 @@ func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool
m, analysed := d[codehash]
if !analysed {
- m = jumpdests(code)
+ m = codeBitmap(code)
d[codehash] = m
}
- return (m[udest/8] & (1 << (udest % 8))) != 0
+ return OpCode(code[udest]) == JUMPDEST && m.codeSegment(udest)
}
-// jumpdests creates a map that contains an entry for each
-// PC location that is a JUMPDEST instruction.
-func jumpdests(code []byte) []byte {
- m := make([]byte, len(code)/8+1)
- for pc := uint64(0); pc < uint64(len(code)); pc++ {
+// bitvec is a bit vector which maps bytes in a program.
+// An unset bit means the byte is an opcode, a set bit means
+// it's data (i.e. argument of PUSHxx).
+type bitvec []byte
+
+func (bits *bitvec) set(pos uint64) {
+ (*bits)[pos/8] |= 0x80 >> (pos % 8)
+}
+func (bits *bitvec) set8(pos uint64) {
+ (*bits)[pos/8] |= 0xFF >> (pos % 8)
+ (*bits)[pos/8+1] |= ^(0xFF >> (pos % 8))
+}
+
+// codeSegment checks if the position is in a code segment.
+func (bits *bitvec) codeSegment(pos uint64) bool {
+ return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0
+}
+
+// codeBitmap collects data locations in code.
+func codeBitmap(code []byte) bitvec {
+ // The bitmap is 4 bytes longer than necessary, in case the code
+ // ends with a PUSH32, the algorithm will push zeroes onto the
+ // bitvector outside the bounds of the actual code.
+ bits := make(bitvec, len(code)/8+1+4)
+ for pc := uint64(0); pc < uint64(len(code)); {
op := OpCode(code[pc])
- if op == JUMPDEST {
- m[pc/8] |= 1 << (pc % 8)
- } else if op >= PUSH1 && op <= PUSH32 {
- a := uint64(op) - uint64(PUSH1) + 1
- pc += a
+
+ if op >= PUSH1 && op <= PUSH32 {
+ numbits := op - PUSH1 + 1
+ pc++
+ for ; numbits >= 8; numbits -= 8 {
+ bits.set8(pc) // 8
+ pc += 8
+ }
+ for ; numbits > 0; numbits-- {
+ bits.set(pc)
+ pc++
+ }
+ } else {
+ pc++
}
}
- return m
+ return bits
}
diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go
new file mode 100644
index 000000000..a64f90ed9
--- /dev/null
+++ b/core/vm/analysis_test.go
@@ -0,0 +1,53 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package vm
+
+import "testing"
+
+func TestJumpDestAnalysis(t *testing.T) {
+ tests := []struct {
+ code []byte
+ exp byte
+ which int
+ }{
+ {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0},
+ {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0},
+ {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0},
+ {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1},
+ {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0},
+ {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1},
+ {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0},
+ {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1},
+ {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0},
+ {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1},
+ {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0},
+ {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1},
+ {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2},
+ {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0},
+ {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1},
+ {[]byte{byte(PUSH32)}, 0x7F, 0},
+ {[]byte{byte(PUSH32)}, 0xFF, 1},
+ {[]byte{byte(PUSH32)}, 0xFF, 2},
+ }
+ for _, test := range tests {
+ ret := codeBitmap(test.code)
+ if ret[test.which] != test.exp {
+ t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
+ }
+ }
+
+}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 093c7d4c1..cbb5a03ce 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -38,7 +38,7 @@ type (
)
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
-func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, error) {
+func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
@@ -48,7 +48,7 @@ func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, erro
return RunPrecompiledContract(p, input, contract)
}
}
- return evm.interpreter.Run(snapshot, contract, input)
+ return evm.interpreter.Run(contract, input)
}
// Context provides the EVM with auxiliary information. Once provided
@@ -104,6 +104,10 @@ type EVM struct {
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
+ // callGasTemp holds the gas available for the current call. This is needed because the
+ // available gas is calculated in gasCall* according to the 63/64 rule and later
+ // applied in opCall*.
+ callGasTemp uint64
}
// NewEVM retutrns a new EVM . The returned EVM is not thread safe and should
@@ -167,7 +171,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
@@ -211,7 +215,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -244,7 +248,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -287,7 +291,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -334,7 +338,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, contractAddr, gas, nil
}
- ret, err = run(evm, snapshot, contract, nil)
+ ret, err = run(evm, contract, nil)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
// if the contract creation ran successfully and no errors were returned
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 0d8e295a5..ff109af57 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -342,19 +342,11 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
return 0, errGasUintOverflow
}
- cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil {
return 0, err
}
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called. This information is otherwise lost due to the dependency on *current*
- // available gas.
- stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
-
- if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow
}
return gas, nil
@@ -374,19 +366,11 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
return 0, errGasUintOverflow
}
- cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil {
return 0, err
}
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called. This information is otherwise lost due to the dependency on *current*
- // available gas.
- stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
-
- if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow
}
return gas, nil
@@ -436,18 +420,11 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
return 0, errGasUintOverflow
}
- cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil {
return 0, err
}
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called.
- stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
-
- if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow
}
return gas, nil
@@ -463,18 +440,11 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
return 0, errGasUintOverflow
}
- cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil {
return 0, err
}
- // Replace the stack item with the new gas calculation. This means that
- // either the original item is left on the stack or the item is replaced by:
- // (availableGas - gas) * 63 / 64
- // We replace the stack item so that it's available when the opCall instruction is
- // called.
- stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
-
- if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow
}
return gas, nil
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index b6d6e22c4..1d1585fca 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -603,24 +603,20 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
}
func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- gas := stack.pop().Uint64()
- // pop gas and value of the stack.
- addr, value := stack.pop(), stack.pop()
+ // Pop gas. The actual gas in in evm.callGasTemp.
+ evm.interpreter.intPool.put(stack.pop())
+ gas := evm.callGasTemp
+ // Pop other call parameters.
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.BigToAddress(addr)
value = math.U256(value)
- // pop input size and offset
- inOffset, inSize := stack.pop(), stack.pop()
- // pop return size and offset
- retOffset, retSize := stack.pop(), stack.pop()
-
- address := common.BigToAddress(addr)
-
- // Get the arguments from the memory
+ // Get the arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 {
gas += params.CallStipend
}
- ret, returnGas, err := evm.Call(contract, address, args, gas, value)
+ ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value)
if err != nil {
stack.push(new(big.Int))
} else {
@@ -636,25 +632,20 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
}
func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- gas := stack.pop().Uint64()
- // pop gas and value of the stack.
- addr, value := stack.pop(), stack.pop()
+ // Pop gas. The actual gas is in evm.callGasTemp.
+ evm.interpreter.intPool.put(stack.pop())
+ gas := evm.callGasTemp
+ // Pop other call parameters.
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.BigToAddress(addr)
value = math.U256(value)
- // pop input size and offset
- inOffset, inSize := stack.pop(), stack.pop()
- // pop return size and offset
- retOffset, retSize := stack.pop(), stack.pop()
-
- address := common.BigToAddress(addr)
-
- // Get the arguments from the memory
+ // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 {
gas += params.CallStipend
}
-
- ret, returnGas, err := evm.CallCode(contract, address, args, gas, value)
+ ret, returnGas, err := evm.CallCode(contract, toAddr, args, gas, value)
if err != nil {
stack.push(new(big.Int))
} else {
@@ -670,9 +661,13 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack
}
func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
-
- toAddr := common.BigToAddress(to)
+ // Pop gas. The actual gas is in evm.callGasTemp.
+ evm.interpreter.intPool.put(stack.pop())
+ gas := evm.callGasTemp
+ // Pop other call parameters.
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.BigToAddress(addr)
+ // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64())
ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas)
@@ -682,30 +677,25 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
stack.push(big.NewInt(1))
}
if err == nil || err == errExecutionReverted {
- memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
+ memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
contract.Gas += returnGas
- evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize)
+ evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- // pop gas
- gas := stack.pop().Uint64()
- // pop address
- addr := stack.pop()
- // pop input size and offset
- inOffset, inSize := stack.pop(), stack.pop()
- // pop return size and offset
- retOffset, retSize := stack.pop(), stack.pop()
-
- address := common.BigToAddress(addr)
-
- // Get the arguments from the memory
+ // Pop gas. The actual gas is in evm.callGasTemp.
+ evm.interpreter.intPool.put(stack.pop())
+ gas := evm.callGasTemp
+ // Pop other call parameters.
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.BigToAddress(addr)
+ // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64())
- ret, returnGas, err := evm.StaticCall(contract, address, args, gas)
+ ret, returnGas, err := evm.StaticCall(contract, toAddr, args, gas)
if err != nil {
stack.push(new(big.Int))
} else {
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 94b922c79..455f970dd 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -107,9 +107,9 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
// the return byte-slice and an error if one occurred.
//
// It's important to note that any errors returned by the interpreter should be
-// considered a revert-and-consume-all-gas operation. No error specific checks
-// should be handled to reduce complexity and errors further down the in.
-func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
+// considered a revert-and-consume-all-gas operation except for
+// errExecutionReverted which means revert-and-keep-gas-left.
+func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
@@ -138,16 +138,15 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
pc = uint64(0) // program counter
cost uint64
// copies used by tracer
- stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule
- pcCopy uint64 // needed for the deferred Tracer
+ pcCopy uint64 // needed for the deferred Tracer
gasCopy uint64 // for Tracer to log gas remaining before execution
- logged bool // deferred Tracer should ignore already logged steps
+ logged bool // deferred Tracer should ignore already logged steps
)
contract.Input = input
defer func() {
if err != nil && !logged && in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
}
}()
@@ -156,35 +155,25 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
// the execution of one of the operations or until the done flag is set by the
// parent context.
for atomic.LoadInt32(&in.evm.abort) == 0 {
- // Get the memory location of pc
- op = contract.GetOp(pc)
-
if in.cfg.Debug {
- logged = false
- pcCopy = uint64(pc)
- gasCopy = uint64(contract.Gas)
- stackCopy = newstack()
- for _, val := range stack.data {
- stackCopy.push(val)
- }
+ // Capture pre-execution values for tracing.
+ logged, pcCopy, gasCopy = false, pc, contract.Gas
}
- // get the operation from the jump table matching the opcode
+ // Get the operation from the jump table and validate the stack to ensure there are
+ // enough stack items available to perform the operation.
+ op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
- if err := in.enforceRestrictions(op, operation, stack); err != nil {
- return nil, err
- }
-
- // if the op is invalid abort the process and return an error
if !operation.valid {
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
}
-
- // validate the stack and make sure there enough stack items available
- // to perform the operation
if err := operation.validateStack(stack); err != nil {
return nil, err
}
+ // If the operation is valid, enforce and write restrictions
+ if err := in.enforceRestrictions(op, operation, stack); err != nil {
+ return nil, err
+ }
var memorySize uint64
// calculate the new memory size and expand the memory to fit
@@ -214,7 +203,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
}
if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
logged = true
}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 9ef192fdf..a1c5ad9c6 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -42,12 +42,12 @@ type operation struct {
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc
- halts bool // indicates whether the operation shoult halt further execution
+ halts bool // indicates whether the operation should halt further execution
jumps bool // indicates whether the program counter should not increment
writes bool // determines whether this a state modifying operation
valid bool // indication whether the retrieved operation is valid and known
reverts bool // determines whether the operation reverts state (implicitly halts)
- returns bool // determines whether the opertions sets the return data content
+ returns bool // determines whether the operations sets the return data content
}
var (
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 623c0d563..75309da92 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -45,7 +45,6 @@ type LogConfig struct {
DisableMemory bool // disable memory capture
DisableStack bool // disable stack capture
DisableStorage bool // disable storage capture
- FullStorage bool // show full storage (slow)
Limit int // maximum length of output, but zero means unlimited
}
@@ -136,14 +135,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
)
l.changedValues[contract.Address()][address] = value
}
- // copy a snapstot of the current memory state to a new buffer
+ // Copy a snapstot of the current memory state to a new buffer
var mem []byte
if !l.cfg.DisableMemory {
mem = make([]byte, len(memory.Data()))
copy(mem, memory.Data())
}
-
- // copy a snapshot of the current stack state to a new buffer
+ // Copy a snapshot of the current stack state to a new buffer
var stck []*big.Int
if !l.cfg.DisableStack {
stck = make([]*big.Int, len(stack.Data()))
@@ -151,26 +149,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
stck[i] = new(big.Int).Set(item)
}
}
-
- // Copy the storage based on the settings specified in the log config. If full storage
- // is disabled (default) we can use the simple Storage.Copy method, otherwise we use
- // the state object to query for all values (slow process).
+ // Copy a snapshot of the current storage to a new container
var storage Storage
if !l.cfg.DisableStorage {
- if l.cfg.FullStorage {
- storage = make(Storage)
- // Get the contract account and loop over each storage entry. This may involve looping over
- // the trie and is a very expensive process.
-
- env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool {
- storage[key] = value
- // Return true, indicating we'd like to continue.
- return true
- })
- } else {
- // copy a snapshot of the current storage to a new container.
- storage = l.changedValues[contract.Address()].Copy()
- }
+ storage = l.changedValues[contract.Address()].Copy()
}
// create a new snaptshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index b6fa31132..915f7177e 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -63,32 +63,8 @@ func TestStoreCapture(t *testing.T) {
if len(logger.changedValues[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
}
-
exp := common.BigToHash(big.NewInt(1))
if logger.changedValues[contract.Address()][index] != exp {
t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index])
}
}
-
-func TestStorageCapture(t *testing.T) {
- t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
- var (
- ref = &dummyContractRef{}
- contract = NewContract(ref, ref, new(big.Int), 0)
- env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
- logger = NewStructLogger(nil)
- mem = NewMemory()
- stack = newstack()
- )
-
- logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil)
- if ref.calledForEach {
- t.Error("didn't expect for each to be called")
- }
-
- logger = NewStructLogger(&LogConfig{FullStorage: true})
- logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil)
- if !ref.calledForEach {
- t.Error("expected for each to be called")
- }
-}