From 0f93da400ab7fd238eb7286f14c229d780f73636 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Apr 2014 01:47:55 +0200 Subject: [PATCH] Added new state object change echanism --- ethchain/state_manager.go | 33 +++++++++++++------ ethchain/state_object.go | 67 +++++++++++++++++++++------------------ ethchain/vm.go | 6 ++-- ethutil/value.go | 7 +++- 4 files changed, 69 insertions(+), 44 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 29c3cd16b1..1ab58386a2 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -50,6 +50,10 @@ type StateManager struct { // Comparative state it used for comparing and validating end // results compState *State + + // It's generally know that a map is faster for small lookups than arrays + // we'll eventually have to make a decision if the map grows too large + watchedAddresses map[string]bool } func NewStateManager(ethereum EthManager) *StateManager { @@ -60,6 +64,7 @@ func NewStateManager(ethereum EthManager) *StateManager { Ethereum: ethereum, stateObjectCache: NewStateObjectCache(), bc: ethereum.BlockChain(), + watchedAddresses: make(map[string]bool), } sm.procState = ethereum.BlockChain().CurrentBlock.State() return sm @@ -309,18 +314,9 @@ func (sm *StateManager) Stop() { } func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { - // Recovering function in case the VM had any errors - /* - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() - */ - caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) - vm := NewVm(sm.procState, RuntimeVars{ + vm := NewVm(sm.procState, sm, RuntimeVars{ Origin: caller.Address(), BlockNumber: block.BlockInfo().Number, PrevHash: block.PrevHash, @@ -333,5 +329,22 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans // Update the account (refunds) sm.procState.UpdateStateObject(caller) + sm.Changed(caller) sm.procState.UpdateStateObject(object) + sm.Changed(object) +} + +// Watch a specific address +func (sm *StateManager) Watch(addr []byte) { + if !sm.watchedAddresses[string(addr)] { + sm.watchedAddresses[string(addr)] = true + } +} + +// The following objects are used when changing a value and using the "watched" attribute +// to determine whether the reactor should be used to notify any subscribers on the address +func (sm *StateManager) Changed(stateObject *StateObject) { + if sm.watchedAddresses[string(stateObject.Address())] { + sm.Ethereum.Reactor().Post("addressChanged", stateObject) + } } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index f562e5b04d..8d86ef44ee 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -18,6 +18,28 @@ type StateObject struct { initScript []byte } +// 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() { + // FIXME + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(addr, value, []byte("")) + state.UpdateStateObject(contract) + + contract.script = tx.Data + contract.initScript = tx.Init + + state.UpdateStateObject(contract) + + return contract + } + + return nil +} + func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { contract := &StateObject{address: address, Amount: Amount, Nonce: 0} contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) @@ -39,6 +61,10 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject { return object } +func (c *StateObject) State() *State { + return c.state +} + func (c *StateObject) Addr(addr []byte) *ethutil.Value { return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) } @@ -47,8 +73,10 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) } -func (c *StateObject) State() *State { - return c.state +func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { + addr := ethutil.BigToBytes(num, 256) + c.SetAddr(addr, val) + //c.state.trie.Update(string(addr), string(val.Encode())) } func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { @@ -65,11 +93,6 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) } -func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { - addr := ethutil.BigToBytes(num, 256) - c.state.trie.Update(string(addr), string(val.Encode())) -} - // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { remainder := new(big.Int).Mul(gas, price) @@ -77,11 +100,15 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { } func (c *StateObject) AddAmount(amount *big.Int) { - c.Amount.Add(c.Amount, amount) + c.SetAmount(new(big.Int).Add(c.Amount, amount)) } func (c *StateObject) SubAmount(amount *big.Int) { - c.Amount.Sub(c.Amount, amount) + c.SetAmount(new(big.Int).Sub(c.Amount, amount)) +} + +func (c *StateObject) SetAmount(amount *big.Int) { + c.Amount = amount } func (c *StateObject) ConvertGas(gas, price *big.Int) error { @@ -130,28 +157,6 @@ 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() { - // FIXME - addr := tx.Hash()[12:] - - value := tx.Value - contract := NewContract(addr, value, []byte("")) - state.UpdateStateObject(contract) - - contract.script = tx.Data - contract.initScript = tx.Init - - state.UpdateStateObject(contract) - - return contract - } - - return nil -} - // The cached state and state object cache are helpers which will give you somewhat // control over the nonce. When creating new transactions you're interested in the 'next' // nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. diff --git a/ethchain/vm.go b/ethchain/vm.go index bc4c65d03e..93557007dc 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -30,6 +30,8 @@ type Vm struct { vars RuntimeVars state *State + + stateManager *StateManager } type RuntimeVars struct { @@ -42,8 +44,8 @@ type RuntimeVars struct { TxData []string } -func NewVm(state *State, vars RuntimeVars) *Vm { - return &Vm{vars: vars, state: state} +func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { + return &Vm{vars: vars, state: state, stateManager: stateManager} } var Pow256 = ethutil.BigPow(2, 256) diff --git a/ethutil/value.go b/ethutil/value.go index 04131aba90..b7756f9b16 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -20,7 +20,12 @@ func (val *Value) String() string { } func NewValue(val interface{}) *Value { - return &Value{Val: val} + t := val + if v, ok := val.(*Value); ok { + t = v.Val + } + + return &Value{Val: t} } func (val *Value) Type() reflect.Kind {