diff options
author | obscuren <obscuren@obscura.com> | 2013-12-26 19:45:52 +0800 |
---|---|---|
committer | obscuren <obscuren@obscura.com> | 2013-12-26 19:45:52 +0800 |
commit | 5db3335dce766bd679c54ea44f6df08a7ff74762 (patch) | |
tree | 75614c847fdc07150351e9aa02353d8f1d23196c /vm.go | |
download | go-tangerine-5db3335dce766bd679c54ea44f6df08a7ff74762.tar.gz go-tangerine-5db3335dce766bd679c54ea44f6df08a7ff74762.tar.zst go-tangerine-5db3335dce766bd679c54ea44f6df08a7ff74762.zip |
Initial commit
Diffstat (limited to 'vm.go')
-rw-r--r-- | vm.go | 182 |
1 files changed, 182 insertions, 0 deletions
@@ -0,0 +1,182 @@ +package main + +import ( + "math" + "math/big" + "fmt" + "strconv" + _ "encoding/hex" +) + +// Op codes +const ( + oSTOP int = 0x00 + 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 + +type Vm struct { + // Memory stack + stack map[string]string + // Index ptr + iptr int + memory map[string]map[string]string +} + +func NewVm() *Vm { + fmt.Println("init Ethereum VM") + + stackSize := uint(256) + fmt.Println("stack size =", stackSize) + + return &Vm{make(map[string]string), 0, make(map[string]map[string]string)} +} + +func (vm *Vm) RunTransaction(tx *Transaction, cb TxCallback) { + 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) + + //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 vm.iptr < 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[vm.iptr]) + + fmt.Printf("%-3d %d %v\n", vm.iptr, 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 := vm.iptr + 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 vm.iptr == nptr { + vm.iptr++ + } else { + vm.iptr = nptr + fmt.Println("... JMP", nptr, "...") + } + } + fmt.Println("# finished processing Tx\n") +} |