package ethchain import ( _ "bytes" "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" _ "math" "math/big" ) var ( GasStep = big.NewInt(1) GasSha = big.NewInt(20) GasSLoad = big.NewInt(20) GasSStore = big.NewInt(100) GasBalance = big.NewInt(20) GasCreate = big.NewInt(100) GasCall = big.NewInt(20) GasMemory = big.NewInt(1) ) type Vm struct { txPool *TxPool // Stack for processing contracts stack *Stack // non-persistent key/value memory storage mem map[string]*big.Int vars RuntimeVars state *State } type RuntimeVars struct { origin []byte blockNumber uint64 prevHash []byte coinbase []byte time int64 diff *big.Int txData []string } func NewVm(state *State, vars RuntimeVars) *Vm { return &Vm{vars: vars, state: state} } var Pow256 = ethutil.BigPow(2, 256) func (vm *Vm) RunClosure(closure *Closure) []byte { // If the amount of gas supplied is less equal to 0 if closure.Gas.Cmp(big.NewInt(0)) <= 0 { // TODO Do something } // Memory for the current closure mem := &Memory{} // New stack (should this be shared?) stack := NewStack() // Instruction pointer pc := big.NewInt(0) // Current step count step := 0 // The base for all big integer arithmetic base := new(big.Int) if ethutil.Config.Debug { ethutil.Config.Log.Debugf("# op\n") } for { step++ // Get the memory location of pc val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) if ethutil.Config.Debug { ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) } // TODO Get each instruction cost properly gas := new(big.Int) useGas := func(amount *big.Int) { gas.Add(gas, amount) } switch op { case oSHA3: useGas(GasSha) case oSLOAD: useGas(GasSLoad) case oSSTORE: var mult *big.Int y, x := stack.Peekn() val := closure.GetMem(x) if val.IsEmpty() && len(y.Bytes()) > 0 { mult = ethutil.Big2 } else if !val.IsEmpty() && len(y.Bytes()) == 0 { mult = ethutil.Big0 } else { mult = ethutil.Big1 } useGas(base.Mul(mult, GasSStore)) case oBALANCE: useGas(GasBalance) case oCREATE: useGas(GasCreate) case oCALL: useGas(GasCall) case oMLOAD, oMSIZE, oMSTORE8, oMSTORE: useGas(GasMemory) default: useGas(GasStep) } if closure.Gas.Cmp(gas) < 0 { ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) return closure.Return(nil) } switch op { case oLOG: stack.Print() mem.Print() case oSTOP: // Stop the closure return closure.Return(nil) // 0x20 range case oADD: x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oSUB: x, y := stack.Popn() // (x - y) % 2 ** 256 base.Sub(x, y) base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oMUL: x, y := stack.Popn() // (x * y) % 2 ** 256 base.Mul(x, y) base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oDIV: x, y := stack.Popn() // floor(x / y) base.Div(x, y) // Pop result back on the stack stack.Push(base) case oSDIV: x, y := 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 stack.Push(z) case oMOD: x, y := stack.Popn() base.Mod(x, y) stack.Push(base) case oSMOD: x, y := 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 stack.Push(z) case oEXP: x, y := stack.Popn() base.Exp(x, y, Pow256) stack.Push(base) case oNEG: base.Sub(Pow256, stack.Pop()) stack.Push(base) case oLT: x, y := stack.Popn() // x < y if x.Cmp(y) < 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case oGT: x, y := stack.Popn() // x > y if x.Cmp(y) > 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case oEQ: x, y := stack.Popn() // x == y if x.Cmp(y) == 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case oNOT: x := stack.Pop() if x.Cmp(ethutil.BigFalse) == 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } // 0x10 range case oAND: x, y := stack.Popn() if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case oOR: x, y := stack.Popn() if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case oXOR: x, y := stack.Popn() stack.Push(base.Xor(x, y)) case oBYTE: val, th := stack.Popn() if th.Cmp(big.NewInt(32)) < 0 { stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64())) } else { stack.Push(ethutil.BigFalse) } // 0x20 range case oSHA3: size, offset := stack.Popn() data := mem.Get(offset.Int64(), size.Int64()) stack.Push(ethutil.BigD(data)) // 0x30 range case oADDRESS: stack.Push(ethutil.BigD(closure.Object().Address())) case oBALANCE: stack.Push(closure.Value) case oORIGIN: stack.Push(ethutil.BigD(vm.vars.origin)) case oCALLER: stack.Push(ethutil.BigD(closure.Callee().Address())) case oCALLVALUE: // FIXME: Original value of the call, not the current value stack.Push(closure.Value) case oCALLDATA: offset := stack.Pop() mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) case oCALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) case oGASPRICE: // TODO // 0x40 range case oPREVHASH: stack.Push(ethutil.BigD(vm.vars.prevHash)) case oCOINBASE: stack.Push(ethutil.BigD(vm.vars.coinbase)) case oTIMESTAMP: stack.Push(big.NewInt(vm.vars.time)) case oNUMBER: stack.Push(big.NewInt(int64(vm.vars.blockNumber))) case oDIFFICULTY: stack.Push(vm.vars.diff) case oGASLIMIT: // TODO // 0x50 range case oPUSH: // Push PC+1 on to the stack pc.Add(pc, ethutil.Big1) val := closure.GetMem(pc).BigInt() stack.Push(val) case oPOP: stack.Pop() case oDUP: stack.Push(stack.Peek()) case oSWAP: x, y := stack.Popn() stack.Push(y) stack.Push(x) case oMLOAD: offset := stack.Pop() stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32))) case oMSTORE: // Store the value at stack top-1 in to memory at location stack top // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) case oMSTORE8: val, mStart := stack.Popn() base.And(val, new(big.Int).SetInt64(0xff)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) case oSLOAD: loc := stack.Pop() val := closure.GetMem(loc) stack.Push(val.BigInt()) case oSSTORE: val, loc := stack.Popn() closure.SetMem(loc, ethutil.NewValue(val)) case oJUMP: pc = stack.Pop() case oJUMPI: cond, pos := stack.Popn() if cond.Cmp(ethutil.BigTrue) == 0 { pc = pos } case oPC: stack.Push(pc) case oMSIZE: stack.Push(big.NewInt(int64(mem.Len()))) // 0x60 range case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() // Pop input size and offset inSize, inOffset := stack.Popn() fmt.Println(inSize, inOffset) // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) // Pop gas and value of the stack. gas, value := stack.Popn() // Closure addr addr := stack.Pop() // Fetch the contract which will serve as the closure body contract := vm.state.GetContract(addr.Bytes()) // Create a new callable closure closure := NewClosure(closure, contract, contract.script, vm.state, gas, value) // Executer the closure and get the return value (if any) ret := closure.Call(vm, args) mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: size, offset := stack.Popn() ret := mem.Get(offset.Int64(), size.Int64()) return closure.Return(ret) case oSUICIDE: /* recAddr := stack.Pop().Bytes() // Purge all memory deletedMemory := contract.state.Purge() // Add refunds to the pop'ed address refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory))) account := state.GetAccount(recAddr) account.Amount.Add(account.Amount, refund) // Update the refunding address state.UpdateAccount(recAddr, account) // Delete the contract state.trie.Update(string(addr), "") ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) break out */ default: ethutil.Config.Log.Debugln("Invalid opcode", op) } pc.Add(pc, ethutil.Big1) } } /* func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length) j := int64(0) dataItems := make([]string, int(length.Uint64())) for i := from.Int64(); i < length.Int64(); i++ { dataItems[j] = contract.GetMem(big.NewInt(j)).Str() j++ } tx := NewTransaction(addr, value, dataItems) if tx.IsContract() { contract := MakeContract(tx, state) state.UpdateContract(contract) } else { account := state.GetAccount(tx.Recipient) account.Amount.Add(account.Amount, tx.Value) state.UpdateAccount(tx.Recipient, account) } } // Returns an address from the specified contract's address func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int { contract := state.GetContract(contractAddr) if contract == nil { log.Panicf("invalid contract addr %x", contractAddr) } val := state.trie.Get(memAddr.String()) // decode the object as a big integer decoder := ethutil.NewValueFromBytes([]byte(val)) if decoder.IsNil() { return ethutil.BigFalse } return decoder.BigInt() } */