From 6930260962f4c6d1fe11d07a10300da96886ecd6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 20 Apr 2014 01:31:01 +0200 Subject: [PATCH] Updated VM --- ethchain/closure.go | 10 ++++---- ethchain/state_object.go | 22 +++++++++++++++-- ethchain/vm.go | 52 +++++++++++++++++++++++++++------------- ethchain/vm_test.go | 41 +++++++++++++++++++++---------- 4 files changed, 89 insertions(+), 36 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 5c508179e5..defd8b5c84 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -8,7 +8,7 @@ import ( ) type Callee interface { - ReturnGas(*big.Int, *State) + ReturnGas(*big.Int, *big.Int, *State) Address() []byte } @@ -83,18 +83,16 @@ func (c *Closure) Return(ret []byte) []byte { // If no callee is present return it to // the origin (i.e. contract or tx) if c.callee != nil { - c.callee.ReturnGas(c.Gas, c.State) + c.callee.ReturnGas(c.Gas, c.Price, c.State) } else { - c.object.ReturnGas(c.Gas, c.State) - // TODO incase it's a POST contract we gotta serialise the contract again. - // But it's not yet defined + c.object.ReturnGas(c.Gas, c.Price, c.State) } return ret } // Implement the Callee interface -func (c *Closure) ReturnGas(gas *big.Int, state *State) { +func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { // Return the gas to the closure c.Gas.Add(c.Gas, gas) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8b4de0c4f3..f562e5b04d 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -1,6 +1,7 @@ package ethchain import ( + "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -70,8 +71,9 @@ func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { } // Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(val *big.Int, state *State) { - c.AddAmount(val) +func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { + remainder := new(big.Int).Mul(gas, price) + c.AddAmount(remainder) } func (c *StateObject) AddAmount(amount *big.Int) { @@ -82,18 +84,33 @@ func (c *StateObject) SubAmount(amount *big.Int) { c.Amount.Sub(c.Amount, amount) } +func (c *StateObject) ConvertGas(gas, price *big.Int) error { + total := new(big.Int).Mul(gas, price) + if total.Cmp(c.Amount) > 0 { + return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total) + } + + c.SubAmount(total) + + return nil +} + +// Returns the address of the contract/account func (c *StateObject) Address() []byte { return c.address } +// Returns the main script body func (c *StateObject) Script() []byte { return c.script } +// Returns the initialization script func (c *StateObject) Init() []byte { return c.initScript } +// State object encoding methods func (c *StateObject) RlpEncode() []byte { var root interface{} if c.state != nil { @@ -113,6 +130,7 @@ func (c *StateObject) RlpDecode(data []byte) { c.script = decoder.Get(3).Bytes() } +// Converts an transaction in to a state object func MakeContract(tx *Transaction, state *State) *StateObject { // Create contract if there's no recipient if tx.IsContract() { diff --git a/ethchain/vm.go b/ethchain/vm.go index 85aefa685d..c249adfebc 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -102,10 +102,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } */ - // TODO Get each instruction cost properly gas := new(big.Int) useGas := func(amount *big.Int) { - gas.Add(gas, new(big.Int).Mul(amount, closure.Price)) + gas.Add(gas, amount) } switch op { @@ -142,6 +141,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + + // Sub the amount of gas from the remaining closure.Gas.Sub(closure.Gas, gas) switch op { @@ -157,7 +158,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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: @@ -165,7 +165,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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: @@ -173,7 +172,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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: @@ -325,7 +323,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oCALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) case oGASPRICE: - // TODO + stack.Push(closure.Price) // 0x40 range case oPREVHASH: @@ -339,7 +337,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oDIFFICULTY: stack.Push(vm.vars.Diff) case oGASLIMIT: - // TODO + // TODO + stack.Push(big.NewInt(0)) // 0x50 range case oPUSH: // Push PC+1 on to the stack @@ -399,11 +398,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oJUMP: require(1) pc = stack.Pop() + // Reduce pc by one because of the increment that's at the end of this for loop + pc.Sub(pc, ethutil.Big1) case oJUMPI: require(2) cond, pos := stack.Popn() if cond.Cmp(ethutil.BigTrue) == 0 { pc = pos + pc.Sub(pc, ethutil.Big1) } case oPC: stack.Push(pc) @@ -421,21 +423,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro inSize, inOffset := stack.Popn() // Pop return size and offset retSize, retOffset := stack.Popn() + // Make sure there's enough gas + if closure.Gas.Cmp(gas) < 0 { + stack.Push(ethutil.BigFalse) + + break + } // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) // 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, closure.Price, value) - // Executer the closure and get the return value (if any) - ret, err := closure.Call(vm, args, hook) - if err != nil { - stack.Push(ethutil.BigFalse) + + if contract != nil { + // Prepay for the gas + // If gas is set to 0 use all remaining gas for the next call + if gas.Cmp(big.NewInt(0)) == 0 { + gas = closure.Gas + } + closure.Gas.Sub(closure.Gas, gas) + // Create a new callable closure + closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value) + // Executer the closure and get the return value (if any) + ret, err := closure.Call(vm, args, hook) + if err != nil { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) + } + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) } else { - stack.Push(ethutil.BigTrue) + ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) + stack.Push(ethutil.BigFalse) } - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: require(2) size, offset := stack.Popn() diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index f66f2a8966..cca9b876a7 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -86,9 +86,9 @@ func TestRun4(t *testing.T) { int32 a = 10 int32 b = 20 if a > b { - int32 c = this.caller() + int32 c = this.Caller() } - exit() + Exit() `), false) script := ethutil.Assemble(asm...) tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil) @@ -103,8 +103,9 @@ func TestRun4(t *testing.T) { store[1000] = 10^20 } - store[1001] = this.value() * 20 - store[this.origin()] = store[this.origin()] + 1000 + + store[1001] = this.Value() * 20 + store[this.Origin()] = store[this.Origin()] + 1000 if store[1001] > 20 { store[1001] = 10^50 @@ -112,8 +113,18 @@ func TestRun4(t *testing.T) { int8 ret = 0 int8 arg = 10 - store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" - call(store[1002], 0, 100000000, arg, ret) + Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret) + + big t + for int8 i = 0; i < 10; i++ { + t = i + } + + if 10 > 20 { + int8 shouldnt = 2 + } else { + int8 should = 1 + } `), false) if err != nil { fmt.Println(err) @@ -125,10 +136,17 @@ func TestRun4(t *testing.T) { callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil) // Contract addr as test address + gas := big.NewInt(1000) + gasPrice := big.NewInt(10) account := NewAccount(ContractAddr, big.NewInt(10000000)) - fmt.Println(account) + fmt.Println("account.Amount =", account.Amount) c := MakeContract(callerTx, state) - callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), big.NewInt(10), big.NewInt(0)) + e := account.ConvertGas(gas, gasPrice) + if e != nil { + fmt.Println(err) + } + fmt.Println("account.Amount =", account.Amount) + callerClosure := NewClosure(account, c, c.script, state, gas, gasPrice, big.NewInt(0)) vm := NewVm(state, RuntimeVars{ Origin: account.Address(), @@ -138,11 +156,11 @@ func TestRun4(t *testing.T) { Time: 1, Diff: big.NewInt(256), }) - _, e := callerClosure.Call(vm, nil, nil) + _, e = callerClosure.Call(vm, nil, nil) if e != nil { fmt.Println("error", e) } - fmt.Println(account) + fmt.Println("account.Amount =", account.Amount) } func TestRun5(t *testing.T) { @@ -156,6 +174,5 @@ func TestRun5(t *testing.T) { } exit() `), false) - script := ethutil.Assemble(asm...) - fmt.Println(Disassemble(script)) + ethutil.Assemble(asm...) }