From 2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 4 Jan 2014 00:32:52 +0100 Subject: [PATCH] Updated stack based vm --- parsing.go | 65 ++++------- vm.go | 320 ++++++++++++++++++++++++----------------------------- vm_test.go | 59 ++++++++-- 3 files changed, 220 insertions(+), 224 deletions(-) diff --git a/parsing.go b/parsing.go index 4c3f1187e..b97d77f3e 100644 --- a/parsing.go +++ b/parsing.go @@ -10,50 +10,29 @@ import ( // Op codes var OpCodes = map[string]string{ - "STOP": "0", - "PUSH": "48", // 0x30 - "POP": "49", // 0x31 - "LOAD": "54", // 0x36 + "STOP": "0", + "ADD": "1", + "MUL": "2", + "SUB": "3", + "DIV": "4", + "SDIV": "5", + "MOD": "6", + "SMOD": "7", + "EXP": "8", + "NEG": "9", + "LT": "10", + "LE": "11", + "GT": "12", + "GE": "13", + "EQ": "14", + "NOT": "15", + "MYADDRESS": "16", + "TXSENDER": "17", - /* OLD VM OPCODES - "ADD": "16", // 0x10 - "SUB": "17", // 0x11 - "MUL": "18", // 0x12 - "DIV": "19", // 0x13 - "SDIV": "20", // 0x14 - "MOD": "21", // 0x15 - "SMOD": "22", // 0x16 - "EXP": "23", // 0x17 - "NEG": "24", // 0x18 - "LT": "32", // 0x20 - "LE": "33", // 0x21 - "GT": "34", // 0x22 - "GE": "35", // 0x23 - "EQ": "36", // 0x24 - "NOT": "37", // 0x25 - "SHA256": "48", // 0x30 - "RIPEMD160": "49", // 0x31 - "ECMUL": "50", // 0x32 - "ECADD": "51", // 0x33 - "SIGN": "52", // 0x34 - "RECOVER": "53", // 0x35 - "COPY": "64", // 0x40 - "ST": "65", // 0x41 - "LD": "66", // 0x42 - "SET": "67", // 0x43 - "JMP": "80", // 0x50 - "JMPI": "81", // 0x51 - "IND": "82", // 0x52 - "EXTRO": "96", // 0x60 - "BALANCE": "97", // 0x61 - "MKTX": "112", // 0x70 - "DATA": "128", // 0x80 - "DATAN": "129", // 0x81 - "MYADDRESS": "144", // 0x90 - "BLKHASH": "145", // 0x91 - "COINBASE": "146", // 0x92 - "SUICIDE": "255", // 0xff - */ + + "PUSH": "48", + "POP": "49", + "LOAD": "54", } diff --git a/vm.go b/vm.go index d7e3f77cf..9e1e83b0b 100644 --- a/vm.go +++ b/vm.go @@ -12,46 +12,28 @@ import ( // 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 + + oPUSH int = 0x30 oPOP int = 0x31 oLOAD int = 0x36 - /* - oADD int = 0x10 - oSUB int = 0x11 - oMUL int = 0x12 - oDIV int = 0x13 - oSDIV int = 0x14 - oMOD int = 0x15 - oSMOD int = 0x16 - oEXP int = 0x17 - oNEG int = 0x18 - oLT int = 0x20 - oLE int = 0x21 - oGT int = 0x22 - oGE int = 0x23 - oEQ int = 0x24 - oNOT int = 0x25 - oSHA256 int = 0x30 - oRIPEMD160 int = 0x31 - oECMUL int = 0x32 - oECADD int = 0x33 - oSIGN int = 0x34 - oRECOVER int = 0x35 - oCOPY int = 0x40 - oST int = 0x41 - oLD int = 0x42 - oSET int = 0x43 - oJMP int = 0x50 - oJMPI int = 0x51 - oIND int = 0x52 - oEXTRO int = 0x60 - oBALANCE int = 0x61 - oMKTX int = 0x70 - oDATA int = 0x80 - oDATAN int = 0x81 - oMYADDRESS int = 0x90 - oSUICIDE int = 0xff - */ ) type OpType int @@ -79,9 +61,21 @@ func (st *Stack) Pop() string { 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 Big(strs[0]), 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 @@ -96,7 +90,7 @@ func NewVm() *Vm { func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) { // Instruction pointer - iptr := 0 + pc := 0 contract := block.GetContract(tx.Hash()) if contract == nil { @@ -104,164 +98,144 @@ func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) { return } + Pow256 := 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) - base.SetString("0",0) // so it doesn't whine about it // XXX Should Instr return big int slice instead of string slice? // Get the next instruction from the contract - op, args, _ := Instr(contract.state.Get(string(Encode(uint32(iptr))))) + //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc))))) + op, _, _ := Instr(contract.state.Get(string(NumberToBytes(uint64(pc), 32)))) + + if !cb(0) { break } if Debug { - fmt.Printf("%-3d %-4d %v\n", iptr, op, args) + 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, 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(tx.sender) case oPUSH: // Get the next entry and pushes the value on the stack - iptr++ - vm.stack.Push(contract.state.Get(string(Encode(uint32(iptr))))) + pc++ + vm.stack.Push(contract.state.Get(string(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(Encode(uint32(i))))) + vm.stack.Push(contract.state.Get(string(NumberToBytes(uint64(i), 32)))) case oSTOP: break out } - iptr++ - } -} - -/* -type Vm struct { - // Memory stack - stack map[string]string - memory map[string]map[string]string -} - -func NewVm() *Vm { - //stackSize := uint(256) - - return &Vm{ - stack: make(map[string]string), - memory: make(map[string]map[string]string), - } -} - -func (vm *Vm) RunTransaction(tx *Transaction, cb TxCallback) { - if Debug { - fmt.Printf(` -# processing Tx (%v) -# fee = %f, ops = %d, sender = %s, value = %d - `, tx.addr, float32(tx.fee) / 1e8, len(tx.data), tx.sender, tx.value) + pc++ } - vm.stack = make(map[string]string) - vm.stack["0"] = tx.sender - vm.stack["1"] = "100" //int(tx.value) - vm.stack["1"] = "1000" //int(tx.fee) - // Stack pointer - stPtr := 0 - - //vm.memory[tx.addr] = make([]int, 256) - vm.memory[tx.addr] = make(map[string]string) - - // Define instruction 'accessors' for the instruction, which makes it more readable - // also called register values, shorthanded as Rx/y/z. Memory address are shorthanded as Mx/y/z. - // Instructions are shorthanded as Ix/y/z - x := 0; y := 1; z := 2; //a := 3; b := 4; c := 5 -out: - for stPtr < len(tx.data) { - // 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? - op, args, _ := Instr(tx.data[stPtr]) - - if Debug { - fmt.Printf("%-3d %d %v\n", stPtr, op, args) - } - - opType := OpType(tNorm) - // Determine the op type (used for calculating fees by the block manager) - switch op { - case oEXTRO, oBALANCE: - opType = tExtro - case oSHA256, oRIPEMD160, oECMUL, oECADD: // TODO add rest - opType = tCrypto - } - - // If the callback yielded a negative result abort execution - if !cb(opType) { break out } - - nptr := stPtr - switch op { - case oSTOP: - fmt.Println("exiting (oSTOP), idx =", nptr) - - break out - case oADD: - // (Rx + Ry) % 2 ** 256 - base.Add(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) - base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) - // Set the result to Rz - vm.stack[args[ z ]] = base.String() - case oSUB: - // (Rx - Ry) % 2 ** 256 - base.Sub(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) - base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) - // Set the result to Rz - vm.stack[args[ z ]] = base.String() - case oMUL: - // (Rx * Ry) % 2 ** 256 - base.Mul(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) - base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) - // Set the result to Rz - vm.stack[args[ z ]] = base.String() - case oDIV: - // floor(Rx / Ry) - base.Div(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) - // Set the result to Rz - vm.stack[args[ z ]] = base.String() - case oSET: - // Set the (numeric) value at Iy to Rx - vm.stack[args[ x ]] = args[ y ] - case oLD: - // Load the value at Mx to Ry - vm.stack[args[ y ]] = vm.memory[tx.addr][vm.stack[args[ x ]]] - case oLT: - cmp := Big(vm.stack[args[ x ]]).Cmp( Big(vm.stack[args[ y ]]) ) - // Set the result as "boolean" value to Rz - if cmp < 0 { // a < b - vm.stack[args[ z ]] = "1" - } else { - vm.stack[args[ z ]] = "0" - } - case oJMP: - // Set the instruction pointer to the value at Rx - ptr, _ := strconv.Atoi( vm.stack[args[ x ]] ) - nptr = ptr - case oJMPI: - // Set the instruction pointer to the value at Ry if Rx yields true - if vm.stack[args[ x ]] != "0" { - ptr, _ := strconv.Atoi( vm.stack[args[ y ]] ) - nptr = ptr - } - default: - fmt.Println("Error op", op) - break - } - - if stPtr == nptr { - stPtr++ - } else { - stPtr = nptr - if Debug { fmt.Println("... JMP", nptr, "...") } - } - } + vm.stack.Print() } -*/ diff --git a/vm_test.go b/vm_test.go index fe0358a42..efcd875cd 100644 --- a/vm_test.go +++ b/vm_test.go @@ -7,23 +7,66 @@ import ( func TestVm(t *testing.T) { + InitFees() + db, _ := NewMemDatabase() Db = db - ctrct := NewTransaction("", 20, []string{ - "PUSH", - "1a2f2e", - "PUSH", - "hallo", + ctrct := NewTransaction("", 200000000, []string{ + "PUSH", "1a2f2e", + "PUSH", "hallo", "POP", // POP hallo - "PUSH", - "3", + "PUSH", "3", "LOAD", // Load hallo back on the stack + + "PUSH", "1", + "PUSH", "2", + "ADD", + + "PUSH", "2", + "PUSH", "1", + "SUB", + + "PUSH", "100000000000000000000000", + "PUSH", "10000000000000", + "SDIV", + + "PUSH", "105", + "PUSH", "200", + "MOD", + + "PUSH", "100000000000000000000000", + "PUSH", "10000000000000", + "SMOD", + + "PUSH", "5", + "PUSH", "10", + "LT", + + "PUSH", "5", + "PUSH", "5", + "LE", + + "PUSH", "50", + "PUSH", "5", + "GT", + + "PUSH", "5", + "PUSH", "5", + "GE", + + "PUSH", "10", + "PUSH", "10", + "NOT", + + "MYADDRESS", + "TXSENDER", + "STOP", }) tx := NewTransaction("1e8a42ea8cce13", 100, []string{}) - block := CreateBlock("", 0, "", "", 0, 0, "", []*Transaction{ctrct, tx}) + block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx}) db.Put(block.Hash(), block.MarshalRlp()) bm := NewBlockManager()