2014-02-24 11:10:45 +00:00
|
|
|
package ethchain
|
|
|
|
|
|
|
|
import (
|
2014-04-11 17:29:57 +00:00
|
|
|
"fmt"
|
2014-06-23 11:54:10 +00:00
|
|
|
"github.com/ethereum/eth-go/ethlog"
|
2014-06-26 17:45:57 +00:00
|
|
|
"github.com/ethereum/eth-go/ethutil"
|
2014-06-13 10:45:11 +00:00
|
|
|
"math"
|
2014-02-24 11:10:45 +00:00
|
|
|
"math/big"
|
|
|
|
)
|
|
|
|
|
2014-06-23 11:54:10 +00:00
|
|
|
var vmlogger = ethlog.NewLogger("VM")
|
|
|
|
|
2014-03-24 12:20:34 +00:00
|
|
|
var (
|
|
|
|
GasStep = big.NewInt(1)
|
|
|
|
GasSha = big.NewInt(20)
|
|
|
|
GasSLoad = big.NewInt(20)
|
|
|
|
GasSStore = big.NewInt(100)
|
|
|
|
GasBalance = big.NewInt(20)
|
|
|
|
GasCreate = big.NewInt(100)
|
|
|
|
GasCall = big.NewInt(20)
|
|
|
|
GasMemory = big.NewInt(1)
|
2014-06-13 10:45:11 +00:00
|
|
|
GasData = big.NewInt(5)
|
2014-05-22 15:35:26 +00:00
|
|
|
GasTx = big.NewInt(500)
|
2014-03-24 12:20:34 +00:00
|
|
|
)
|
|
|
|
|
2014-05-28 10:05:46 +00:00
|
|
|
func CalculateTxGas(initSize *big.Int) *big.Int {
|
2014-04-27 14:50:44 +00:00
|
|
|
totalGas := new(big.Int)
|
|
|
|
|
2014-05-28 10:05:46 +00:00
|
|
|
txTotalBytes := new(big.Int).Set(initSize)
|
2014-04-27 14:50:44 +00:00
|
|
|
txTotalBytes.Div(txTotalBytes, ethutil.Big32)
|
|
|
|
totalGas.Add(totalGas, new(big.Int).Mul(txTotalBytes, GasSStore))
|
|
|
|
|
|
|
|
return totalGas
|
|
|
|
}
|
|
|
|
|
2014-02-24 11:10:45 +00:00
|
|
|
type Vm struct {
|
|
|
|
txPool *TxPool
|
|
|
|
// Stack for processing contracts
|
|
|
|
stack *Stack
|
|
|
|
// non-persistent key/value memory storage
|
|
|
|
mem map[string]*big.Int
|
|
|
|
|
|
|
|
vars RuntimeVars
|
2014-03-20 18:50:53 +00:00
|
|
|
|
|
|
|
state *State
|
2014-04-25 23:47:55 +00:00
|
|
|
|
|
|
|
stateManager *StateManager
|
2014-06-17 16:49:26 +00:00
|
|
|
|
|
|
|
Verbose bool
|
|
|
|
|
|
|
|
logStr string
|
2014-06-19 22:41:28 +00:00
|
|
|
|
|
|
|
err error
|
2014-02-24 11:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RuntimeVars struct {
|
2014-04-11 17:29:57 +00:00
|
|
|
Origin []byte
|
2014-06-19 11:45:29 +00:00
|
|
|
Block *Block
|
|
|
|
BlockNumber *big.Int
|
2014-04-11 17:29:57 +00:00
|
|
|
PrevHash []byte
|
|
|
|
Coinbase []byte
|
|
|
|
Time int64
|
|
|
|
Diff *big.Int
|
|
|
|
TxData []string
|
2014-05-07 09:05:49 +00:00
|
|
|
Value *big.Int
|
2014-02-24 11:10:45 +00:00
|
|
|
}
|
|
|
|
|
2014-06-17 16:49:26 +00:00
|
|
|
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
|
|
|
|
if self.Verbose {
|
|
|
|
self.logStr += fmt.Sprintf(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Vm) Endl() *Vm {
|
|
|
|
if self.Verbose {
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Infoln(self.logStr)
|
2014-06-17 16:49:26 +00:00
|
|
|
self.logStr = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return self
|
|
|
|
}
|
|
|
|
|
2014-04-25 23:47:55 +00:00
|
|
|
func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
|
|
|
|
return &Vm{vars: vars, state: state, stateManager: stateManager}
|
2014-03-20 18:50:53 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 10:54:36 +00:00
|
|
|
var Pow256 = ethutil.BigPow(2, 256)
|
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
var isRequireError = false
|
|
|
|
|
|
|
|
func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) {
|
|
|
|
// Recover from any require exception
|
|
|
|
defer func() {
|
2014-06-19 11:45:29 +00:00
|
|
|
if r := recover(); r != nil {
|
2014-04-15 20:16:38 +00:00
|
|
|
ret = closure.Return(nil)
|
|
|
|
err = fmt.Errorf("%v", r)
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Errorln("vm err", err)
|
2014-04-15 20:16:38 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Debugf("(~) %x gas: %v (d) %x\n", closure.object.Address(), closure.Gas, closure.Args)
|
2014-03-20 16:26:07 +00:00
|
|
|
|
2014-06-26 09:26:42 +00:00
|
|
|
var (
|
|
|
|
op OpCode
|
|
|
|
|
|
|
|
mem = &Memory{}
|
|
|
|
stack = NewStack()
|
|
|
|
pc = big.NewInt(0)
|
|
|
|
step = 0
|
|
|
|
prevStep = 0
|
|
|
|
require = func(m int) {
|
|
|
|
if stack.Len() < m {
|
|
|
|
isRequireError = true
|
|
|
|
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
|
|
|
|
}
|
2014-04-15 20:16:38 +00:00
|
|
|
}
|
2014-06-26 09:26:42 +00:00
|
|
|
)
|
2014-03-20 16:26:07 +00:00
|
|
|
|
|
|
|
for {
|
2014-05-28 10:05:46 +00:00
|
|
|
prevStep = step
|
2014-04-23 22:00:50 +00:00
|
|
|
// The base for all big integer arithmetic
|
|
|
|
base := new(big.Int)
|
|
|
|
|
2014-03-20 16:26:07 +00:00
|
|
|
step++
|
|
|
|
// Get the memory location of pc
|
2014-04-09 16:27:54 +00:00
|
|
|
val := closure.Get(pc)
|
2014-03-20 16:26:07 +00:00
|
|
|
// Get the opcode (it must be an opcode!)
|
2014-06-26 09:26:42 +00:00
|
|
|
op = OpCode(val.Uint())
|
2014-06-17 09:06:06 +00:00
|
|
|
|
2014-03-24 12:20:34 +00:00
|
|
|
gas := new(big.Int)
|
2014-06-13 14:06:27 +00:00
|
|
|
addStepGasUsage := func(amount *big.Int) {
|
2014-06-19 22:41:28 +00:00
|
|
|
if amount.Cmp(ethutil.Big0) >= 0 {
|
|
|
|
gas.Add(gas, amount)
|
|
|
|
}
|
2014-03-24 12:20:34 +00:00
|
|
|
}
|
|
|
|
|
2014-06-13 14:06:27 +00:00
|
|
|
addStepGasUsage(GasStep)
|
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
var newMemSize uint64 = 0
|
2014-03-24 12:20:34 +00:00
|
|
|
switch op {
|
2014-06-13 14:06:27 +00:00
|
|
|
case STOP:
|
2014-06-17 16:05:46 +00:00
|
|
|
gas.Set(ethutil.Big0)
|
2014-06-13 14:06:27 +00:00
|
|
|
case SUICIDE:
|
2014-06-17 16:05:46 +00:00
|
|
|
gas.Set(ethutil.Big0)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SLOAD:
|
2014-06-13 14:06:27 +00:00
|
|
|
gas.Set(GasSLoad)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SSTORE:
|
2014-03-24 12:20:34 +00:00
|
|
|
var mult *big.Int
|
|
|
|
y, x := stack.Peekn()
|
|
|
|
val := closure.GetMem(x)
|
|
|
|
if val.IsEmpty() && len(y.Bytes()) > 0 {
|
|
|
|
mult = ethutil.Big2
|
|
|
|
} else if !val.IsEmpty() && len(y.Bytes()) == 0 {
|
|
|
|
mult = ethutil.Big0
|
|
|
|
} else {
|
|
|
|
mult = ethutil.Big1
|
|
|
|
}
|
2014-06-13 14:06:27 +00:00
|
|
|
gas = new(big.Int).Mul(mult, GasSStore)
|
2014-05-28 10:05:46 +00:00
|
|
|
case BALANCE:
|
2014-06-13 14:06:27 +00:00
|
|
|
gas.Set(GasBalance)
|
2014-06-13 10:45:11 +00:00
|
|
|
case MSTORE:
|
|
|
|
require(2)
|
|
|
|
newMemSize = stack.Peek().Uint64() + 32
|
2014-06-13 14:06:27 +00:00
|
|
|
case MLOAD:
|
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
case MSTORE8:
|
|
|
|
require(2)
|
|
|
|
newMemSize = stack.Peek().Uint64() + 1
|
|
|
|
case RETURN:
|
|
|
|
require(2)
|
|
|
|
|
|
|
|
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
|
|
|
|
case SHA3:
|
|
|
|
require(2)
|
|
|
|
|
2014-06-13 14:06:27 +00:00
|
|
|
gas.Set(GasSha)
|
2014-06-13 10:45:11 +00:00
|
|
|
|
|
|
|
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
|
|
|
|
case CALLDATACOPY:
|
2014-04-27 14:50:44 +00:00
|
|
|
require(3)
|
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
|
|
|
|
case CODECOPY:
|
|
|
|
require(3)
|
2014-04-27 14:50:44 +00:00
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALL:
|
2014-06-13 10:45:11 +00:00
|
|
|
require(7)
|
2014-06-13 14:06:27 +00:00
|
|
|
gas.Set(GasCall)
|
2014-06-19 11:45:29 +00:00
|
|
|
addStepGasUsage(stack.data[stack.Len()-1])
|
2014-06-13 10:45:11 +00:00
|
|
|
|
|
|
|
x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
|
|
|
|
y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
|
|
|
|
|
|
|
|
newMemSize = uint64(math.Max(float64(x), float64(y)))
|
|
|
|
case CREATE:
|
|
|
|
require(3)
|
2014-06-13 14:06:27 +00:00
|
|
|
gas.Set(GasCreate)
|
2014-06-13 10:45:11 +00:00
|
|
|
|
|
|
|
newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
|
2014-03-24 12:20:34 +00:00
|
|
|
}
|
2014-03-20 16:26:07 +00:00
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
newMemSize = (newMemSize + 31) / 32 * 32
|
|
|
|
if newMemSize > uint64(mem.Len()) {
|
|
|
|
m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
|
2014-06-13 14:06:27 +00:00
|
|
|
addStepGasUsage(big.NewInt(int64(m)))
|
2014-06-13 10:45:11 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 10:05:46 +00:00
|
|
|
if !closure.UseGas(gas) {
|
2014-06-19 11:45:29 +00:00
|
|
|
err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas)
|
2014-03-30 23:03:28 +00:00
|
|
|
|
2014-06-19 11:45:29 +00:00
|
|
|
closure.UseGas(closure.Gas)
|
|
|
|
|
|
|
|
return closure.Return(nil), err
|
2014-03-20 16:26:07 +00:00
|
|
|
}
|
2014-04-19 23:31:01 +00:00
|
|
|
|
2014-06-19 22:41:28 +00:00
|
|
|
vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
|
2014-06-17 16:49:26 +00:00
|
|
|
vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
|
|
|
|
|
2014-06-13 10:45:11 +00:00
|
|
|
mem.Resize(newMemSize)
|
|
|
|
|
2014-03-20 16:26:07 +00:00
|
|
|
switch op {
|
2014-05-28 10:05:46 +00:00
|
|
|
case LOG:
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Print()
|
|
|
|
mem.Print()
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x20 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case ADD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" %v + %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Add(y, x)
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" = %v", base)
|
2014-03-21 10:54:36 +00:00
|
|
|
// Pop result back on the stack
|
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SUB:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" %v - %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Sub(y, x)
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" = %v", base)
|
2014-03-21 10:54:36 +00:00
|
|
|
// Pop result back on the stack
|
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case MUL:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" %v * %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Mul(y, x)
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" = %v", base)
|
2014-03-21 10:54:36 +00:00
|
|
|
// Pop result back on the stack
|
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case DIV:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" %v / %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Div(y, x)
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" = %v", base)
|
2014-03-21 10:54:36 +00:00
|
|
|
// Pop result back on the stack
|
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SDIV:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
|
|
|
// n > 2**255
|
|
|
|
if x.Cmp(Pow256) > 0 {
|
|
|
|
x.Sub(Pow256, x)
|
|
|
|
}
|
|
|
|
if y.Cmp(Pow256) > 0 {
|
|
|
|
y.Sub(Pow256, y)
|
|
|
|
}
|
|
|
|
z := new(big.Int)
|
|
|
|
z.Div(x, y)
|
|
|
|
if z.Cmp(Pow256) > 0 {
|
|
|
|
z.Sub(Pow256, z)
|
|
|
|
}
|
|
|
|
// Push result on to the stack
|
|
|
|
stack.Push(z)
|
2014-05-28 10:05:46 +00:00
|
|
|
case MOD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" %v %% %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Mod(y, x)
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" = %v", base)
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SMOD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
|
|
|
// n > 2**255
|
|
|
|
if x.Cmp(Pow256) > 0 {
|
|
|
|
x.Sub(Pow256, x)
|
|
|
|
}
|
|
|
|
if y.Cmp(Pow256) > 0 {
|
|
|
|
y.Sub(Pow256, y)
|
|
|
|
}
|
|
|
|
z := new(big.Int)
|
|
|
|
z.Mod(x, y)
|
|
|
|
if z.Cmp(Pow256) > 0 {
|
|
|
|
z.Sub(Pow256, z)
|
|
|
|
}
|
|
|
|
// Push result on to the stack
|
|
|
|
stack.Push(z)
|
2014-05-28 10:05:46 +00:00
|
|
|
case EXP:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
vm.Printf(" %v ** %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
base.Exp(y, x, Pow256)
|
2014-03-21 10:54:36 +00:00
|
|
|
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" = %v", base)
|
|
|
|
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case NEG:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 10:54:36 +00:00
|
|
|
base.Sub(Pow256, stack.Pop())
|
|
|
|
stack.Push(base)
|
2014-05-28 10:05:46 +00:00
|
|
|
case LT:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-06-20 18:11:40 +00:00
|
|
|
x, y := stack.Popn()
|
|
|
|
vm.Printf(" %v < %v", y, x)
|
2014-03-21 10:54:36 +00:00
|
|
|
// x < y
|
2014-06-20 18:11:40 +00:00
|
|
|
if y.Cmp(x) < 0 {
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Push(ethutil.BigTrue)
|
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigFalse)
|
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case GT:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-06-20 18:11:40 +00:00
|
|
|
x, y := stack.Popn()
|
|
|
|
vm.Printf(" %v > %v", y, x)
|
2014-06-17 22:25:58 +00:00
|
|
|
|
2014-03-21 10:54:36 +00:00
|
|
|
// x > y
|
2014-06-20 18:11:40 +00:00
|
|
|
if y.Cmp(x) > 0 {
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Push(ethutil.BigTrue)
|
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigFalse)
|
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case EQ:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 10:54:36 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-17 22:36:23 +00:00
|
|
|
vm.Printf(" %v == %v", y, x)
|
|
|
|
|
2014-03-30 16:55:51 +00:00
|
|
|
// x == y
|
|
|
|
if x.Cmp(y) == 0 {
|
|
|
|
stack.Push(ethutil.BigTrue)
|
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigFalse)
|
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case NOT:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-30 16:55:51 +00:00
|
|
|
x := stack.Pop()
|
2014-06-23 09:26:51 +00:00
|
|
|
if x.Cmp(ethutil.BigFalse) > 0 {
|
2014-03-21 10:54:36 +00:00
|
|
|
stack.Push(ethutil.BigFalse)
|
2014-06-23 09:26:51 +00:00
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigTrue)
|
2014-03-21 10:54:36 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x10 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case AND:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-04-09 12:08:18 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-17 22:36:23 +00:00
|
|
|
vm.Printf(" %v & %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
stack.Push(base.And(y, x))
|
2014-05-28 10:05:46 +00:00
|
|
|
case OR:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-04-09 12:08:18 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-17 22:36:23 +00:00
|
|
|
vm.Printf(" %v | %v", y, x)
|
|
|
|
|
2014-06-17 22:32:48 +00:00
|
|
|
stack.Push(base.Or(y, x))
|
2014-05-28 10:05:46 +00:00
|
|
|
case XOR:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-04-09 12:08:18 +00:00
|
|
|
x, y := stack.Popn()
|
2014-06-17 22:36:23 +00:00
|
|
|
vm.Printf(" %v ^ %v", y, x)
|
|
|
|
|
|
|
|
stack.Push(base.Xor(y, x))
|
2014-05-28 10:05:46 +00:00
|
|
|
case BYTE:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-04-09 12:08:18 +00:00
|
|
|
val, th := stack.Popn()
|
|
|
|
if th.Cmp(big.NewInt(32)) < 0 {
|
|
|
|
stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64()))
|
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigFalse)
|
|
|
|
}
|
2014-03-21 10:54:36 +00:00
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x20 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case SHA3:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-04-09 12:08:18 +00:00
|
|
|
size, offset := stack.Popn()
|
2014-06-09 19:35:56 +00:00
|
|
|
data := ethutil.Sha3Bin(mem.Get(offset.Int64(), size.Int64()))
|
2014-03-21 10:54:36 +00:00
|
|
|
|
2014-04-09 12:08:18 +00:00
|
|
|
stack.Push(ethutil.BigD(data))
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x30 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case ADDRESS:
|
2014-03-21 13:47:55 +00:00
|
|
|
stack.Push(ethutil.BigD(closure.Object().Address()))
|
2014-05-28 10:05:46 +00:00
|
|
|
case BALANCE:
|
2014-05-08 12:20:45 +00:00
|
|
|
stack.Push(closure.object.Amount)
|
2014-05-28 10:05:46 +00:00
|
|
|
case ORIGIN:
|
2014-04-11 17:29:57 +00:00
|
|
|
stack.Push(ethutil.BigD(vm.vars.Origin))
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALLER:
|
2014-06-17 22:25:58 +00:00
|
|
|
caller := closure.caller.Address()
|
|
|
|
stack.Push(ethutil.BigD(caller))
|
|
|
|
|
|
|
|
vm.Printf(" => %x", caller)
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALLVALUE:
|
2014-05-07 09:05:49 +00:00
|
|
|
stack.Push(vm.vars.Value)
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALLDATALOAD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-04-23 09:51:04 +00:00
|
|
|
offset := stack.Pop().Int64()
|
|
|
|
|
2014-06-18 11:48:08 +00:00
|
|
|
data := make([]byte, 32)
|
2014-06-17 16:05:46 +00:00
|
|
|
if len(closure.Args) >= int(offset) {
|
|
|
|
l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
|
2014-06-18 11:48:08 +00:00
|
|
|
|
|
|
|
copy(data, closure.Args[offset:l])
|
2014-05-28 21:16:54 +00:00
|
|
|
}
|
|
|
|
|
2014-06-17 22:25:58 +00:00
|
|
|
vm.Printf(" => 0x%x", data)
|
|
|
|
|
2014-05-28 21:16:54 +00:00
|
|
|
stack.Push(ethutil.BigD(data))
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALLDATASIZE:
|
2014-06-17 22:25:58 +00:00
|
|
|
l := int64(len(closure.Args))
|
|
|
|
stack.Push(big.NewInt(l))
|
|
|
|
|
|
|
|
vm.Printf(" => %d", l)
|
2014-06-12 09:19:32 +00:00
|
|
|
case CALLDATACOPY:
|
2014-06-19 23:10:20 +00:00
|
|
|
var (
|
|
|
|
size = int64(len(closure.Args))
|
|
|
|
mOff = stack.Pop().Int64()
|
|
|
|
cOff = stack.Pop().Int64()
|
|
|
|
l = stack.Pop().Int64()
|
|
|
|
)
|
|
|
|
|
|
|
|
if cOff > size {
|
|
|
|
cOff = 0
|
|
|
|
l = 0
|
|
|
|
} else if cOff+l > size {
|
|
|
|
l = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
code := closure.Args[cOff : cOff+l]
|
|
|
|
|
|
|
|
mem.Set(mOff, l, code)
|
2014-06-12 09:19:32 +00:00
|
|
|
case CODESIZE:
|
2014-06-19 23:10:20 +00:00
|
|
|
l := big.NewInt(int64(len(closure.Script)))
|
|
|
|
stack.Push(l)
|
|
|
|
|
|
|
|
vm.Printf(" => %d", l)
|
2014-06-12 09:19:32 +00:00
|
|
|
case CODECOPY:
|
2014-06-13 10:45:11 +00:00
|
|
|
var (
|
|
|
|
size = int64(len(closure.Script))
|
|
|
|
mOff = stack.Pop().Int64()
|
|
|
|
cOff = stack.Pop().Int64()
|
|
|
|
l = stack.Pop().Int64()
|
|
|
|
)
|
|
|
|
|
|
|
|
if cOff > size {
|
|
|
|
cOff = 0
|
|
|
|
l = 0
|
|
|
|
} else if cOff+l > size {
|
|
|
|
l = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
code := closure.Script[cOff : cOff+l]
|
|
|
|
|
|
|
|
mem.Set(mOff, l, code)
|
2014-05-28 10:05:46 +00:00
|
|
|
case GASPRICE:
|
2014-04-19 23:31:01 +00:00
|
|
|
stack.Push(closure.Price)
|
2014-03-21 10:54:36 +00:00
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x40 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case PREVHASH:
|
2014-04-11 17:29:57 +00:00
|
|
|
stack.Push(ethutil.BigD(vm.vars.PrevHash))
|
2014-05-28 10:05:46 +00:00
|
|
|
case COINBASE:
|
2014-04-11 17:29:57 +00:00
|
|
|
stack.Push(ethutil.BigD(vm.vars.Coinbase))
|
2014-05-28 10:05:46 +00:00
|
|
|
case TIMESTAMP:
|
2014-04-11 17:29:57 +00:00
|
|
|
stack.Push(big.NewInt(vm.vars.Time))
|
2014-05-28 10:05:46 +00:00
|
|
|
case NUMBER:
|
2014-06-19 11:45:29 +00:00
|
|
|
stack.Push(vm.vars.BlockNumber)
|
2014-05-28 10:05:46 +00:00
|
|
|
case DIFFICULTY:
|
2014-04-11 17:29:57 +00:00
|
|
|
stack.Push(vm.vars.Diff)
|
2014-05-28 10:05:46 +00:00
|
|
|
case GASLIMIT:
|
2014-04-19 23:31:01 +00:00
|
|
|
// TODO
|
|
|
|
stack.Push(big.NewInt(0))
|
2014-03-21 10:54:36 +00:00
|
|
|
|
2014-05-06 15:43:27 +00:00
|
|
|
// 0x50 range
|
2014-05-28 10:05:46 +00:00
|
|
|
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:
|
|
|
|
a := big.NewInt(int64(op) - int64(PUSH1) + 1)
|
2014-03-21 17:22:47 +00:00
|
|
|
pc.Add(pc, ethutil.Big1)
|
2014-05-06 15:43:27 +00:00
|
|
|
data := closure.Gets(pc, a)
|
2014-04-10 18:40:12 +00:00
|
|
|
val := ethutil.BigD(data.Bytes())
|
|
|
|
// Push value to stack
|
2014-03-20 16:26:07 +00:00
|
|
|
stack.Push(val)
|
2014-05-06 15:43:27 +00:00
|
|
|
pc.Add(pc, a.Sub(a, big.NewInt(1)))
|
2014-04-10 22:14:19 +00:00
|
|
|
|
2014-05-28 10:05:46 +00:00
|
|
|
step += int(op) - int(PUSH1) + 1
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-06-17 22:25:58 +00:00
|
|
|
vm.Printf(" => 0x%x", data.Bytes())
|
2014-05-28 10:05:46 +00:00
|
|
|
case POP:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 13:47:55 +00:00
|
|
|
stack.Pop()
|
2014-05-28 10:05:46 +00:00
|
|
|
case DUP:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 13:47:55 +00:00
|
|
|
stack.Push(stack.Peek())
|
2014-06-19 22:41:28 +00:00
|
|
|
|
|
|
|
vm.Printf(" => 0x%x", stack.Peek().Bytes())
|
2014-05-28 10:05:46 +00:00
|
|
|
case SWAP:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 13:47:55 +00:00
|
|
|
x, y := stack.Popn()
|
|
|
|
stack.Push(y)
|
|
|
|
stack.Push(x)
|
2014-05-28 10:05:46 +00:00
|
|
|
case MLOAD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 10:54:36 +00:00
|
|
|
offset := stack.Pop()
|
2014-06-20 18:11:40 +00:00
|
|
|
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
|
|
|
stack.Push(val)
|
|
|
|
|
|
|
|
vm.Printf(" => 0x%x", val.Bytes())
|
2014-05-28 10:05:46 +00:00
|
|
|
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-20 16:26:07 +00:00
|
|
|
// Pop value of the stack
|
2014-03-20 18:50:53 +00:00
|
|
|
val, mStart := stack.Popn()
|
2014-03-20 21:51:20 +00:00
|
|
|
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-06-17 22:25:58 +00:00
|
|
|
vm.Printf(" => 0x%x", val)
|
2014-05-28 10:05:46 +00:00
|
|
|
case MSTORE8:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 13:47:55 +00:00
|
|
|
val, mStart := stack.Popn()
|
|
|
|
base.And(val, new(big.Int).SetInt64(0xff))
|
|
|
|
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-06-17 22:25:58 +00:00
|
|
|
vm.Printf(" => 0x%x", val)
|
2014-05-28 10:05:46 +00:00
|
|
|
case SLOAD:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 13:47:55 +00:00
|
|
|
loc := stack.Pop()
|
2014-03-21 17:22:47 +00:00
|
|
|
val := closure.GetMem(loc)
|
2014-06-23 09:26:51 +00:00
|
|
|
|
2014-03-21 13:47:55 +00:00
|
|
|
stack.Push(val.BigInt())
|
2014-06-17 22:25:58 +00:00
|
|
|
|
2014-06-23 09:26:51 +00:00
|
|
|
vm.Printf(" {0x%x} 0x%x", loc.Bytes(), val.Bytes())
|
2014-05-28 10:05:46 +00:00
|
|
|
case SSTORE:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-21 17:22:47 +00:00
|
|
|
val, loc := stack.Popn()
|
2014-06-20 18:11:40 +00:00
|
|
|
closure.SetStorage(loc, ethutil.NewValue(val))
|
2014-04-30 12:43:32 +00:00
|
|
|
|
|
|
|
// Add the change to manifest
|
2014-05-08 17:09:36 +00:00
|
|
|
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" {0x%x} 0x%x", loc, val)
|
2014-05-28 10:05:46 +00:00
|
|
|
case JUMP:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(1)
|
2014-03-21 17:22:47 +00:00
|
|
|
pc = stack.Pop()
|
2014-04-19 23:31:01 +00:00
|
|
|
// Reduce pc by one because of the increment that's at the end of this for loop
|
2014-06-17 16:49:26 +00:00
|
|
|
vm.Printf(" ~> %v", pc).Endl()
|
|
|
|
|
2014-05-08 12:20:45 +00:00
|
|
|
continue
|
2014-05-28 10:05:46 +00:00
|
|
|
case JUMPI:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-30 16:55:51 +00:00
|
|
|
cond, pos := stack.Popn()
|
2014-06-17 16:05:46 +00:00
|
|
|
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
2014-03-21 17:22:47 +00:00
|
|
|
pc = pos
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-06-18 11:48:08 +00:00
|
|
|
vm.Printf(" ~> %v (t)", pc).Endl()
|
2014-06-17 16:49:26 +00:00
|
|
|
|
2014-05-08 12:20:45 +00:00
|
|
|
continue
|
2014-06-17 22:25:58 +00:00
|
|
|
} else {
|
|
|
|
vm.Printf(" (f)")
|
2014-03-21 17:22:47 +00:00
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case PC:
|
2014-03-21 17:22:47 +00:00
|
|
|
stack.Push(pc)
|
2014-05-28 10:05:46 +00:00
|
|
|
case MSIZE:
|
2014-03-21 17:22:47 +00:00
|
|
|
stack.Push(big.NewInt(int64(mem.Len())))
|
2014-06-12 09:19:32 +00:00
|
|
|
case GAS:
|
|
|
|
stack.Push(closure.Gas)
|
2014-04-15 20:16:38 +00:00
|
|
|
// 0x60 range
|
2014-05-28 10:05:46 +00:00
|
|
|
case CREATE:
|
2014-04-27 14:50:44 +00:00
|
|
|
require(3)
|
|
|
|
|
|
|
|
value := stack.Pop()
|
|
|
|
size, offset := stack.Popn()
|
|
|
|
|
2014-05-25 22:09:38 +00:00
|
|
|
// Snapshot the current stack so we are able to
|
|
|
|
// revert back to it later.
|
2014-06-19 22:41:28 +00:00
|
|
|
snapshot := vm.state.Copy()
|
2014-05-25 22:09:38 +00:00
|
|
|
|
2014-04-27 14:50:44 +00:00
|
|
|
// Generate a new address
|
2014-06-13 14:06:27 +00:00
|
|
|
addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N())
|
2014-06-23 14:11:55 +00:00
|
|
|
|
|
|
|
vm.Printf(" (*) %x", addr).Endl()
|
|
|
|
|
2014-04-27 14:50:44 +00:00
|
|
|
// Create a new contract
|
2014-06-23 11:42:30 +00:00
|
|
|
contract := vm.state.NewStateObject(addr)
|
|
|
|
contract.Amount = value
|
|
|
|
|
2014-04-27 14:50:44 +00:00
|
|
|
// Set the init script
|
2014-06-23 14:11:55 +00:00
|
|
|
contract.initScript = ethutil.BigD(mem.Get(offset.Int64(), size.Int64())).Bytes()
|
2014-04-27 14:50:44 +00:00
|
|
|
// Transfer all remaining gas to the new
|
|
|
|
// contract so it may run the init script
|
|
|
|
gas := new(big.Int).Set(closure.Gas)
|
2014-05-28 10:05:46 +00:00
|
|
|
|
2014-04-27 14:50:44 +00:00
|
|
|
// Create the closure
|
2014-06-13 14:06:27 +00:00
|
|
|
c := NewClosure(closure.caller,
|
2014-04-27 14:50:44 +00:00
|
|
|
closure.Object(),
|
|
|
|
contract.initScript,
|
|
|
|
vm.state,
|
|
|
|
gas,
|
2014-05-08 12:20:45 +00:00
|
|
|
closure.Price)
|
2014-04-27 14:50:44 +00:00
|
|
|
// Call the closure and set the return value as
|
|
|
|
// main script.
|
2014-06-23 14:11:55 +00:00
|
|
|
var err error
|
2014-05-28 11:14:56 +00:00
|
|
|
c.Script, gas, err = c.Call(vm, nil, hook)
|
2014-05-28 10:05:46 +00:00
|
|
|
|
2014-04-27 14:50:44 +00:00
|
|
|
if err != nil {
|
|
|
|
stack.Push(ethutil.BigFalse)
|
2014-05-25 22:09:38 +00:00
|
|
|
|
|
|
|
// Revert the state as it was before.
|
2014-06-19 22:41:28 +00:00
|
|
|
vm.state.Set(snapshot)
|
2014-04-27 14:50:44 +00:00
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigD(addr))
|
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case CALL:
|
2014-04-18 11:41:07 +00:00
|
|
|
require(7)
|
2014-06-19 11:45:29 +00:00
|
|
|
|
|
|
|
vm.Endl()
|
|
|
|
|
|
|
|
gas := stack.Pop()
|
2014-04-10 18:40:12 +00:00
|
|
|
// Pop gas and value of the stack.
|
2014-06-19 11:45:29 +00:00
|
|
|
value, addr := stack.Popn()
|
2014-03-20 16:26:07 +00:00
|
|
|
// Pop input size and offset
|
|
|
|
inSize, inOffset := stack.Popn()
|
2014-04-10 18:40:12 +00:00
|
|
|
// Pop return size and offset
|
|
|
|
retSize, retOffset := stack.Popn()
|
2014-05-08 12:20:45 +00:00
|
|
|
|
2014-03-21 10:54:36 +00:00
|
|
|
// Get the arguments from the memory
|
|
|
|
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
2014-05-08 12:20:45 +00:00
|
|
|
|
2014-06-16 09:14:01 +00:00
|
|
|
if closure.object.Amount.Cmp(value) < 0 {
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
|
2014-05-08 12:20:45 +00:00
|
|
|
|
2014-06-16 09:14:01 +00:00
|
|
|
stack.Push(ethutil.BigFalse)
|
|
|
|
} else {
|
2014-06-19 22:41:28 +00:00
|
|
|
snapshot := vm.state.Copy()
|
|
|
|
|
2014-06-19 11:45:29 +00:00
|
|
|
stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
|
|
|
|
|
|
|
|
closure.object.SubAmount(value)
|
|
|
|
// Add the value to the state object
|
|
|
|
stateObject.AddAmount(value)
|
|
|
|
|
|
|
|
// Create a new callable closure
|
|
|
|
closure := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
|
|
|
|
// Executer the closure and get the return value (if any)
|
|
|
|
ret, _, err := closure.Call(vm, args, hook)
|
|
|
|
if err != nil {
|
2014-06-16 09:14:01 +00:00
|
|
|
stack.Push(ethutil.BigFalse)
|
2014-06-19 11:45:29 +00:00
|
|
|
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Debugf("Closure execution failed. %v\n", err)
|
2014-06-19 22:41:28 +00:00
|
|
|
|
|
|
|
vm.err = err
|
|
|
|
vm.state.Set(snapshot)
|
2014-06-19 11:45:29 +00:00
|
|
|
} else {
|
|
|
|
stack.Push(ethutil.BigTrue)
|
|
|
|
|
|
|
|
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
2014-05-25 22:09:38 +00:00
|
|
|
}
|
2014-04-15 20:16:38 +00:00
|
|
|
}
|
2014-05-28 10:05:46 +00:00
|
|
|
case RETURN:
|
2014-04-15 20:16:38 +00:00
|
|
|
require(2)
|
2014-03-20 16:26:07 +00:00
|
|
|
size, offset := stack.Popn()
|
2014-03-20 21:51:20 +00:00
|
|
|
ret := mem.Get(offset.Int64(), size.Int64())
|
2014-03-20 16:26:07 +00:00
|
|
|
|
2014-06-19 22:41:28 +00:00
|
|
|
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
|
2014-06-19 11:45:29 +00:00
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
return closure.Return(ret), nil
|
2014-05-28 10:05:46 +00:00
|
|
|
case SUICIDE:
|
2014-05-01 20:14:34 +00:00
|
|
|
require(1)
|
|
|
|
|
|
|
|
receiver := vm.state.GetAccount(stack.Pop().Bytes())
|
|
|
|
receiver.AddAmount(closure.object.Amount)
|
|
|
|
|
2014-06-17 09:06:06 +00:00
|
|
|
trie := closure.object.state.trie
|
|
|
|
trie.NewIterator().Each(func(key string, v *ethutil.Value) {
|
|
|
|
trie.Delete(key)
|
|
|
|
})
|
2014-05-01 20:14:34 +00:00
|
|
|
|
|
|
|
fallthrough
|
2014-05-28 10:05:46 +00:00
|
|
|
case STOP: // Stop the closure
|
2014-06-23 09:26:51 +00:00
|
|
|
vm.Endl()
|
2014-06-19 11:45:29 +00:00
|
|
|
|
2014-05-01 20:14:34 +00:00
|
|
|
return closure.Return(nil), nil
|
2014-03-21 10:54:36 +00:00
|
|
|
default:
|
2014-06-23 11:54:10 +00:00
|
|
|
vmlogger.Debugf("Invalid opcode %x\n", op)
|
2014-04-10 18:40:12 +00:00
|
|
|
|
2014-04-15 20:16:38 +00:00
|
|
|
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
|
2014-03-20 16:26:07 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 17:22:47 +00:00
|
|
|
pc.Add(pc, ethutil.Big1)
|
2014-02-24 11:10:45 +00:00
|
|
|
|
2014-06-17 16:49:26 +00:00
|
|
|
vm.Endl()
|
|
|
|
|
2014-04-11 12:28:30 +00:00
|
|
|
if hook != nil {
|
2014-05-28 10:05:46 +00:00
|
|
|
if !hook(prevStep, op, mem, stack, closure.Object()) {
|
2014-05-27 11:32:31 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
2014-04-11 17:29:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|