Closures and vm based on closures
Status: Work in progress
This commit is contained in:
parent
82d0f65dab
commit
38ea6a6d5d
68
ethchain/closure.go
Normal file
68
ethchain/closure.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
// TODO Re write VM to use values instead of big integers?
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Callee interface {
|
||||||
|
ReturnGas(*big.Int, *State)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClosureBody interface {
|
||||||
|
Callee
|
||||||
|
ethutil.RlpEncodable
|
||||||
|
GetMem(int64) *ethutil.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic inline closure object which implement the 'closure' interface
|
||||||
|
type Closure struct {
|
||||||
|
callee Callee
|
||||||
|
object ClosureBody
|
||||||
|
state *State
|
||||||
|
|
||||||
|
gas *big.Int
|
||||||
|
val *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retuns the x element in data slice
|
||||||
|
func (c *Closure) GetMem(x int64) *ethutil.Value {
|
||||||
|
m := c.object.GetMem(x)
|
||||||
|
if m == nil {
|
||||||
|
return ethutil.EmptyValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the Callee interface
|
||||||
|
func (c *Closure) ReturnGas(gas *big.Int, state *State) {
|
||||||
|
// Return the gas to the closure
|
||||||
|
c.gas.Add(c.gas, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Closure) GetGas() *big.Int {
|
||||||
|
return c.gas
|
||||||
|
}
|
@ -32,13 +32,101 @@ type RuntimeVars struct {
|
|||||||
txData []string
|
txData []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byte {
|
||||||
|
// If the amount of gas supplied is less equal to 0
|
||||||
|
if closure.GetGas().Cmp(big.NewInt(0)) <= 0 {
|
||||||
|
// TODO Do something
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory for the current closure
|
||||||
|
var mem []byte
|
||||||
|
// New stack (should this be shared?)
|
||||||
|
stack := NewStack()
|
||||||
|
// Instruction pointer
|
||||||
|
pc := int64(0)
|
||||||
|
// Current address
|
||||||
|
//addr := vars.address
|
||||||
|
step := 0
|
||||||
|
|
||||||
|
if ethutil.Config.Debug {
|
||||||
|
ethutil.Config.Log.Debugf("# op\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
step++
|
||||||
|
// Get the memory location of pc
|
||||||
|
val := closure.GetMem(pc)
|
||||||
|
// Get the opcode (it must be an opcode!)
|
||||||
|
op := OpCode(val.Uint())
|
||||||
|
if ethutil.Config.Debug {
|
||||||
|
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Get each instruction cost properly
|
||||||
|
fee := new(big.Int)
|
||||||
|
fee.Add(fee, big.NewInt(1000))
|
||||||
|
|
||||||
|
if closure.GetGas().Cmp(fee) < 0 {
|
||||||
|
return closure.Return(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case oSTOP:
|
||||||
|
return closure.Return(nil)
|
||||||
|
case oPUSH:
|
||||||
|
pc++
|
||||||
|
val := closure.GetMem(pc).BigInt()
|
||||||
|
stack.Push(val)
|
||||||
|
case oMSTORE:
|
||||||
|
// Pop value of the stack
|
||||||
|
val := stack.Pop()
|
||||||
|
// Set the bytes to the memory field
|
||||||
|
mem = append(mem, ethutil.BigToBytes(val, 256)...)
|
||||||
|
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)
|
||||||
|
// Pop gas and value of the stack.
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Ensure that memory is large enough to hold the returned data
|
||||||
|
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 {
|
||||||
|
diff := new(big.Int).Sub(totSize, lenSize)
|
||||||
|
newSlice := make([]byte, diff.Int64()+1)
|
||||||
|
mem = append(mem, newSlice...)
|
||||||
|
}
|
||||||
|
copy(mem[retOffset.Int64():retOffset.Int64()+retSize.Int64()+1], ret)
|
||||||
|
case oRETURN:
|
||||||
|
size, offset := stack.Popn()
|
||||||
|
ret := mem[offset.Int64() : offset.Int64()+size.Int64()+1]
|
||||||
|
|
||||||
|
return closure.Return(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old VM code
|
||||||
func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) {
|
func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) {
|
||||||
vm.mem = make(map[string]*big.Int)
|
vm.mem = make(map[string]*big.Int)
|
||||||
vm.stack = NewStack()
|
vm.stack = NewStack()
|
||||||
|
|
||||||
addr := vars.address // tx.Hash()[12:]
|
addr := vars.address // tx.Hash()[12:]
|
||||||
// Instruction pointer
|
// Instruction pointer
|
||||||
pc := 0
|
pc := int64(0)
|
||||||
|
|
||||||
if contract == nil {
|
if contract == nil {
|
||||||
fmt.Println("Contract not found")
|
fmt.Println("Contract not found")
|
||||||
@ -344,7 +432,7 @@ out:
|
|||||||
contract.SetAddr(addr, y)
|
contract.SetAddr(addr, y)
|
||||||
//contract.State().Update(string(idx), string(y))
|
//contract.State().Update(string(idx), string(y))
|
||||||
case oJMP:
|
case oJMP:
|
||||||
x := int(vm.stack.Pop().Uint64())
|
x := vm.stack.Pop().Int64()
|
||||||
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
||||||
pc = x
|
pc = x
|
||||||
pc--
|
pc--
|
||||||
@ -352,7 +440,7 @@ out:
|
|||||||
x := vm.stack.Pop()
|
x := vm.stack.Pop()
|
||||||
// Set pc to x if it's non zero
|
// Set pc to x if it's non zero
|
||||||
if x.Cmp(ethutil.BigFalse) != 0 {
|
if x.Cmp(ethutil.BigFalse) != 0 {
|
||||||
pc = int(x.Uint64())
|
pc = x.Int64()
|
||||||
pc--
|
pc--
|
||||||
}
|
}
|
||||||
case oIND:
|
case oIND:
|
||||||
@ -400,9 +488,9 @@ out:
|
|||||||
|
|
||||||
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
|
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
|
||||||
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
|
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
|
||||||
j := 0
|
j := int64(0)
|
||||||
dataItems := make([]string, int(length.Uint64()))
|
dataItems := make([]string, int(length.Uint64()))
|
||||||
for i := from.Uint64(); i < length.Uint64(); i++ {
|
for i := from.Int64(); i < length.Int64(); i++ {
|
||||||
dataItems[j] = contract.GetMem(j).Str()
|
dataItems[j] = contract.GetMem(j).Str()
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
func TestRun(t *testing.T) {
|
||||||
InitFees()
|
InitFees()
|
||||||
|
|
||||||
@ -104,3 +106,57 @@ func TestRun2(t *testing.T) {
|
|||||||
txData: tx.Data,
|
txData: tx.Data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// XXX Full stack test
|
||||||
|
func TestRun3(t *testing.T) {
|
||||||
|
ethutil.ReadConfig("")
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state := NewState(ethutil.NewTrie(db, ""))
|
||||||
|
|
||||||
|
script := Compile([]string{
|
||||||
|
"PUSH", "300",
|
||||||
|
"MSTORE",
|
||||||
|
"PUSH", "300",
|
||||||
|
"MSTORE",
|
||||||
|
"PUSH", "62",
|
||||||
|
"PUSH", "0",
|
||||||
|
"RETURN",
|
||||||
|
})
|
||||||
|
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||||
|
addr := tx.Hash()[12:]
|
||||||
|
fmt.Printf("addr contract %x\n", addr)
|
||||||
|
contract := MakeContract(tx, state)
|
||||||
|
state.UpdateContract(addr, contract)
|
||||||
|
|
||||||
|
callerScript := Compile([]string{
|
||||||
|
"PUSH", "62", // REND
|
||||||
|
"PUSH", "0", // RSTART
|
||||||
|
"PUSH", "22", // MEND
|
||||||
|
"PUSH", "15", // MSTART
|
||||||
|
"PUSH", "1000", /// Gas
|
||||||
|
"PUSH", "0", /// value
|
||||||
|
"PUSH", string(addr), // Sender
|
||||||
|
"CALL",
|
||||||
|
})
|
||||||
|
callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
|
||||||
|
callerAddr := callerTx.Hash()[12:]
|
||||||
|
executer := NewTransaction(ContractAddr, ethutil.Big("10000"), nil)
|
||||||
|
|
||||||
|
executer.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||||
|
callerClosure := NewClosure(executer, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int))
|
||||||
|
|
||||||
|
vm := &Vm{}
|
||||||
|
vm.RunClosure(callerClosure, state, RuntimeVars{
|
||||||
|
address: callerAddr,
|
||||||
|
blockNumber: 1,
|
||||||
|
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||||
|
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||||
|
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||||
|
time: 1,
|
||||||
|
diff: big.NewInt(256),
|
||||||
|
txValue: big.NewInt(10000),
|
||||||
|
txData: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user