Implemented closure arguments

This commit is contained in:
obscuren 2014-03-21 11:54:36 +01:00
parent 9cf8ce9ef8
commit fa1db8d2dc
4 changed files with 225 additions and 22 deletions

View File

@ -68,12 +68,16 @@ const (
oJUMP = 0x59 oJUMP = 0x59
oJUMPI = 0x5a oJUMPI = 0x5a
oPC = 0x5b oPC = 0x5b
oMEMSIZE = 0x5c oMSIZE = 0x5c
// 0x60 range - closures // 0x60 range - closures
oCREATE = 0x60 oCREATE = 0x60
oCALL = 0x61 oCALL = 0x61
oRETURN = 0x62 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 // 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", oJUMP: "JUMP",
oJUMPI: "JUMPI", oJUMPI: "JUMPI",
oPC: "PC", oPC: "PC",
oMEMSIZE: "MEMSIZE", oMSIZE: "MSIZE",
// 0x60 range - closures // 0x60 range - closures
oCREATE: "CREATE", oCREATE: "CREATE",
oCALL: "CALL", oCALL: "CALL",
oRETURN: "RETURN", oRETURN: "RETURN",
// 0x70 range - other
oLOG: "LOG",
oSUICIDE: "SUICIDE",
} }
func (o OpCode) String() string { func (o OpCode) String() string {
@ -215,20 +223,30 @@ type Memory struct {
func (m *Memory) Set(offset, size int64, value []byte) { func (m *Memory) Set(offset, size int64, value []byte) {
totSize := offset + size totSize := offset + size
lenSize := int64(len(m.store)) lenSize := int64(len(m.store) - 1)
if totSize > lenSize { if totSize > lenSize {
// Calculate the diff between the sizes // Calculate the diff between the sizes
diff := totSize - lenSize diff := totSize - lenSize
if diff > 0 { if diff > 0 {
// Create a new empty slice and append it // Create a new empty slice and append it
newSlice := make([]byte, diff+1) newSlice := make([]byte, diff-1)
// Resize slice // Resize slice
m.store = append(m.store, newSlice...) 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 { func (m *Memory) Get(offset, size int64) []byte {
return m.store[offset : offset+size] 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("###########")
}

View File

@ -2,7 +2,7 @@ package ethchain
import ( import (
_ "bytes" _ "bytes"
"fmt" _ "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go" _ "github.com/obscuren/secp256k1-go"
"log" "log"
@ -36,6 +36,8 @@ func NewVm(state *State, vars RuntimeVars) *Vm {
return &Vm{vars: vars, state: state} return &Vm{vars: vars, state: state}
} }
var Pow256 = ethutil.BigPow(2, 256)
func (vm *Vm) RunClosure(closure *Closure) []byte { func (vm *Vm) RunClosure(closure *Closure) []byte {
// If the amount of gas supplied is less equal to 0 // If the amount of gas supplied is less equal to 0
if closure.GetGas().Cmp(big.NewInt(0)) <= 0 { if closure.GetGas().Cmp(big.NewInt(0)) <= 0 {
@ -48,9 +50,10 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
stack := NewStack() stack := NewStack()
// Instruction pointer // Instruction pointer
pc := int64(0) pc := int64(0)
// Current address // Current step count
//addr := vars.address
step := 0 step := 0
// The base for all big integer arithmetic
base := new(big.Int)
if ethutil.Config.Debug { if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n") ethutil.Config.Log.Debugf("# op\n")
@ -75,27 +78,171 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
} }
switch op { switch op {
case oLOG:
stack.Print()
mem.Print()
case oSTOP: // Stop the closure case oSTOP: // Stop the closure
return closure.Return(nil) 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 case oPUSH: // Push PC+1 on to the stack
pc++ pc++
val := closure.GetMem(pc).BigInt() val := closure.GetMem(pc).BigInt()
stack.Push(val) 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 case oMSTORE: // Store the value at stack top-1 in to memory at location stack top
// Pop value of the stack // Pop value of the stack
val, mStart := stack.Popn() val, mStart := stack.Popn()
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) 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: // 0x60 range
offset := stack.Pop()
mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
case oCALL: case oCALL:
// Pop return size and offset // Pop return size and offset
retSize, retOffset := stack.Popn() retSize, retOffset := stack.Popn()
// Pop input size and offset // Pop input size and offset
inSize, inOffset := stack.Popn() inSize, inOffset := stack.Popn()
// TODO remove me. // Get the arguments from the memory
fmt.Sprintln(inSize, inOffset) args := mem.Get(inOffset.Int64(), inSize.Int64())
// Pop gas and value of the stack. // Pop gas and value of the stack.
gas, value := stack.Popn() gas, value := stack.Popn()
// Closure addr // Closure addr
@ -105,7 +252,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// Create a new callable closure // Create a new callable closure
closure := NewClosure(closure, contract, vm.state, gas, value) closure := NewClosure(closure, contract, vm.state, gas, value)
// Executer the closure and get the return value (if any) // 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) mem.Set(retOffset.Int64(), retSize.Int64(), ret)
case oRETURN: case oRETURN:
@ -113,6 +260,25 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
ret := mem.Get(offset.Int64(), size.Int64()) ret := mem.Get(offset.Int64(), size.Int64())
return closure.Return(ret) 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++ pc++

View File

@ -1,6 +1,7 @@
package ethchain package ethchain
import ( import (
"bytes"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
@ -119,11 +120,13 @@ func TestRun3(t *testing.T) {
"PUSH", "300", "PUSH", "300",
"PUSH", "0", "PUSH", "0",
"MSTORE", "MSTORE",
"PUSH", "300",
"PUSH", "31", "PUSH", "32",
"MSTORE", "CALLDATA",
"PUSH", "62",
"PUSH", "64",
"PUSH", "0", "PUSH", "0",
"LOG",
"RETURN", "RETURN",
}) })
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
@ -133,14 +136,21 @@ func TestRun3(t *testing.T) {
state.UpdateContract(contract) state.UpdateContract(contract)
callerScript := Compile([]string{ 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", "0", // ret offset
"PUSH", "32", // arg size "PUSH", "32", // arg size
"PUSH", "63", // arg offset "PUSH", "65", // arg offset
"PUSH", "1000", /// Gas "PUSH", "1000", /// Gas
"PUSH", "0", /// value "PUSH", "0", /// value
"PUSH", string(addr), // Sender "PUSH", string(addr), // Sender
"CALL", "CALL",
"PUSH", "64",
"PUSH", "0",
"RETURN",
}) })
callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) 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 // XXX Tx data? Could be just an argument to the closure instead
txData: nil, 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)
}
} }

View File

@ -65,12 +65,16 @@ var OpCodes = map[string]byte{
"JUMP": 0x59, "JUMP": 0x59,
"JUMPI": 0x5a, "JUMPI": 0x5a,
"PC": 0x5b, "PC": 0x5b,
"MEMSIZE": 0x5c, "MSIZE": 0x5c,
// 0x60 range - closures // 0x60 range - closures
"CREATE": 0x60, "CREATE": 0x60,
"CALL": 0x61, "CALL": 0x61,
"RETURN": 0x62, "RETURN": 0x62,
// 0x70 range - other
"LOG": 0x70,
"SUICIDE": 0x7f,
} }
func IsOpCode(s string) bool { func IsOpCode(s string) bool {