diff options
author | Maran <maran.hidskes@gmail.com> | 2014-03-24 17:24:39 +0800 |
---|---|---|
committer | Maran <maran.hidskes@gmail.com> | 2014-03-24 17:24:39 +0800 |
commit | 97786d03d57e1ca79e34ce5fd9aa172c61c3e665 (patch) | |
tree | 251a5e54305e5cedf568f2fb73dbff9302df4185 /ethchain/vm.go | |
parent | 274d5cc91c45349ec8d7a1f5a20ef29896b38b2e (diff) | |
parent | 6a86c517c4f4b372cad0ae1d92e926a482eac5ba (diff) | |
download | go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar.gz go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar.bz2 go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar.lz go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar.xz go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.tar.zst go-tangerine-97786d03d57e1ca79e34ce5fd9aa172c61c3e665.zip |
Merge branch 'master' into miner
Diffstat (limited to 'ethchain/vm.go')
-rw-r--r-- | ethchain/vm.go | 461 |
1 files changed, 190 insertions, 271 deletions
diff --git a/ethchain/vm.go b/ethchain/vm.go index 7e119ac99..126592b25 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -1,12 +1,12 @@ package ethchain import ( - "bytes" - "fmt" + _ "bytes" + _ "fmt" "github.com/ethereum/eth-go/ethutil" - "github.com/obscuren/secp256k1-go" + _ "github.com/obscuren/secp256k1-go" "log" - "math" + _ "math" "math/big" ) @@ -18,122 +18,102 @@ type Vm struct { mem map[string]*big.Int vars RuntimeVars + + state *State } type RuntimeVars struct { - address []byte + origin []byte blockNumber uint64 - sender []byte prevHash []byte coinbase []byte time int64 diff *big.Int - txValue *big.Int txData []string } -func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { - vm.mem = make(map[string]*big.Int) - vm.stack = NewStack() +func NewVm(state *State, vars RuntimeVars) *Vm { + return &Vm{vars: vars, state: state} +} - addr := vars.address // tx.Hash()[12:] - // Instruction pointer - pc := 0 +var Pow256 = ethutil.BigPow(2, 256) - if contract == nil { - fmt.Println("Contract not found") - return +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 } - Pow256 := ethutil.BigPow(2, 256) + // 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") } - stepcount := 0 - totalFee := new(big.Int) - -out: for { - stepcount++ - // The base big int for all calculations. Use this for any results. - base := new(big.Int) - val := contract.GetMem(pc) - //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) + step++ + // Get the memory location of pc + val := closure.GetMem(pc) + // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - - var fee *big.Int = new(big.Int) - var fee2 *big.Int = new(big.Int) - if stepcount > 16 { - fee.Add(fee, StepFee) - } - - // Calculate the fees - switch op { - case oSSTORE: - y, x := vm.stack.Peekn() - val := contract.Addr(ethutil.BigToBytes(x, 256)) - if val.IsEmpty() && len(y.Bytes()) > 0 { - fee2.Add(DataFee, StoreFee) - } else { - fee2.Sub(DataFee, StoreFee) - } - case oSLOAD: - fee.Add(fee, StoreFee) - case oEXTRO, oBALANCE: - fee.Add(fee, ExtroFee) - case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: - fee.Add(fee, CryptoFee) - case oMKTX: - fee.Add(fee, ContractFee) + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) } - tf := new(big.Int).Add(fee, fee2) - if contract.Amount.Cmp(tf) < 0 { - fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount) - break - } - // Add the fee to the total fee. It's subtracted when we're done looping - totalFee.Add(totalFee, tf) + // TODO Get each instruction cost properly + fee := new(big.Int) + fee.Add(fee, big.NewInt(1000)) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + if closure.Gas.Cmp(fee) < 0 { + return closure.Return(nil) } switch op { - case oSTOP: - fmt.Println("") - break out + case oLOG: + stack.Print() + mem.Print() + case oSTOP: // Stop the closure + return closure.Return(nil) + + // 0x20 range case oADD: - x, y := vm.stack.Popn() + x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) base.Mod(base, Pow256) // Pop result back on the stack - vm.stack.Push(base) + stack.Push(base) case oSUB: - x, y := vm.stack.Popn() + x, y := stack.Popn() // (x - y) % 2 ** 256 base.Sub(x, y) base.Mod(base, Pow256) // Pop result back on the stack - vm.stack.Push(base) + stack.Push(base) case oMUL: - x, y := vm.stack.Popn() + x, y := stack.Popn() // (x * y) % 2 ** 256 base.Mul(x, y) base.Mod(base, Pow256) // Pop result back on the stack - vm.stack.Push(base) + stack.Push(base) case oDIV: - x, y := vm.stack.Popn() + x, y := stack.Popn() // floor(x / y) base.Div(x, y) // Pop result back on the stack - vm.stack.Push(base) + stack.Push(base) case oSDIV: - x, y := vm.stack.Popn() + x, y := stack.Popn() // n > 2**255 if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) @@ -147,13 +127,13 @@ out: z.Sub(Pow256, z) } // Push result on to the stack - vm.stack.Push(z) + stack.Push(z) case oMOD: - x, y := vm.stack.Popn() + x, y := stack.Popn() base.Mod(x, y) - vm.stack.Push(base) + stack.Push(base) case oSMOD: - x, y := vm.stack.Popn() + x, y := stack.Popn() // n > 2**255 if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) @@ -167,250 +147,189 @@ out: z.Sub(Pow256, z) } // Push result on to the stack - vm.stack.Push(z) + stack.Push(z) case oEXP: - x, y := vm.stack.Popn() + x, y := stack.Popn() base.Exp(x, y, Pow256) - vm.stack.Push(base) + stack.Push(base) case oNEG: - base.Sub(Pow256, vm.stack.Pop()) - vm.stack.Push(base) + base.Sub(Pow256, stack.Pop()) + stack.Push(base) case oLT: - x, y := vm.stack.Popn() + x, y := stack.Popn() // x < y if x.Cmp(y) < 0 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oLE: - x, y := vm.stack.Popn() - // x <= y - if x.Cmp(y) < 1 { - vm.stack.Push(ethutil.BigTrue) + stack.Push(ethutil.BigTrue) } else { - vm.stack.Push(ethutil.BigFalse) + stack.Push(ethutil.BigFalse) } case oGT: - x, y := vm.stack.Popn() + x, y := stack.Popn() // x > y if x.Cmp(y) > 0 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oGE: - x, y := vm.stack.Popn() - // x >= y - if x.Cmp(y) > -1 { - vm.stack.Push(ethutil.BigTrue) + stack.Push(ethutil.BigTrue) } else { - vm.stack.Push(ethutil.BigFalse) + stack.Push(ethutil.BigFalse) } case oNOT: - x, y := vm.stack.Popn() + x, y := stack.Popn() // x != y if x.Cmp(y) != 0 { - vm.stack.Push(ethutil.BigTrue) + stack.Push(ethutil.BigTrue) } else { - vm.stack.Push(ethutil.BigFalse) + stack.Push(ethutil.BigFalse) } - case oMYADDRESS: - vm.stack.Push(ethutil.BigD(addr)) - case oTXSENDER: - vm.stack.Push(ethutil.BigD(vars.sender)) - case oTXVALUE: - vm.stack.Push(vars.txValue) - case oTXDATAN: - vm.stack.Push(big.NewInt(int64(len(vars.txData)))) - case oTXDATA: - v := vm.stack.Pop() - // v >= len(data) - if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 { - vm.stack.Push(ethutil.Big("0")) - } else { - vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()])) - } - case oBLK_PREVHASH: - vm.stack.Push(ethutil.BigD(vars.prevHash)) - case oBLK_COINBASE: - vm.stack.Push(ethutil.BigD(vars.coinbase)) - case oBLK_TIMESTAMP: - vm.stack.Push(big.NewInt(vars.time)) - case oBLK_NUMBER: - vm.stack.Push(big.NewInt(int64(vars.blockNumber))) - case oBLK_DIFFICULTY: - vm.stack.Push(vars.diff) - case oBASEFEE: - // e = 10^21 - e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0)) - d := new(big.Rat) - d.SetInt(vars.diff) - c := new(big.Rat) - c.SetFloat64(0.5) - // d = diff / 0.5 - d.Quo(d, c) - // base = floor(d) - base.Div(d.Num(), d.Denom()) - x := new(big.Int) - x.Div(e, base) + // 0x10 range + case oAND: + case oOR: + case oXOR: + case oBYTE: - // x = floor(10^21 / floor(diff^0.5)) - vm.stack.Push(x) - case oSHA256, oSHA3, oRIPEMD160: - // This is probably save - // ceil(pop / 32) - length := int(math.Ceil(float64(vm.stack.Pop().Uint64()) / 32.0)) - // New buffer which will contain the concatenated popped items - data := new(bytes.Buffer) - for i := 0; i < length; i++ { - // Encode the number to bytes and have it 32bytes long - num := ethutil.NumberToBytes(vm.stack.Pop().Bytes(), 256) - data.WriteString(string(num)) - } + // 0x20 range + case oSHA3: - if op == oSHA256 { - vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) - } else if op == oSHA3 { - vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) - } else { - vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes()))) - } - case oECMUL: - y := vm.stack.Pop() - x := vm.stack.Pop() - //n := vm.stack.Pop() + // 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 - //if ethutil.Big(x).Cmp(ethutil.Big(y)) { - data := new(bytes.Buffer) - data.WriteString(x.String()) - data.WriteString(y.String()) - if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 { - // TODO - } else { - // Invalid, push infinity - vm.stack.Push(ethutil.Big("0")) - vm.stack.Push(ethutil.Big("0")) - } - //} else { - // // Invalid, push infinity - // vm.stack.Push("0") - // vm.stack.Push("0") - //} + // 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 - case oECADD: - case oECSIGN: - case oECRECOVER: - case oECVALID: - case oPUSH: - pc++ - vm.stack.Push(contract.GetMem(pc).BigInt()) + // 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: - // Pop current value of the stack - vm.stack.Pop() + stack.Pop() case oDUP: - // Dup top stack - x := vm.stack.Pop() - vm.stack.Push(x) - vm.stack.Push(x) + stack.Push(stack.Peek()) case oSWAP: - // Swap two top most values - x, y := vm.stack.Popn() - vm.stack.Push(y) - vm.stack.Push(x) + x, y := stack.Popn() + stack.Push(y) + stack.Push(x) case oMLOAD: - x := vm.stack.Pop() - vm.stack.Push(vm.mem[x.String()]) - case oMSTORE: - x, y := vm.stack.Popn() - vm.mem[x.String()] = y + 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: - // Load the value in storage and push it on the stack - x := vm.stack.Pop() - // decode the object as a big integer - decoder := contract.Addr(x.Bytes()) - if !decoder.IsNil() { - vm.stack.Push(decoder.BigInt()) - } else { - vm.stack.Push(ethutil.BigFalse) - } + loc := stack.Pop() + val := closure.GetMem(loc) + stack.Push(val.BigInt()) case oSSTORE: - // Store Y at index X - y, x := vm.stack.Popn() - addr := ethutil.BigToBytes(x, 256) - fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr)) - contract.SetAddr(addr, y) - //contract.State().Update(string(idx), string(y)) - case oJMP: - x := int(vm.stack.Pop().Uint64()) - // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) - pc = x - pc-- - case oJMPI: - x := vm.stack.Pop() - // Set pc to x if it's non zero - if x.Cmp(ethutil.BigFalse) != 0 { - pc = int(x.Uint64()) - pc-- + val, loc := stack.Popn() + closure.SetMem(loc, ethutil.NewValue(val)) + case oJUMP: + pc = stack.Pop() + case oJUMPI: + pos, cond := stack.Popn() + if cond.Cmp(big.NewInt(0)) > 0 { + pc = pos } - case oIND: - vm.stack.Push(big.NewInt(int64(pc))) - case oEXTRO: - memAddr := vm.stack.Pop() - contractAddr := vm.stack.Pop().Bytes() + 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() + // 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, vm.state, gas, value) + // Executer the closure and get the return value (if any) + ret := closure.Call(vm, args) - // Push the contract's memory on to the stack - vm.stack.Push(contractMemory(state, contractAddr, memAddr)) - case oBALANCE: - // Pushes the balance of the popped value on to the stack - account := state.GetAccount(vm.stack.Pop().Bytes()) - vm.stack.Push(account.Amount) - case oMKTX: - addr, value := vm.stack.Popn() - from, length := vm.stack.Popn() + mem.Set(retOffset.Int64(), retSize.Int64(), ret) + case oRETURN: + size, offset := stack.Popn() + ret := mem.Get(offset.Int64(), size.Int64()) - makeInlineTx(addr.Bytes(), value, from, length, contract, state) + return closure.Return(ret) case oSUICIDE: - recAddr := vm.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), "") + /* + 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 + ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) + break out + */ default: - fmt.Printf("Invalid OPCODE: %x\n", op) + ethutil.Config.Log.Debugln("Invalid opcode", op) } - ethutil.Config.Log.Debugln("") - //vm.stack.Print() - pc++ - } - state.UpdateContract(addr, contract) + 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 := 0 + j := int64(0) dataItems := make([]string, int(length.Uint64())) - for i := from.Uint64(); i < length.Uint64(); i++ { - dataItems[j] = contract.GetMem(j).Str() + 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(tx.Hash()[12:], contract) + state.UpdateContract(contract) } else { account := state.GetAccount(tx.Recipient) account.Amount.Add(account.Amount, tx.Value) |