Removed defer/panic. #503

This commit is contained in:
obscuren 2015-03-27 16:09:57 +01:00
parent 00f8319faf
commit 8a22cd5e6c
4 changed files with 181 additions and 121 deletions

View File

@ -1,11 +1,9 @@
package vm
import "math/big"
type req struct {
stack int
gas *big.Int
}
import (
"fmt"
"math/big"
)
var (
GasQuickStep = big.NewInt(2)
@ -56,75 +54,30 @@ var (
GasCopyWord = big.NewInt(3)
)
var _baseCheck = map[OpCode]req{
// Req stack Gas price
ADD: {2, GasFastestStep},
LT: {2, GasFastestStep},
GT: {2, GasFastestStep},
SLT: {2, GasFastestStep},
SGT: {2, GasFastestStep},
EQ: {2, GasFastestStep},
ISZERO: {1, GasFastestStep},
SUB: {2, GasFastestStep},
AND: {2, GasFastestStep},
OR: {2, GasFastestStep},
XOR: {2, GasFastestStep},
NOT: {1, GasFastestStep},
BYTE: {2, GasFastestStep},
CALLDATALOAD: {1, GasFastestStep},
CALLDATACOPY: {3, GasFastestStep},
MLOAD: {1, GasFastestStep},
MSTORE: {2, GasFastestStep},
MSTORE8: {2, GasFastestStep},
CODECOPY: {3, GasFastestStep},
MUL: {2, GasFastStep},
DIV: {2, GasFastStep},
SDIV: {2, GasFastStep},
MOD: {2, GasFastStep},
SMOD: {2, GasFastStep},
SIGNEXTEND: {2, GasFastStep},
ADDMOD: {3, GasMidStep},
MULMOD: {3, GasMidStep},
JUMP: {1, GasMidStep},
JUMPI: {2, GasSlowStep},
EXP: {2, GasSlowStep},
ADDRESS: {0, GasQuickStep},
ORIGIN: {0, GasQuickStep},
CALLER: {0, GasQuickStep},
CALLVALUE: {0, GasQuickStep},
CODESIZE: {0, GasQuickStep},
GASPRICE: {0, GasQuickStep},
COINBASE: {0, GasQuickStep},
TIMESTAMP: {0, GasQuickStep},
NUMBER: {0, GasQuickStep},
CALLDATASIZE: {0, GasQuickStep},
DIFFICULTY: {0, GasQuickStep},
GASLIMIT: {0, GasQuickStep},
POP: {0, GasQuickStep},
PC: {0, GasQuickStep},
MSIZE: {0, GasQuickStep},
GAS: {0, GasQuickStep},
BLOCKHASH: {1, GasExtStep},
BALANCE: {0, GasExtStep},
EXTCODESIZE: {1, GasExtStep},
EXTCODECOPY: {4, GasExtStep},
SLOAD: {1, GasStorageGet},
SSTORE: {2, Zero},
SHA3: {1, GasSha3Base},
CREATE: {3, GasCreate},
CALL: {7, GasCall},
CALLCODE: {7, GasCall},
JUMPDEST: {0, GasJumpDest},
SUICIDE: {1, Zero},
RETURN: {2, Zero},
}
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
// PUSH is also allowed to calculate the same price for all PUSHes
// DUP requirements are handled elsewhere (except for the stack limit check)
if op >= PUSH1 && op <= PUSH32 {
op = PUSH1
}
if op >= SWAP1 && op <= SWAP16 {
op = SWAP1
}
func baseCheck(op OpCode, stack *stack, gas *big.Int) {
if r, ok := _baseCheck[op]; ok {
stack.require(r.stack)
err := stack.require(r.stackPop)
if err != nil {
return err
}
if r.stackPush && len(stack.data)-r.stackPop == 1024 {
return fmt.Errorf("stack limit reached (%d)", maxStack)
}
gas.Add(gas, r.gas)
}
return nil
}
func toWordSize(size *big.Int) *big.Int {
@ -133,3 +86,74 @@ func toWordSize(size *big.Int) *big.Int {
tmp.Div(tmp, u256(32))
return tmp
}
type req struct {
stackPop int
gas *big.Int
stackPush bool
}
var _baseCheck = map[OpCode]req{
// opcode | stack pop | gas price | stack push
ADD: {2, GasFastestStep, true},
LT: {2, GasFastestStep, true},
GT: {2, GasFastestStep, true},
SLT: {2, GasFastestStep, true},
SGT: {2, GasFastestStep, true},
EQ: {2, GasFastestStep, true},
ISZERO: {1, GasFastestStep, true},
SUB: {2, GasFastestStep, true},
AND: {2, GasFastestStep, true},
OR: {2, GasFastestStep, true},
XOR: {2, GasFastestStep, true},
NOT: {1, GasFastestStep, true},
BYTE: {2, GasFastestStep, true},
CALLDATALOAD: {1, GasFastestStep, true},
CALLDATACOPY: {3, GasFastestStep, true},
MLOAD: {1, GasFastestStep, true},
MSTORE: {2, GasFastestStep, false},
MSTORE8: {2, GasFastestStep, false},
CODECOPY: {3, GasFastestStep, false},
MUL: {2, GasFastStep, true},
DIV: {2, GasFastStep, true},
SDIV: {2, GasFastStep, true},
MOD: {2, GasFastStep, true},
SMOD: {2, GasFastStep, true},
SIGNEXTEND: {2, GasFastStep, true},
ADDMOD: {3, GasMidStep, true},
MULMOD: {3, GasMidStep, true},
JUMP: {1, GasMidStep, false},
JUMPI: {2, GasSlowStep, false},
EXP: {2, GasSlowStep, true},
ADDRESS: {0, GasQuickStep, true},
ORIGIN: {0, GasQuickStep, true},
CALLER: {0, GasQuickStep, true},
CALLVALUE: {0, GasQuickStep, true},
CODESIZE: {0, GasQuickStep, true},
GASPRICE: {0, GasQuickStep, true},
COINBASE: {0, GasQuickStep, true},
TIMESTAMP: {0, GasQuickStep, true},
NUMBER: {0, GasQuickStep, true},
CALLDATASIZE: {0, GasQuickStep, true},
DIFFICULTY: {0, GasQuickStep, true},
GASLIMIT: {0, GasQuickStep, true},
POP: {1, GasQuickStep, false},
PC: {0, GasQuickStep, true},
MSIZE: {0, GasQuickStep, true},
GAS: {0, GasQuickStep, true},
BLOCKHASH: {1, GasExtStep, true},
BALANCE: {0, GasExtStep, true},
EXTCODESIZE: {1, GasExtStep, true},
EXTCODECOPY: {4, GasExtStep, false},
SLOAD: {1, GasStorageGet, true},
SSTORE: {2, Zero, false},
SHA3: {1, GasSha3Base, true},
CREATE: {3, GasCreate, true},
CALL: {7, GasCall, true},
CALLCODE: {7, GasCall, true},
JUMPDEST: {0, GasJumpDest, false},
SUICIDE: {1, Zero, false},
RETURN: {2, Zero, false},
PUSH1: {0, GasFastStep, true},
DUP1: {0, Zero, true},
}

View File

@ -15,28 +15,32 @@ func NewMemory() *Memory {
}
func (m *Memory) Set(offset, size uint64, value []byte) {
// If the length of the store is 0 this is a complete failure
// memory size is set prior to calling this method so enough size
// should always be available.
if len(m.store) == 0 {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if size > uint64(len(m.store)) {
panic("INVALID memory: store empty")
}
value = common.RightPadBytes(value, int(size))
totSize := offset + size
lenSize := int64(len(m.store) - 1)
if totSize > lenSize {
// Calculate the diff between the sizes
diff := totSize - lenSize
if diff > 0 {
// Create a new empty slice and append it
newSlice := make([]byte, diff-1)
// Resize slice
m.store = append(m.store, newSlice...)
}
// It's possible the offset is greater than 0 and size equals 0. This is because
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
if size > 0 {
copy(m.store[offset:offset+size], common.RightPadBytes(value, int(size)))
}
copy(m.store[offset:offset+size], value)
/*
totSize := offset + size
lenSize := uint64(len(m.store) - 1)
if totSize > lenSize {
// Calculate the diff between the sizes
diff := totSize - lenSize
if diff > 0 {
// Create a new empty slice and append it
newSlice := make([]byte, diff-1)
// Resize slice
m.store = append(m.store, newSlice...)
}
}
*/
}
func (m *Memory) Resize(size uint64) {

View File

@ -17,10 +17,7 @@ type stack struct {
}
func (st *stack) push(d *big.Int) {
if len(st.data) == maxStack {
panic(fmt.Sprintf("stack limit reached (%d)", maxStack))
}
// NOTE push limit (1024) is checked in baseCheck
stackItem := new(big.Int).Set(d)
if len(st.data) > st.ptr {
st.data[st.ptr] = stackItem
@ -52,10 +49,11 @@ func (st *stack) peek() *big.Int {
return st.data[st.len()-1]
}
func (st *stack) require(n int) {
func (st *stack) require(n int) error {
if st.len() < n {
panic(fmt.Sprintf("stack underflow (%d <=> %d)", len(st.data), n))
return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
}
return nil
}
func (st *stack) Print() {

View File

@ -45,20 +45,32 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl()
if self.Recoverable {
// Recover from any require exception
defer func() {
if r := recover(); r != nil {
self.Printf(" %v", r).Endl()
/*
if self.Recoverable {
// Recover from any require exception
defer func() {
if r := recover(); r != nil {
self.Printf(" %v", r).Endl()
context.UseGas(context.Gas)
context.UseGas(context.Gas)
ret = context.Return(nil)
ret = context.Return(nil)
err = fmt.Errorf("%v", r)
}
}()
}
err = fmt.Errorf("%v", r)
}
}()
}
*/
defer func() {
if err != nil {
self.Printf(" %v", err).Endl()
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
context.UseGas(context.Gas)
ret = context.Return(nil)
}
}()
if context.CodeAddr != nil {
if p := Precompiled[context.CodeAddr.Str()]; p != nil {
@ -76,18 +88,20 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
step = 0
statedb = self.env.State()
jump = func(from uint64, to *big.Int) {
jump = func(from uint64, to *big.Int) error {
p := to.Uint64()
nop := context.GetOp(p)
if !destinations.Has(p) {
panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p))
return fmt.Errorf("invalid jump destination (%v) %v", nop, p)
}
self.Printf(" ~> %v", to)
pc = to.Uint64()
self.Endl()
return nil
}
)
@ -105,7 +119,10 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
op = context.GetOp(pc)
self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len())
newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
self.Printf("(g) %-3v (%v)", gas, context.Gas)
@ -600,14 +617,18 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf(" {0x%x : 0x%x}", loc, val.Bytes())
case JUMP:
jump(pc, stack.pop())
if err := jump(pc, stack.pop()); err != nil {
return nil, err
}
continue
case JUMPI:
pos, cond := stack.pop(), stack.pop()
if cond.Cmp(common.BigTrue) >= 0 {
jump(pc, pos)
if err := jump(pc, pos); err != nil {
return nil, err
}
continue
}
@ -720,7 +741,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
default:
self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl()
panic(fmt.Errorf("Invalid opcode %x", op))
return nil, fmt.Errorf("Invalid opcode %x", op)
}
pc++
@ -729,28 +750,38 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
}
}
func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int) {
func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
)
baseCheck(op, stack, gas)
err := baseCheck(op, stack, gas)
if err != nil {
return nil, nil, err
}
// stack Check, memory resize & gas phase
switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
gas.Set(GasFastestStep)
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2)
stack.require(n)
err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep)
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1)
stack.require(n)
err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep)
case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0)
stack.require(n + 2)
err := stack.require(n + 2)
if err != nil {
return nil, nil, err
}
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
@ -762,7 +793,10 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte))
case SSTORE:
stack.require(2)
err := stack.require(2)
if err != nil {
return nil, nil, err
}
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
@ -853,7 +887,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
}
}
return newMemSize, gas
return newMemSize, gas, nil
}
func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
@ -869,7 +903,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *
tmp := new(big.Int).Set(context.Gas)
panic(OOG(gas, tmp).Error())
return nil, OOG(gas, tmp)
}
}