aboutsummaryrefslogtreecommitdiffstats
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
parent6ab61f2c524686e479f9546d5ce2529f3b8eb7fe (diff)
downloadgo-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.gz
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.bz2
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.lz
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.xz
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.tar.zst
go-tangerine-2d3c3674faa2c9c15d4ab27c7dc6b3e07a532780.zip
Updated stack based vm
-rw-r--r--parsing.go67
-rw-r--r--vm.go320
-rw-r--r--vm_test.go59
3 files changed, 221 insertions, 225 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
-
- /* 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
- */
+ "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",
+
+
+ "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++
+ 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()
}
-*/
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()