Implemented closure arguments
This commit is contained in:
parent
9cf8ce9ef8
commit
fa1db8d2dc
@ -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("###########")
|
||||
}
|
||||
|
184
ethchain/vm.go
184
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++
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user