aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ethchain/stack.go28
-rw-r--r--ethchain/vm.go184
-rw-r--r--ethchain/vm_test.go29
-rw-r--r--ethutil/parsing.go6
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 {