aboutsummaryrefslogblamecommitdiffstats
path: root/vm.go
blob: 96a3dfa05abbe21e001043d1d6717559ec69e6eb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


            



                                        



           
















































                                  


               
 
       



                    
 
 

                                        

                                  
                     
 
 
                        
                       

                               
                         
 

                               
 
                  

 
                                              
                         
 

                               
 
                                                         

 
                                 
                                    
 
                          
                            
 

                

                    


                  


                                  

 



                                                   
 




                                                 
 
                                        
 
                                      
    







                                                                                   
 


                             
 


                                                           
 


































































                                                       
 































































                                                                                                          
 
                        
 
package main

import (
    "fmt"
    "github.com/ethereum/ethutil-go"
    "math/big"
    "strconv"
)

// 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
    oTXVALUE        int = 0x12
    oTXFEE          int = 0x13
    oTXDATAN        int = 0x14
    oTXDATA         int = 0x15
    oBLK_PREVHASH   int = 0x16
    oBLK_COINBASE   int = 0x17
    oBLK_TIMESTAMP  int = 0x18
    oBLK_NUMBER     int = 0x19
    oBLK_DIFFICULTY int = 0x1a
    oSHA256         int = 0x20
    oRIPEMD160      int = 0x21
    oECMUL          int = 0x22
    oECADD          int = 0x23
    oECSIGN         int = 0x24
    oECRECOVER      int = 0x25
    oECVALID        int = 0x26
    oPUSH           int = 0x30
    oPOP            int = 0x31
    oDUP            int = 0x32
    oDUPN           int = 0x33
    oSWAP           int = 0x34
    oSWAPN          int = 0x35
    oLOAD           int = 0x36
    oSTORE          int = 0x37
    oJMP            int = 0x40
    oJMPI           int = 0x41
    oIND            int = 0x42
    oEXTRO          int = 0x50
    oBALANCE        int = 0x51
    oMKTX           int = 0x60
    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) Popn() (*big.Int, *big.Int) {
    s := len(st.data)

    strs := st.data[s-2:]
    st.data = st.data[:s-2]

    return ethutil.Big(strs[0]), ethutil.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
    stack *Stack
}

func NewVm() *Vm {
    return &Vm{
        stack: NewStack(),
    }
}

func (vm *Vm) ProcContract(tx *ethutil.Transaction,
    block *ethutil.Block, cb TxCallback) {
    // Instruction pointer
    pc := 0

    contract := block.GetContract(tx.Hash())
    if contract == nil {
        fmt.Println("Contract not found")
        return
    }

    Pow256 := ethutil.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)
        // XXX Should Instr return big int slice instead of string slice?
        // Get the next instruction from the contract
        //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
        nb := ethutil.NumberToBytes(uint64(pc), 32)
        op, _, _ := ethutil.Instr(contract.State().Get(string(nb)))

        if !cb(0) {
            break
        }

        if Debug {
            //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, ethutil.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(string(tx.Sender()))
        case oPUSH:
            // Get the next entry and pushes the value on the stack
            pc++
            vm.stack.Push(contract.State().Get(string(ethutil.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(ethutil.NumberToBytes(uint64(i), 32))))
        case oSTOP:
            break out
        }
        pc++
    }

    vm.stack.Print()
}