Updated VM

This commit is contained in:
obscuren 2014-04-20 01:31:01 +02:00
parent a96c8c8af9
commit 6930260962
4 changed files with 90 additions and 37 deletions

View File

@ -8,7 +8,7 @@ import (
) )
type Callee interface { type Callee interface {
ReturnGas(*big.Int, *State) ReturnGas(*big.Int, *big.Int, *State)
Address() []byte Address() []byte
} }
@ -83,18 +83,16 @@ func (c *Closure) Return(ret []byte) []byte {
// If no callee is present return it to // If no callee is present return it to
// the origin (i.e. contract or tx) // the origin (i.e. contract or tx)
if c.callee != nil { if c.callee != nil {
c.callee.ReturnGas(c.Gas, c.State) c.callee.ReturnGas(c.Gas, c.Price, c.State)
} else { } else {
c.object.ReturnGas(c.Gas, c.State) c.object.ReturnGas(c.Gas, c.Price, c.State)
// TODO incase it's a POST contract we gotta serialise the contract again.
// But it's not yet defined
} }
return ret return ret
} }
// Implement the Callee interface // 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 // Return the gas to the closure
c.Gas.Add(c.Gas, gas) c.Gas.Add(c.Gas, gas)
} }

View File

@ -1,6 +1,7 @@
package ethchain package ethchain
import ( import (
"fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big" "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 // Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(val *big.Int, state *State) { func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
c.AddAmount(val) remainder := new(big.Int).Mul(gas, price)
c.AddAmount(remainder)
} }
func (c *StateObject) AddAmount(amount *big.Int) { func (c *StateObject) AddAmount(amount *big.Int) {
@ -82,18 +84,33 @@ func (c *StateObject) SubAmount(amount *big.Int) {
c.Amount.Sub(c.Amount, amount) 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 { func (c *StateObject) Address() []byte {
return c.address return c.address
} }
// Returns the main script body
func (c *StateObject) Script() []byte { func (c *StateObject) Script() []byte {
return c.script return c.script
} }
// Returns the initialization script
func (c *StateObject) Init() []byte { func (c *StateObject) Init() []byte {
return c.initScript return c.initScript
} }
// State object encoding methods
func (c *StateObject) RlpEncode() []byte { func (c *StateObject) RlpEncode() []byte {
var root interface{} var root interface{}
if c.state != nil { if c.state != nil {
@ -113,6 +130,7 @@ func (c *StateObject) RlpDecode(data []byte) {
c.script = decoder.Get(3).Bytes() c.script = decoder.Get(3).Bytes()
} }
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *State) *StateObject { func MakeContract(tx *Transaction, state *State) *StateObject {
// Create contract if there's no recipient // Create contract if there's no recipient
if tx.IsContract() { if tx.IsContract() {

View File

@ -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) gas := new(big.Int)
useGas := func(amount *big.Int) { useGas := func(amount *big.Int) {
gas.Add(gas, new(big.Int).Mul(amount, closure.Price)) gas.Add(gas, amount)
} }
switch op { 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) 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) closure.Gas.Sub(closure.Gas, gas)
switch op { switch op {
@ -157,7 +158,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
x, y := stack.Popn() x, y := stack.Popn()
// (x + y) % 2 ** 256 // (x + y) % 2 ** 256
base.Add(x, y) base.Add(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oSUB: case oSUB:
@ -165,7 +165,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
x, y := stack.Popn() x, y := stack.Popn()
// (x - y) % 2 ** 256 // (x - y) % 2 ** 256
base.Sub(x, y) base.Sub(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oMUL: case oMUL:
@ -173,7 +172,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
x, y := stack.Popn() x, y := stack.Popn()
// (x * y) % 2 ** 256 // (x * y) % 2 ** 256
base.Mul(x, y) base.Mul(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oDIV: case oDIV:
@ -325,7 +323,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case oCALLDATASIZE: case oCALLDATASIZE:
stack.Push(big.NewInt(int64(len(closure.Args)))) stack.Push(big.NewInt(int64(len(closure.Args))))
case oGASPRICE: case oGASPRICE:
// TODO stack.Push(closure.Price)
// 0x40 range // 0x40 range
case oPREVHASH: case oPREVHASH:
@ -339,7 +337,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case oDIFFICULTY: case oDIFFICULTY:
stack.Push(vm.vars.Diff) stack.Push(vm.vars.Diff)
case oGASLIMIT: case oGASLIMIT:
// TODO // TODO
stack.Push(big.NewInt(0))
// 0x50 range // 0x50 range
case oPUSH: // Push PC+1 on to the stack 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: case oJUMP:
require(1) require(1)
pc = stack.Pop() 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: case oJUMPI:
require(2) require(2)
cond, pos := stack.Popn() cond, pos := stack.Popn()
if cond.Cmp(ethutil.BigTrue) == 0 { if cond.Cmp(ethutil.BigTrue) == 0 {
pc = pos pc = pos
pc.Sub(pc, ethutil.Big1)
} }
case oPC: case oPC:
stack.Push(pc) stack.Push(pc)
@ -421,21 +423,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
inSize, inOffset := stack.Popn() inSize, inOffset := stack.Popn()
// Pop return size and offset // Pop return size and offset
retSize, retOffset := stack.Popn() 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 // Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
// Fetch the contract which will serve as the closure body // Fetch the contract which will serve as the closure body
contract := vm.state.GetContract(addr.Bytes()) 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: case oRETURN:
require(2) require(2)
size, offset := stack.Popn() size, offset := stack.Popn()

View File

@ -86,9 +86,9 @@ func TestRun4(t *testing.T) {
int32 a = 10 int32 a = 10
int32 b = 20 int32 b = 20
if a > b { if a > b {
int32 c = this.caller() int32 c = this.Caller()
} }
exit() Exit()
`), false) `), false)
script := ethutil.Assemble(asm...) script := ethutil.Assemble(asm...)
tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil) tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil)
@ -103,8 +103,9 @@ func TestRun4(t *testing.T) {
store[1000] = 10^20 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 { if store[1001] > 20 {
store[1001] = 10^50 store[1001] = 10^50
@ -112,8 +113,18 @@ func TestRun4(t *testing.T) {
int8 ret = 0 int8 ret = 0
int8 arg = 10 int8 arg = 10
store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret)
call(store[1002], 0, 100000000, arg, ret)
big t
for int8 i = 0; i < 10; i++ {
t = i
}
if 10 > 20 {
int8 shouldnt = 2
} else {
int8 should = 1
}
`), false) `), false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -125,10 +136,17 @@ func TestRun4(t *testing.T) {
callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil) callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil)
// Contract addr as test address // Contract addr as test address
gas := big.NewInt(1000)
gasPrice := big.NewInt(10)
account := NewAccount(ContractAddr, big.NewInt(10000000)) account := NewAccount(ContractAddr, big.NewInt(10000000))
fmt.Println(account) fmt.Println("account.Amount =", account.Amount)
c := MakeContract(callerTx, state) 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{ vm := NewVm(state, RuntimeVars{
Origin: account.Address(), Origin: account.Address(),
@ -138,11 +156,11 @@ func TestRun4(t *testing.T) {
Time: 1, Time: 1,
Diff: big.NewInt(256), Diff: big.NewInt(256),
}) })
_, e := callerClosure.Call(vm, nil, nil) _, e = callerClosure.Call(vm, nil, nil)
if e != nil { if e != nil {
fmt.Println("error", e) fmt.Println("error", e)
} }
fmt.Println(account) fmt.Println("account.Amount =", account.Amount)
} }
func TestRun5(t *testing.T) { func TestRun5(t *testing.T) {
@ -156,6 +174,5 @@ func TestRun5(t *testing.T) {
} }
exit() exit()
`), false) `), false)
script := ethutil.Assemble(asm...) ethutil.Assemble(asm...)
fmt.Println(Disassemble(script))
} }