forked from cerc-io/plugeth
268 lines
6.4 KiB
Go
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, "...") }
|
|
}
|
|
}
|
|
}
|
|
*/
|