added a transfer method to vm env

This commit is contained in:
obscuren 2014-10-22 15:22:21 +02:00
parent 27cb0750c1
commit b5beb1aac1
9 changed files with 59 additions and 36 deletions

View File

@ -28,7 +28,7 @@ func (self *State) Dump() []byte {
self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) { self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) {
stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes()) stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes())
account := Account{Balance: stateObject.Balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)} account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)
stateObject.EachStorage(func(key string, value *ethutil.Value) { stateObject.EachStorage(func(key string, value *ethutil.Value) {

View File

@ -33,7 +33,7 @@ func New(trie *ethtrie.Trie) *State {
func (self *State) GetBalance(addr []byte) *big.Int { func (self *State) GetBalance(addr []byte) *big.Int {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.Balance return stateObject.balance
} }
return ethutil.Big0 return ethutil.Big0

View File

@ -31,7 +31,7 @@ type StateObject struct {
// Address of the object // Address of the object
address []byte address []byte
// Shared attributes // Shared attributes
Balance *big.Int balance *big.Int
codeHash []byte codeHash []byte
Nonce uint64 Nonce uint64
// Contract related attributes // Contract related attributes
@ -61,7 +61,7 @@ func NewStateObject(addr []byte) *StateObject {
// This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter. // This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
address := ethutil.Address(addr) address := ethutil.Address(addr)
object := &StateObject{address: address, Balance: new(big.Int), gasPool: new(big.Int)} object := &StateObject{address: address, balance: new(big.Int), gasPool: new(big.Int)}
object.State = New(ethtrie.New(ethutil.Config.Db, "")) object.State = New(ethtrie.New(ethutil.Config.Db, ""))
object.storage = make(Storage) object.storage = make(Storage)
object.gasPool = new(big.Int) object.gasPool = new(big.Int)
@ -71,7 +71,7 @@ func NewStateObject(addr []byte) *StateObject {
func NewContract(address []byte, balance *big.Int, root []byte) *StateObject { func NewContract(address []byte, balance *big.Int, root []byte) *StateObject {
contract := NewStateObject(address) contract := NewStateObject(address)
contract.Balance = balance contract.balance = balance
contract.State = New(ethtrie.New(ethutil.Config.Db, string(root))) contract.State = New(ethtrie.New(ethutil.Config.Db, string(root)))
return contract return contract
@ -86,7 +86,7 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject {
func (self *StateObject) MarkForDeletion() { func (self *StateObject) MarkForDeletion() {
self.remove = true self.remove = true
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Balance) statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.balance)
} }
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
@ -174,22 +174,26 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
return ethutil.NewValueFromBytes([]byte{c.Code[pc.Int64()]}) return ethutil.NewValueFromBytes([]byte{c.Code[pc.Int64()]})
} }
func (c *StateObject) AddAmount(amount *big.Int) { func (c *StateObject) AddBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Add(c.Balance, amount)) c.SetBalance(new(big.Int).Add(c.balance, amount))
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Balance, amount) statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.balance, amount)
} }
func (c *StateObject) AddAmount(amount *big.Int) { c.AddBalance(amount) }
func (c *StateObject) SubAmount(amount *big.Int) { func (c *StateObject) SubBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Sub(c.Balance, amount)) c.SetBalance(new(big.Int).Sub(c.balance, amount))
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Balance, amount) statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.balance, amount)
} }
func (c *StateObject) SubAmount(amount *big.Int) { c.SubBalance(amount) }
func (c *StateObject) SetBalance(amount *big.Int) { func (c *StateObject) SetBalance(amount *big.Int) {
c.Balance = amount c.balance = amount
} }
func (self *StateObject) Balance() *big.Int { return self.balance }
// //
// Gas setters and getters // Gas setters and getters
// //
@ -198,8 +202,8 @@ func (c *StateObject) SetBalance(amount *big.Int) {
func (c *StateObject) ReturnGas(gas, price *big.Int) {} func (c *StateObject) ReturnGas(gas, price *big.Int) {}
func (c *StateObject) ConvertGas(gas, price *big.Int) error { func (c *StateObject) ConvertGas(gas, price *big.Int) error {
total := new(big.Int).Mul(gas, price) total := new(big.Int).Mul(gas, price)
if total.Cmp(c.Balance) > 0 { if total.Cmp(c.balance) > 0 {
return fmt.Errorf("insufficient amount: %v, %v", c.Balance, total) return fmt.Errorf("insufficient amount: %v, %v", c.balance, total)
} }
c.SubAmount(total) c.SubAmount(total)
@ -232,12 +236,12 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
rGas := new(big.Int).Set(gas) rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price) rGas.Mul(rGas, price)
self.Balance.Sub(self.Balance, rGas) self.balance.Sub(self.balance, rGas)
} }
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address()) stateObject := NewStateObject(self.Address())
stateObject.Balance.Set(self.Balance) stateObject.balance.Set(self.balance)
stateObject.codeHash = ethutil.CopyBytes(self.codeHash) stateObject.codeHash = ethutil.CopyBytes(self.codeHash)
stateObject.Nonce = self.Nonce stateObject.Nonce = self.Nonce
if self.State != nil { if self.State != nil {
@ -281,7 +285,7 @@ func (self *StateObject) Object() *StateObject {
// Debug stuff // Debug stuff
func (self *StateObject) CreateOutputForDiff() { func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce) fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce)
self.EachStorage(func(addr string, value *ethutil.Value) { self.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("%x %x\n", addr, value.Bytes()) fmt.Printf("%x %x\n", addr, value.Bytes())
}) })
@ -300,7 +304,7 @@ func (c *StateObject) RlpEncode() []byte {
root = "" root = ""
} }
return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, c.CodeHash()}) return ethutil.Encode([]interface{}{c.Nonce, c.balance, root, c.CodeHash()})
} }
func (c *StateObject) CodeHash() ethutil.Bytes { func (c *StateObject) CodeHash() ethutil.Bytes {
@ -316,7 +320,7 @@ func (c *StateObject) RlpDecode(data []byte) {
decoder := ethutil.NewValueFromBytes(data) decoder := ethutil.NewValueFromBytes(data)
c.Nonce = decoder.Get(0).Uint() c.Nonce = decoder.Get(0).Uint()
c.Balance = decoder.Get(1).BigInt() c.balance = decoder.Get(1).BigInt()
c.State = New(ethtrie.New(ethutil.Config.Db, decoder.Get(2).Interface())) c.State = New(ethtrie.New(ethutil.Config.Db, decoder.Get(2).Interface()))
c.storage = make(map[string]*ethutil.Value) c.storage = make(map[string]*ethutil.Value)
c.gasPool = new(big.Int) c.gasPool = new(big.Int)

View File

@ -674,7 +674,7 @@ func (p *Peer) pushPeers() {
func (self *Peer) pushStatus() { func (self *Peer) pushStatus() {
msg := ethwire.NewMessage(ethwire.MsgStatusTy, []interface{}{ msg := ethwire.NewMessage(ethwire.MsgStatusTy, []interface{}{
//uint32(ProtocolVersion), uint32(ProtocolVersion),
uint32(NetVersion), uint32(NetVersion),
self.ethereum.ChainManager().TD, self.ethereum.ChainManager().TD,
self.ethereum.ChainManager().CurrentBlock.Hash(), self.ethereum.ChainManager().CurrentBlock.Hash(),

View File

@ -50,11 +50,14 @@ func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) BlockHash() []byte { return nil } func (self *Env) BlockHash() []byte { return nil }
func (self *Env) State() *ethstate.State { return self.state } func (self *Env) State() *ethstate.State { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) error {
return nil
}
func RunVm(state *ethstate.State, env, exec map[string]string) ([]byte, *big.Int, error) { func RunVm(state *ethstate.State, env, exec map[string]string) ([]byte, *big.Int, error) {
address := FromHex(exec["address"]) address := FromHex(exec["address"])
caller := state.GetOrNewStateObject(FromHex(exec["caller"])) caller := state.GetOrNewStateObject(FromHex(exec["caller"]))
caller.Balance = ethutil.Big(exec["value"]) caller.SetBalance(ethutil.Big(exec["value"]))
evm := vm.New(NewEnvFromMap(state, env, exec), vm.DebugVmTy) evm := vm.New(NewEnvFromMap(state, env, exec), vm.DebugVmTy)

View File

@ -18,7 +18,7 @@ type Account struct {
func StateObjectFromAccount(addr string, account Account) *ethstate.StateObject { func StateObjectFromAccount(addr string, account Account) *ethstate.StateObject {
obj := ethstate.NewStateObject(ethutil.Hex2Bytes(addr)) obj := ethstate.NewStateObject(ethutil.Hex2Bytes(addr))
obj.Balance = ethutil.Big(account.Balance) obj.SetBalance(ethutil.Big(account.Balance))
if ethutil.IsHex(account.Code) { if ethutil.IsHex(account.Code) {
account.Code = account.Code[2:] account.Code = account.Code[2:]
@ -44,9 +44,6 @@ func RunVmTest(p string, t *testing.T) {
helper.CreateFileTests(t, p, &tests) helper.CreateFileTests(t, p, &tests)
for name, test := range tests { for name, test := range tests {
if name != "CallRecursiveBomb" {
continue
}
state := ethstate.New(helper.NewTrie()) state := ethstate.New(helper.NewTrie())
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(addr, account) obj := StateObjectFromAccount(addr, account)
@ -92,7 +89,6 @@ func TestVMArithmetic(t *testing.T) {
} }
func TestVMSystemOperation(t *testing.T) { func TestVMSystemOperation(t *testing.T) {
helper.Logger.SetLogLevel(5)
const fn = "../files/vmtests/vmSystemOperationsTest.json" const fn = "../files/vmtests/vmSystemOperationsTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }

View File

@ -1,6 +1,7 @@
package vm package vm
import ( import (
"errors"
"math/big" "math/big"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
@ -18,9 +19,28 @@ type Environment interface {
Difficulty() *big.Int Difficulty() *big.Int
BlockHash() []byte BlockHash() []byte
GasLimit() *big.Int GasLimit() *big.Int
Transfer(from, to Account, amount *big.Int) error
} }
type Object interface { type Object interface {
GetStorage(key *big.Int) *ethutil.Value GetStorage(key *big.Int) *ethutil.Value
SetStorage(key *big.Int, value *ethutil.Value) SetStorage(key *big.Int, value *ethutil.Value)
} }
type Account interface {
SubBalance(amount *big.Int)
AddBalance(amount *big.Int)
Balance() *big.Int
}
// generic transfer method
func Transfer(from, to Account, amount *big.Int) error {
if from.Balance().Cmp(amount) < 0 {
return errors.New("Insufficient balance in account")
}
from.SubBalance(amount)
to.AddBalance(amount)
return nil
}

View File

@ -48,17 +48,17 @@ func (self *Execution) exec(code, caddr []byte, caller ClosureRef) (ret []byte,
Value: self.value, Value: self.value,
}) })
object := caller.Object() from, to := caller.Object(), env.State().GetOrNewStateObject(self.address)
if object.Balance.Cmp(self.value) < 0 { err = env.Transfer(from, to, self.value)
if err != nil {
caller.ReturnGas(self.Gas, self.price) caller.ReturnGas(self.Gas, self.price)
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, object.Balance) err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance)
} else { } else {
stateObject := env.State().GetOrNewStateObject(self.address) self.object = to
self.object = stateObject
caller.Object().SubAmount(self.value) //caller.Object().SubAmount(self.value)
stateObject.AddAmount(self.value) //stateObject.AddAmount(self.value)
// Pre-compiled contracts (address.go) 1, 2 & 3. // Pre-compiled contracts (address.go) 1, 2 & 3.
naddr := ethutil.BigD(caddr).Uint64() naddr := ethutil.BigD(caddr).Uint64()
@ -69,7 +69,7 @@ func (self *Execution) exec(code, caddr []byte, caller ClosureRef) (ret []byte,
} }
} else { } else {
// Create a new callable closure // Create a new callable closure
c := NewClosure(msg, caller, stateObject, code, self.Gas, self.price) c := NewClosure(msg, caller, to, code, self.Gas, self.price)
c.exe = self c.exe = self
if self.vm.Depth() == MaxCallDepth { if self.vm.Depth() == MaxCallDepth {

View File

@ -692,7 +692,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes()) receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes())
receiver.AddAmount(closure.object.Balance) receiver.AddAmount(closure.object.Balance())
closure.object.MarkForDeletion() closure.object.MarkForDeletion()