Removed defer/panic. #503

pull/587/head
obscuren 10 years ago
parent 00f8319faf
commit 8a22cd5e6c
  1. 162
      core/vm/gas.go
  2. 18
      core/vm/memory.go
  3. 10
      core/vm/stack.go
  4. 66
      core/vm/vm.go

@ -1,11 +1,9 @@
package vm package vm
import "math/big" import (
"fmt"
type req struct { "math/big"
stack int )
gas *big.Int
}
var ( var (
GasQuickStep = big.NewInt(2) GasQuickStep = big.NewInt(2)
@ -56,75 +54,30 @@ var (
GasCopyWord = big.NewInt(3) GasCopyWord = big.NewInt(3)
) )
var _baseCheck = map[OpCode]req{ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// Req stack Gas price // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
ADD: {2, GasFastestStep}, // PUSH is also allowed to calculate the same price for all PUSHes
LT: {2, GasFastestStep}, // DUP requirements are handled elsewhere (except for the stack limit check)
GT: {2, GasFastestStep}, if op >= PUSH1 && op <= PUSH32 {
SLT: {2, GasFastestStep}, op = PUSH1
SGT: {2, GasFastestStep}, }
EQ: {2, GasFastestStep}, if op >= SWAP1 && op <= SWAP16 {
ISZERO: {1, GasFastestStep}, op = SWAP1
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) {
if r, ok := _baseCheck[op]; ok { 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) gas.Add(gas, r.gas)
} }
return nil
} }
func toWordSize(size *big.Int) *big.Int { func toWordSize(size *big.Int) *big.Int {
@ -133,3 +86,74 @@ func toWordSize(size *big.Int) *big.Int {
tmp.Div(tmp, u256(32)) tmp.Div(tmp, u256(32))
return tmp 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},
}

@ -15,17 +15,21 @@ func NewMemory() *Memory {
} }
func (m *Memory) Set(offset, size uint64, value []byte) { func (m *Memory) Set(offset, size uint64, value []byte) {
// If the length of the store is 0 this is a complete failure // length of store may never be less than offset + size.
// memory size is set prior to calling this method so enough size // The store should be resized PRIOR to setting the memory
// should always be available. if size > uint64(len(m.store)) {
if len(m.store) == 0 {
panic("INVALID memory: store empty") panic("INVALID memory: store empty")
} }
value = common.RightPadBytes(value, int(size)) // 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)))
}
/*
totSize := offset + size totSize := offset + size
lenSize := int64(len(m.store) - 1) lenSize := uint64(len(m.store) - 1)
if totSize > lenSize { if totSize > lenSize {
// Calculate the diff between the sizes // Calculate the diff between the sizes
diff := totSize - lenSize diff := totSize - lenSize
@ -36,7 +40,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
m.store = append(m.store, newSlice...) m.store = append(m.store, newSlice...)
} }
} }
copy(m.store[offset:offset+size], value) */
} }
func (m *Memory) Resize(size uint64) { func (m *Memory) Resize(size uint64) {

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

@ -45,6 +45,7 @@ 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() 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 { if self.Recoverable {
// Recover from any require exception // Recover from any require exception
defer func() { defer func() {
@ -59,6 +60,17 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
} }
}() }()
} }
*/
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 context.CodeAddr != nil {
if p := Precompiled[context.CodeAddr.Str()]; p != 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 step = 0
statedb = self.env.State() statedb = self.env.State()
jump = func(from uint64, to *big.Int) { jump = func(from uint64, to *big.Int) error {
p := to.Uint64() p := to.Uint64()
nop := context.GetOp(p) nop := context.GetOp(p)
if !destinations.Has(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) self.Printf(" ~> %v", to)
pc = to.Uint64() pc = to.Uint64()
self.Endl() self.Endl()
return nil
} }
) )
@ -105,7 +119,10 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
op = context.GetOp(pc) op = context.GetOp(pc)
self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) 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) 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()) self.Printf(" {0x%x : 0x%x}", loc, val.Bytes())
case JUMP: case JUMP:
jump(pc, stack.pop()) if err := jump(pc, stack.pop()); err != nil {
return nil, err
}
continue continue
case JUMPI: case JUMPI:
pos, cond := stack.pop(), stack.pop() pos, cond := stack.pop(), stack.pop()
if cond.Cmp(common.BigTrue) >= 0 { if cond.Cmp(common.BigTrue) >= 0 {
jump(pc, pos) if err := jump(pc, pos); err != nil {
return nil, err
}
continue continue
} }
@ -720,7 +741,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
default: default:
self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() 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++ 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 ( var (
gas = new(big.Int) gas = new(big.Int)
newMemSize *big.Int = 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 // stack Check, memory resize & gas phase
switch op { 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: case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2) n := int(op - SWAP1 + 2)
stack.require(n) err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep) gas.Set(GasFastestStep)
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1) n := int(op - DUP1 + 1)
stack.require(n) err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep) gas.Set(GasFastestStep)
case LOG0, LOG1, LOG2, LOG3, LOG4: case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0) 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] 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: case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte)) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte))
case SSTORE: case SSTORE:
stack.require(2) err := stack.require(2)
if err != nil {
return nil, nil, err
}
var g *big.Int var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] 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) { 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) tmp := new(big.Int).Set(context.Gas)
panic(OOG(gas, tmp).Error()) return nil, OOG(gas, tmp)
} }
} }

Loading…
Cancel
Save