plugeth/vm.go
2014-01-03 13:40:12 +01:00

268 lines
6.4 KiB
Go

package main
import (
_"math"
"math/big"
"fmt"
_"strconv"
_ "encoding/hex"
"strconv"
)
// Op codes
const (
oSTOP int = 0x00
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
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) Push(d string) {
st.data = append(st.data, d)
}
type Vm struct {
// Stack
stack *Stack
}
func NewVm() *Vm {
return &Vm{
stack: NewStack(),
}
}
func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
// Instruction pointer
iptr := 0
contract := block.GetContract(tx.Hash())
if contract == nil {
fmt.Println("Contract not found")
return
}
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)))))
if Debug {
fmt.Printf("%-3d %-4d %v\n", iptr, op, args)
}
switch op {
case oPUSH:
// Get the next entry and pushes the value on the stack
iptr++
vm.stack.Push(contract.state.Get(string(Encode(uint32(iptr)))))
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)))))
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)
}
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, "...") }
}
}
}
*/