Updated VM
This commit is contained in:
		@@ -8,7 +8,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Callee interface {
 | 
			
		||||
	ReturnGas(*big.Int, *State)
 | 
			
		||||
	ReturnGas(*big.Int, *big.Int, *State)
 | 
			
		||||
	Address() []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -83,18 +83,16 @@ func (c *Closure) Return(ret []byte) []byte {
 | 
			
		||||
	// 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)
 | 
			
		||||
		c.callee.ReturnGas(c.Gas, c.Price, 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
 | 
			
		||||
		c.object.ReturnGas(c.Gas, c.Price, c.State)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement the Callee interface
 | 
			
		||||
func (c *Closure) ReturnGas(gas *big.Int, state *State) {
 | 
			
		||||
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
 | 
			
		||||
	// Return the gas to the closure
 | 
			
		||||
	c.Gas.Add(c.Gas, gas)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package ethchain
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/ethereum/eth-go/ethutil"
 | 
			
		||||
	"math/big"
 | 
			
		||||
)
 | 
			
		||||
@@ -70,8 +71,9 @@ func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
 | 
			
		||||
func (c *StateObject) ReturnGas(val *big.Int, state *State) {
 | 
			
		||||
	c.AddAmount(val)
 | 
			
		||||
func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
 | 
			
		||||
	remainder := new(big.Int).Mul(gas, price)
 | 
			
		||||
	c.AddAmount(remainder)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *StateObject) AddAmount(amount *big.Int) {
 | 
			
		||||
@@ -82,18 +84,33 @@ func (c *StateObject) SubAmount(amount *big.Int) {
 | 
			
		||||
	c.Amount.Sub(c.Amount, amount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
 | 
			
		||||
	total := new(big.Int).Mul(gas, price)
 | 
			
		||||
	if total.Cmp(c.Amount) > 0 {
 | 
			
		||||
		return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.SubAmount(total)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the address of the contract/account
 | 
			
		||||
func (c *StateObject) Address() []byte {
 | 
			
		||||
	return c.address
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the main script body
 | 
			
		||||
func (c *StateObject) Script() []byte {
 | 
			
		||||
	return c.script
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the initialization script
 | 
			
		||||
func (c *StateObject) Init() []byte {
 | 
			
		||||
	return c.initScript
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// State object encoding methods
 | 
			
		||||
func (c *StateObject) RlpEncode() []byte {
 | 
			
		||||
	var root interface{}
 | 
			
		||||
	if c.state != nil {
 | 
			
		||||
@@ -113,6 +130,7 @@ func (c *StateObject) RlpDecode(data []byte) {
 | 
			
		||||
	c.script = decoder.Get(3).Bytes()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Converts an transaction in to a state object
 | 
			
		||||
func MakeContract(tx *Transaction, state *State) *StateObject {
 | 
			
		||||
	// Create contract if there's no recipient
 | 
			
		||||
	if tx.IsContract() {
 | 
			
		||||
 
 | 
			
		||||
@@ -102,10 +102,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
			}
 | 
			
		||||
		*/
 | 
			
		||||
 | 
			
		||||
		// TODO Get each instruction cost properly
 | 
			
		||||
		gas := new(big.Int)
 | 
			
		||||
		useGas := func(amount *big.Int) {
 | 
			
		||||
			gas.Add(gas, new(big.Int).Mul(amount, closure.Price))
 | 
			
		||||
			gas.Add(gas, amount)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch op {
 | 
			
		||||
@@ -142,6 +141,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
 | 
			
		||||
			return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Sub the amount of gas from the remaining
 | 
			
		||||
		closure.Gas.Sub(closure.Gas, gas)
 | 
			
		||||
 | 
			
		||||
		switch op {
 | 
			
		||||
@@ -157,7 +158,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
			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:
 | 
			
		||||
@@ -165,7 +165,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
			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:
 | 
			
		||||
@@ -173,7 +172,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
			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:
 | 
			
		||||
@@ -325,7 +323,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
		case oCALLDATASIZE:
 | 
			
		||||
			stack.Push(big.NewInt(int64(len(closure.Args))))
 | 
			
		||||
		case oGASPRICE:
 | 
			
		||||
			// TODO
 | 
			
		||||
			stack.Push(closure.Price)
 | 
			
		||||
 | 
			
		||||
			// 0x40 range
 | 
			
		||||
		case oPREVHASH:
 | 
			
		||||
@@ -339,7 +337,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
		case oDIFFICULTY:
 | 
			
		||||
			stack.Push(vm.vars.Diff)
 | 
			
		||||
		case oGASLIMIT:
 | 
			
		||||
		// TODO
 | 
			
		||||
			// TODO
 | 
			
		||||
			stack.Push(big.NewInt(0))
 | 
			
		||||
 | 
			
		||||
		// 0x50 range
 | 
			
		||||
		case oPUSH: // Push PC+1 on to the stack
 | 
			
		||||
@@ -399,11 +398,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
		case oJUMP:
 | 
			
		||||
			require(1)
 | 
			
		||||
			pc = stack.Pop()
 | 
			
		||||
			// Reduce pc by one because of the increment that's at the end of this for loop
 | 
			
		||||
			pc.Sub(pc, ethutil.Big1)
 | 
			
		||||
		case oJUMPI:
 | 
			
		||||
			require(2)
 | 
			
		||||
			cond, pos := stack.Popn()
 | 
			
		||||
			if cond.Cmp(ethutil.BigTrue) == 0 {
 | 
			
		||||
				pc = pos
 | 
			
		||||
				pc.Sub(pc, ethutil.Big1)
 | 
			
		||||
			}
 | 
			
		||||
		case oPC:
 | 
			
		||||
			stack.Push(pc)
 | 
			
		||||
@@ -421,21 +423,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
 | 
			
		||||
			inSize, inOffset := stack.Popn()
 | 
			
		||||
			// Pop return size and offset
 | 
			
		||||
			retSize, retOffset := stack.Popn()
 | 
			
		||||
			// Make sure there's enough gas
 | 
			
		||||
			if closure.Gas.Cmp(gas) < 0 {
 | 
			
		||||
				stack.Push(ethutil.BigFalse)
 | 
			
		||||
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			// Get the arguments from the memory
 | 
			
		||||
			args := mem.Get(inOffset.Int64(), inSize.Int64())
 | 
			
		||||
			// Fetch the contract which will serve as the closure body
 | 
			
		||||
			contract := vm.state.GetContract(addr.Bytes())
 | 
			
		||||
			// Create a new callable closure
 | 
			
		||||
			closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value)
 | 
			
		||||
			// Executer the closure and get the return value (if any)
 | 
			
		||||
			ret, err := closure.Call(vm, args, hook)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				stack.Push(ethutil.BigFalse)
 | 
			
		||||
			} else {
 | 
			
		||||
				stack.Push(ethutil.BigTrue)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mem.Set(retOffset.Int64(), retSize.Int64(), ret)
 | 
			
		||||
			if contract != nil {
 | 
			
		||||
				// Prepay for the gas
 | 
			
		||||
				// If gas is set to 0 use all remaining gas for the next call
 | 
			
		||||
				if gas.Cmp(big.NewInt(0)) == 0 {
 | 
			
		||||
					gas = closure.Gas
 | 
			
		||||
				}
 | 
			
		||||
				closure.Gas.Sub(closure.Gas, gas)
 | 
			
		||||
				// Create a new callable closure
 | 
			
		||||
				closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value)
 | 
			
		||||
				// Executer the closure and get the return value (if any)
 | 
			
		||||
				ret, err := closure.Call(vm, args, hook)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					stack.Push(ethutil.BigFalse)
 | 
			
		||||
				} else {
 | 
			
		||||
					stack.Push(ethutil.BigTrue)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				mem.Set(retOffset.Int64(), retSize.Int64(), ret)
 | 
			
		||||
			} else {
 | 
			
		||||
				ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
 | 
			
		||||
				stack.Push(ethutil.BigFalse)
 | 
			
		||||
			}
 | 
			
		||||
		case oRETURN:
 | 
			
		||||
			require(2)
 | 
			
		||||
			size, offset := stack.Popn()
 | 
			
		||||
 
 | 
			
		||||
@@ -86,9 +86,9 @@ func TestRun4(t *testing.T) {
 | 
			
		||||
		int32 a = 10
 | 
			
		||||
		int32 b = 20
 | 
			
		||||
		if a > b {
 | 
			
		||||
			int32 c = this.caller()
 | 
			
		||||
			int32 c = this.Caller()
 | 
			
		||||
		}
 | 
			
		||||
		exit()
 | 
			
		||||
		Exit()
 | 
			
		||||
	`), false)
 | 
			
		||||
	script := ethutil.Assemble(asm...)
 | 
			
		||||
	tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil)
 | 
			
		||||
@@ -103,8 +103,9 @@ func TestRun4(t *testing.T) {
 | 
			
		||||
			store[1000] = 10^20
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		store[1001] = this.value() * 20
 | 
			
		||||
		store[this.origin()] = store[this.origin()] + 1000
 | 
			
		||||
 | 
			
		||||
		store[1001] = this.Value() * 20
 | 
			
		||||
		store[this.Origin()] = store[this.Origin()] + 1000
 | 
			
		||||
 | 
			
		||||
		if store[1001] > 20 {
 | 
			
		||||
			store[1001] = 10^50
 | 
			
		||||
@@ -112,8 +113,18 @@ func TestRun4(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
		int8 ret = 0
 | 
			
		||||
		int8 arg = 10
 | 
			
		||||
		store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f"
 | 
			
		||||
		call(store[1002], 0, 100000000, arg, ret)
 | 
			
		||||
		Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret)
 | 
			
		||||
 | 
			
		||||
		big t
 | 
			
		||||
		for int8 i = 0; i < 10; i++ {
 | 
			
		||||
			t = i
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if 10 > 20 {
 | 
			
		||||
			int8 shouldnt = 2
 | 
			
		||||
		} else {
 | 
			
		||||
			int8 should = 1
 | 
			
		||||
		}
 | 
			
		||||
	`), false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
@@ -125,10 +136,17 @@ func TestRun4(t *testing.T) {
 | 
			
		||||
	callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil)
 | 
			
		||||
 | 
			
		||||
	// Contract addr as test address
 | 
			
		||||
	gas := big.NewInt(1000)
 | 
			
		||||
	gasPrice := big.NewInt(10)
 | 
			
		||||
	account := NewAccount(ContractAddr, big.NewInt(10000000))
 | 
			
		||||
	fmt.Println(account)
 | 
			
		||||
	fmt.Println("account.Amount =", account.Amount)
 | 
			
		||||
	c := MakeContract(callerTx, state)
 | 
			
		||||
	callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), big.NewInt(10), big.NewInt(0))
 | 
			
		||||
	e := account.ConvertGas(gas, gasPrice)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println("account.Amount =", account.Amount)
 | 
			
		||||
	callerClosure := NewClosure(account, c, c.script, state, gas, gasPrice, big.NewInt(0))
 | 
			
		||||
 | 
			
		||||
	vm := NewVm(state, RuntimeVars{
 | 
			
		||||
		Origin:      account.Address(),
 | 
			
		||||
@@ -138,11 +156,11 @@ func TestRun4(t *testing.T) {
 | 
			
		||||
		Time:        1,
 | 
			
		||||
		Diff:        big.NewInt(256),
 | 
			
		||||
	})
 | 
			
		||||
	_, e := callerClosure.Call(vm, nil, nil)
 | 
			
		||||
	_, e = callerClosure.Call(vm, nil, nil)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		fmt.Println("error", e)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(account)
 | 
			
		||||
	fmt.Println("account.Amount =", account.Amount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRun5(t *testing.T) {
 | 
			
		||||
@@ -156,6 +174,5 @@ func TestRun5(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		exit()
 | 
			
		||||
	`), false)
 | 
			
		||||
	script := ethutil.Assemble(asm...)
 | 
			
		||||
	fmt.Println(Disassemble(script))
 | 
			
		||||
	ethutil.Assemble(asm...)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user