package main import ( "fmt" "github.com/ethereum/ethutil-go" "math/big" "strconv" ) // Op codes const ( oSTOP int = 0x00 oADD int = 0x01 oMUL int = 0x02 oSUB int = 0x03 oDIV int = 0x04 oSDIV int = 0x05 oMOD int = 0x06 oSMOD int = 0x07 oEXP int = 0x08 oNEG int = 0x09 oLT int = 0x0a oLE int = 0x0b oGT int = 0x0c oGE int = 0x0d oEQ int = 0x0e oNOT int = 0x0f oMYADDRESS int = 0x10 oTXSENDER int = 0x11 oTXVALUE int = 0x12 oTXFEE int = 0x13 oTXDATAN int = 0x14 oTXDATA int = 0x15 oBLK_PREVHASH int = 0x16 oBLK_COINBASE int = 0x17 oBLK_TIMESTAMP int = 0x18 oBLK_NUMBER int = 0x19 oBLK_DIFFICULTY int = 0x1a oSHA256 int = 0x20 oRIPEMD160 int = 0x21 oECMUL int = 0x22 oECADD int = 0x23 oECSIGN int = 0x24 oECRECOVER int = 0x25 oECVALID int = 0x26 oPUSH int = 0x30 oPOP int = 0x31 oDUP int = 0x32 oDUPN int = 0x33 oSWAP int = 0x34 oSWAPN int = 0x35 oLOAD int = 0x36 oSTORE int = 0x37 oJMP int = 0x40 oJMPI int = 0x41 oIND int = 0x42 oEXTRO int = 0x50 oBALANCE int = 0x51 oMKTX int = 0x60 oSUICIDE int = 0xff ) type OpType int const ( tNorm = iota tData tExtro tCrypto ) type TxCallback func(opType OpType) bool // Simple push/pop stack mechanism type Stack struct { data []string } func NewStack() *Stack { return &Stack{} } func (st *Stack) Pop() string { s := len(st.data) str := st.data[s-1] st.data = st.data[:s-1] return str } func (st *Stack) Popn() (*big.Int, *big.Int) { s := len(st.data) strs := st.data[s-2:] st.data = st.data[:s-2] return ethutil.Big(strs[0]), ethutil.Big(strs[1]) } func (st *Stack) Push(d string) { st.data = append(st.data, d) } func (st *Stack) Print() { fmt.Println(st.data) } type Vm struct { // Stack stack *Stack } func NewVm() *Vm { return &Vm{ stack: NewStack(), } } func (vm *Vm) ProcContract(tx *ethutil.Transaction, block *ethutil.Block, cb TxCallback) { // Instruction pointer pc := 0 contract := block.GetContract(tx.Hash()) if contract == nil { fmt.Println("Contract not found") return } Pow256 := ethutil.BigPow(2, 256) //fmt.Printf("# op arg\n") out: for { // The base big int for all calculations. Use this for any results. base := new(big.Int) // XXX Should Instr return big int slice instead of string slice? // Get the next instruction from the contract //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc))))) nb := ethutil.NumberToBytes(uint64(pc), 32) op, _, _ := ethutil.Instr(contract.State().Get(string(nb))) if !cb(0) { break } if Debug { //fmt.Printf("%-3d %-4d\n", pc, op) } switch op { case oADD: x, y := vm.stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) base.Mod(base, Pow256) // Pop result back on the stack vm.stack.Push(base.String()) case oSUB: x, y := vm.stack.Popn() // (x - y) % 2 ** 256 base.Sub(x, y) base.Mod(base, Pow256) // Pop result back on the stack vm.stack.Push(base.String()) case oMUL: x, y := vm.stack.Popn() // (x * y) % 2 ** 256 base.Mul(x, y) base.Mod(base, Pow256) // Pop result back on the stack vm.stack.Push(base.String()) case oDIV: x, y := vm.stack.Popn() // floor(x / y) base.Div(x, y) // Pop result back on the stack vm.stack.Push(base.String()) case oSDIV: x, y := vm.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 vm.stack.Push(z.String()) case oMOD: x, y := vm.stack.Popn() base.Mod(x, y) vm.stack.Push(base.String()) case oSMOD: x, y := vm.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 vm.stack.Push(z.String()) case oEXP: x, y := vm.stack.Popn() base.Exp(x, y, Pow256) vm.stack.Push(base.String()) case oNEG: base.Sub(Pow256, ethutil.Big(vm.stack.Pop())) vm.stack.Push(base.String()) case oLT: x, y := vm.stack.Popn() // x < y if x.Cmp(y) < 0 { vm.stack.Push("1") } else { vm.stack.Push("0") } case oLE: x, y := vm.stack.Popn() // x <= y if x.Cmp(y) < 1 { vm.stack.Push("1") } else { vm.stack.Push("0") } case oGT: x, y := vm.stack.Popn() // x > y if x.Cmp(y) > 0 { vm.stack.Push("1") } else { vm.stack.Push("0") } case oGE: x, y := vm.stack.Popn() // x >= y if x.Cmp(y) > -1 { vm.stack.Push("1") } else { vm.stack.Push("0") } case oNOT: x, y := vm.stack.Popn() // x != y if x.Cmp(y) != 0 { vm.stack.Push("1") } else { vm.stack.Push("0") } case oMYADDRESS: vm.stack.Push(string(tx.Hash())) case oTXSENDER: vm.stack.Push(string(tx.Sender())) case oPUSH: // Get the next entry and pushes the value on the stack pc++ vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32)))) case oPOP: // Pop current value of the stack vm.stack.Pop() case oLOAD: // Load instruction X on the stack i, _ := strconv.Atoi(vm.stack.Pop()) vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32)))) case oSTOP: break out } pc++ } vm.stack.Print() }