From fa1db8d2dcbc12fd9b343e6572c541d92fe7cb55 Mon Sep 17 00:00:00 2001
From: obscuren <geffobscura@gmail.com>
Date: Fri, 21 Mar 2014 11:54:36 +0100
Subject: Implemented closure arguments

---
 ethchain/stack.go   |  28 ++++++--
 ethchain/vm.go      | 184 +++++++++++++++++++++++++++++++++++++++++++++++++---
 ethchain/vm_test.go |  29 +++++++--
 ethutil/parsing.go  |   6 +-
 4 files changed, 225 insertions(+), 22 deletions(-)

diff --git a/ethchain/stack.go b/ethchain/stack.go
index 429c31d08..c75d02dda 100644
--- a/ethchain/stack.go
+++ b/ethchain/stack.go
@@ -68,12 +68,16 @@ const (
 	oJUMP    = 0x59
 	oJUMPI   = 0x5a
 	oPC      = 0x5b
-	oMEMSIZE = 0x5c
+	oMSIZE   = 0x5c
 
 	// 0x60 range - closures
 	oCREATE = 0x60
 	oCALL   = 0x61
 	oRETURN = 0x62
+
+	// 0x70 range - other
+	oLOG     = 0x70 // XXX Unofficial
+	oSUICIDE = 0x7f
 )
 
 // Since the opcodes aren't all in order we can't use a regular slice
@@ -136,12 +140,16 @@ var opCodeToString = map[OpCode]string{
 	oJUMP:    "JUMP",
 	oJUMPI:   "JUMPI",
 	oPC:      "PC",
-	oMEMSIZE: "MEMSIZE",
+	oMSIZE:   "MSIZE",
 
 	// 0x60 range - closures
 	oCREATE: "CREATE",
 	oCALL:   "CALL",
 	oRETURN: "RETURN",
+
+	// 0x70 range - other
+	oLOG:     "LOG",
+	oSUICIDE: "SUICIDE",
 }
 
 func (o OpCode) String() string {
@@ -215,20 +223,30 @@ type Memory struct {
 
 func (m *Memory) Set(offset, size int64, value []byte) {
 	totSize := offset + size
-	lenSize := int64(len(m.store))
+	lenSize := int64(len(m.store) - 1)
 	if totSize > lenSize {
 		// Calculate the diff between the sizes
 		diff := totSize - lenSize
 		if diff > 0 {
 			// Create a new empty slice and append it
-			newSlice := make([]byte, diff+1)
+			newSlice := make([]byte, diff-1)
 			// Resize slice
 			m.store = append(m.store, newSlice...)
 		}
 	}
-	copy(m.store[offset:offset+size+1], value)
+	copy(m.store[offset:offset+size], value)
 }
 
 func (m *Memory) Get(offset, size int64) []byte {
 	return m.store[offset : offset+size]
 }
+
+func (m *Memory) Print() {
+	fmt.Println("### MEM ###")
+	if len(m.store) > 0 {
+		fmt.Println(m.store)
+	} else {
+		fmt.Println("-- empty --")
+	}
+	fmt.Println("###########")
+}
diff --git a/ethchain/vm.go b/ethchain/vm.go
index ba19231cc..3d2ee4c86 100644
--- a/ethchain/vm.go
+++ b/ethchain/vm.go
@@ -2,7 +2,7 @@ package ethchain
 
 import (
 	_ "bytes"
-	"fmt"
+	_ "fmt"
 	"github.com/ethereum/eth-go/ethutil"
 	_ "github.com/obscuren/secp256k1-go"
 	"log"
@@ -36,6 +36,8 @@ 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.GetGas().Cmp(big.NewInt(0)) <= 0 {
@@ -48,9 +50,10 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
 	stack := NewStack()
 	// Instruction pointer
 	pc := int64(0)
-	// Current address
-	//addr := vars.address
+	// 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")
@@ -75,27 +78,171 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
 		}
 
 		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 oNOT:
+			x, y := stack.Popn()
+			// x != y
+			if x.Cmp(y) != 0 {
+				stack.Push(ethutil.BigTrue)
+			} else {
+				stack.Push(ethutil.BigFalse)
+			}
+
+		// 0x10 range
+		case oAND:
+		case oOR:
+		case oXOR:
+		case oBYTE:
+
+		// 0x20 range
+		case oSHA3:
+
+		// 0x30 range
+		case oADDRESS:
+		case oBALANCE:
+		case oORIGIN:
+		case oCALLER:
+		case oCALLVALUE:
+		case oCALLDATA:
+			offset := stack.Pop()
+			mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
+		case oCALLDATASIZE:
+		case oRETURNDATASIZE:
+		case oTXGASPRICE:
+
+		// 0x40 range
+		case oPREVHASH:
+		case oPREVNONCE:
+		case oCOINBASE:
+		case oTIMESTAMP:
+		case oNUMBER:
+		case oDIFFICULTY:
+		case oGASLIMIT:
+
+		// 0x50 range
 		case oPUSH: // Push PC+1 on to the stack
 			pc++
 			val := closure.GetMem(pc).BigInt()
 			stack.Push(val)
+		case oPOP:
+		case oDUP:
+		case oSWAP:
+		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:
+		case oSLOAD:
+		case oSSTORE:
+		case oJUMP:
+		case oJUMPI:
+		case oPC:
+		case oMSIZE:
 
-		case oCALLDATA:
-			offset := stack.Pop()
-			mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
+		// 0x60 range
 		case oCALL:
 			// Pop return size and offset
 			retSize, retOffset := stack.Popn()
 			// Pop input size and offset
 			inSize, inOffset := stack.Popn()
-			// TODO remove me.
-			fmt.Sprintln(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
@@ -105,7 +252,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
 			// 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)
+			ret := closure.Call(vm, args)
 
 			mem.Set(retOffset.Int64(), retSize.Int64(), ret)
 		case oRETURN:
@@ -113,6 +260,25 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
 			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++
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
index 1dca5cfb7..ce8c7a4de 100644
--- a/ethchain/vm_test.go
+++ b/ethchain/vm_test.go
@@ -1,6 +1,7 @@
 package ethchain
 
 import (
+	"bytes"
 	"fmt"
 	"github.com/ethereum/eth-go/ethdb"
 	"github.com/ethereum/eth-go/ethutil"
@@ -119,11 +120,13 @@ func TestRun3(t *testing.T) {
 		"PUSH", "300",
 		"PUSH", "0",
 		"MSTORE",
-		"PUSH", "300",
-		"PUSH", "31",
-		"MSTORE",
-		"PUSH", "62",
+
+		"PUSH", "32",
+		"CALLDATA",
+
+		"PUSH", "64",
 		"PUSH", "0",
+		"LOG",
 		"RETURN",
 	})
 	tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
@@ -133,14 +136,21 @@ func TestRun3(t *testing.T) {
 	state.UpdateContract(contract)
 
 	callerScript := Compile([]string{
-		"PUSH", "62", // ret size
+		"PUSH", "1337", // Argument
+		"PUSH", "65", // argument mem offset
+		"MSTORE",
+		"PUSH", "64", // ret size
 		"PUSH", "0", // ret offset
+
 		"PUSH", "32", // arg size
-		"PUSH", "63", // arg offset
+		"PUSH", "65", // arg offset
 		"PUSH", "1000", /// Gas
 		"PUSH", "0", /// value
 		"PUSH", string(addr), // Sender
 		"CALL",
+		"PUSH", "64",
+		"PUSH", "0",
+		"RETURN",
 	})
 	callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
 
@@ -158,5 +168,10 @@ func TestRun3(t *testing.T) {
 		// XXX Tx data? Could be just an argument to the closure instead
 		txData: nil,
 	})
-	callerClosure.Call(vm, nil)
+	ret := callerClosure.Call(vm, nil)
+
+	exp := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 57}
+	if bytes.Compare(ret, exp) != 0 {
+		t.Errorf("expected return value to be %v, got %v", exp, ret)
+	}
 }
diff --git a/ethutil/parsing.go b/ethutil/parsing.go
index 9ff2827a0..b2e9d9fee 100644
--- a/ethutil/parsing.go
+++ b/ethutil/parsing.go
@@ -65,12 +65,16 @@ var OpCodes = map[string]byte{
 	"JUMP":    0x59,
 	"JUMPI":   0x5a,
 	"PC":      0x5b,
-	"MEMSIZE": 0x5c,
+	"MSIZE":   0x5c,
 
 	// 0x60 range - closures
 	"CREATE": 0x60,
 	"CALL":   0x61,
 	"RETURN": 0x62,
+
+	// 0x70 range - other
+	"LOG":     0x70,
+	"SUICIDE": 0x7f,
 }
 
 func IsOpCode(s string) bool {
-- 
cgit v1.2.3