diff --git a/ethchain/closure.go b/ethchain/closure.go index 4ef43a2da..204fbce06 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -21,15 +21,17 @@ type ClosureBody interface { type Closure struct { callee Callee object ClosureBody - state *State + State *State gas *big.Int val *big.Int + + args []byte } // Create a new closure for the given data items func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.Int) *Closure { - return &Closure{callee, object, state, gas, val} + return &Closure{callee, object, state, gas, val, nil} } // Retuns the x element in data slice @@ -42,14 +44,20 @@ func (c *Closure) GetMem(x int64) *ethutil.Value { return m } +func (c *Closure) Call(vm *Vm, args []byte) []byte { + c.args = args + + return vm.RunClosure(c) +} + func (c *Closure) Return(ret []byte) []byte { // Return the remaining gas to the callee // 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.State) } else { - c.object.ReturnGas(c.gas, c.state) + 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 } diff --git a/ethchain/stack.go b/ethchain/stack.go index 349e7817a..bfb19614e 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -235,6 +235,7 @@ func (st *ValueStack) Peekn() (*ethutil.Value, *ethutil.Value) { func (st *ValueStack) Push(d *ethutil.Value) { st.data = append(st.data, d) } + func (st *ValueStack) Print() { fmt.Println("### STACK ###") if len(st.data) > 0 { diff --git a/ethchain/vm.go b/ethchain/vm.go index 861b041d8..2fa78a748 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -18,6 +18,8 @@ type Vm struct { mem map[string]*big.Int vars RuntimeVars + + state *State } type RuntimeVars struct { @@ -32,7 +34,11 @@ type RuntimeVars struct { txData []string } -func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byte { +func NewVm(state *State, vars RuntimeVars) *Vm { + return &Vm{vars: vars, state: state} +} + +func (vm *Vm) RunClosure(closure *Closure) []byte { // If the amount of gas supplied is less equal to 0 if closure.GetGas().Cmp(big.NewInt(0)) <= 0 { // TODO Do something @@ -71,17 +77,28 @@ func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byt } switch op { - case oSTOP: + case oSTOP: // Stop the closure return closure.Return(nil) - case oPUSH: + case oPUSH: // Push PC+1 on to the stack pc++ val := closure.GetMem(pc).BigInt() stack.Push(val) - case oMSTORE: + case oMSTORE: // Store the value at stack top-1 in to memory at location stack top // Pop value of the stack - val := stack.Pop() - // Set the bytes to the memory field - mem = append(mem, ethutil.BigToBytes(val, 256)...) + val, mStart := stack.Popn() + // Ensure that memory is large enough to hold the data + // If it isn't resize the memory slice so that it may hold the value + bytesLen := big.NewInt(32) + totSize := new(big.Int).Add(mStart, bytesLen) + lenSize := big.NewInt(int64(len(mem))) + if totSize.Cmp(lenSize) > 0 { + // Calculate the diff between the sizes + diff := new(big.Int).Sub(totSize, lenSize) + // Create a new empty slice and append it + newSlice := make([]byte, diff.Int64()+1) + mem = append(mem, newSlice...) + } + copy(mem[mStart.Int64():mStart.Int64()+bytesLen.Int64()+1], ethutil.BigToBytes(val, 256)) case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() @@ -93,20 +110,25 @@ func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byt gas, value := stack.Popn() // Closure addr addr := stack.Pop() - - contract := state.GetContract(addr.Bytes()) - closure := NewClosure(closure, contract, state, gas, value) - ret := vm.RunClosure(closure, state, vars) + // 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, nil) // Ensure that memory is large enough to hold the returned data + // If it isn't resize the memory slice so that it may hold the value totSize := new(big.Int).Add(retOffset, retSize) lenSize := big.NewInt(int64(len(mem))) - // Resize the current memory slice so that the return value may fit if totSize.Cmp(lenSize) > 0 { + // Calculate the diff between the sizes diff := new(big.Int).Sub(totSize, lenSize) + // Create a new empty slice and append it newSlice := make([]byte, diff.Int64()+1) mem = append(mem, newSlice...) } + // Copy over the returned values to the memory given the offset and size copy(mem[retOffset.Int64():retOffset.Int64()+retSize.Int64()+1], ret) case oRETURN: size, offset := stack.Popn() diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 4e72c9249..654ddb566 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -117,8 +117,10 @@ func TestRun3(t *testing.T) { script := Compile([]string{ "PUSH", "300", + "PUSH", "0", "MSTORE", "PUSH", "300", + "PUSH", "31", "MSTORE", "PUSH", "62", "PUSH", "0", @@ -147,8 +149,7 @@ func TestRun3(t *testing.T) { executer.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) callerClosure := NewClosure(executer, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) - vm := &Vm{} - vm.RunClosure(callerClosure, state, RuntimeVars{ + vm := NewVm(state, RuntimeVars{ address: callerAddr, blockNumber: 1, sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), @@ -159,4 +160,5 @@ func TestRun3(t *testing.T) { txValue: big.NewInt(10000), txData: nil, }) + callerClosure.Call(vm, nil) }