aboutsummaryrefslogtreecommitdiffstats
path: root/vm.go
diff options
context:
space:
mode:
authorobscuren <obscuren@obscura.com>2014-01-04 07:32:52 +0800
committerobscuren <obscuren@obscura.com>2014-01-04 07:32:52 +0800
commit2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780 (patch)
tree620540f7299aec1f3bc9dd7151d2421a0d2ab534 /vm.go
parent6ab61f2c524686e479f9546d5ce2529f3b8eb7fe (diff)
downloaddexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.gz
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.bz2
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.lz
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.xz
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.zst
dexon-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.zip
Updated stack based vm
Diffstat (limited to 'vm.go')
-rw-r--r--vm.go320
1 files changed, 147 insertions, 173 deletions
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++
+ pc++
}
-}
-/*
-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)
- }
-
- 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()
}
-*/