From 53e30f750dd0c91279bfebe01bb12fd170cb74ff Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 17 Jun 2014 11:06:06 +0200 Subject: [PATCH 1/2] Removal of manual updating of state objects * You'll only ever need to update the state by calling Update. Update will take care of the updating of it's child state objects. --- ethchain/state.go | 27 +++++++++++++++++++++++++-- ethchain/state_manager.go | 4 +++- ethchain/state_object.go | 26 +++++++++++++++----------- ethchain/state_transition.go | 26 +++++++++++++------------- ethchain/vm.go | 19 +++++++------------ ethminer/miner.go | 2 ++ 6 files changed, 65 insertions(+), 39 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index 9a9d0a278..f5a3d3071 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -38,12 +38,16 @@ func (s *State) Reset() { stateObject.state.Reset() } + + s.Empty() } // Syncs the trie and all siblings func (s *State) Sync() { // Sync all nested states for _, stateObject := range s.stateObjects { + s.UpdateStateObject(stateObject) + if stateObject.state == nil { continue } @@ -52,6 +56,18 @@ func (s *State) Sync() { } s.trie.Sync() + + s.Empty() +} + +func (self *State) Empty() { + self.stateObjects = make(map[string]*StateObject) +} + +func (self *State) Update() { + for _, stateObject := range self.stateObjects { + self.UpdateStateObject(stateObject) + } } // Purges the current trie. @@ -68,6 +84,7 @@ func (self *State) UpdateStateObject(stateObject *StateObject) { addr := stateObject.Address() if self.stateObjects[string(addr)] == nil { + panic("?") self.stateObjects[string(addr)] = stateObject } @@ -98,13 +115,19 @@ func (self *State) GetStateObject(addr []byte) *StateObject { func (self *State) GetOrNewStateObject(addr []byte) *StateObject { stateObject := self.GetStateObject(addr) if stateObject == nil { - stateObject = NewStateObject(addr) - self.stateObjects[string(addr)] = stateObject + stateObject = self.NewStateObject(addr) } return stateObject } +func (self *State) NewStateObject(addr []byte) *StateObject { + stateObject := NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject + + return stateObject +} + func (self *State) GetAccount(addr []byte) *StateObject { return self.GetOrNewStateObject(addr) } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 36bb14846..a0051181f 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -181,7 +181,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea coinbase.SetGasPool(block.CalcGasLimit(parent)) // Process the transactions on to current block - //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) // Block validation @@ -197,6 +196,9 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea return err } + // Update the state with pending changes + state.Update() + if !block.State().Cmp(state) { return fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 1445bcd82..5fc738fee 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -4,8 +4,15 @@ import ( "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" + "strings" ) +type Code []byte + +func (self Code) String() string { + return strings.Join(Disassemble(self), " ") +} + type StateObject struct { // Address of the object address []byte @@ -15,8 +22,8 @@ type StateObject struct { Nonce uint64 // Contract related attributes state *State - script []byte - initScript []byte + script Code + initScript Code // Total gas pool is the total amount of gas currently // left if this object is the coinbase. Gas is directly @@ -30,12 +37,9 @@ func MakeContract(tx *Transaction, state *State) *StateObject { if tx.IsContract() { addr := tx.CreationAddress() - value := tx.Value - contract := NewContract(addr, value, ZeroHash256) - + contract := state.NewStateObject(addr) contract.initScript = tx.Data - - state.UpdateStateObject(contract) + contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, "")) return contract } @@ -120,13 +124,13 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { func (c *StateObject) AddAmount(amount *big.Int) { c.SetAmount(new(big.Int).Add(c.Amount, amount)) - ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SubAmount(amount *big.Int) { c.SetAmount(new(big.Int).Sub(c.Amount, amount)) - ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SetAmount(amount *big.Int) { @@ -197,12 +201,12 @@ func (c *StateObject) Address() []byte { } // Returns the main script body -func (c *StateObject) Script() []byte { +func (c *StateObject) Script() Code { return c.script } // Returns the initialization script -func (c *StateObject) Init() []byte { +func (c *StateObject) Init() Code { return c.initScript } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 25efd64cc..23175b0f3 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -67,13 +67,8 @@ func (self *StateTransition) Receiver() *StateObject { func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject { contract := MakeContract(tx, state) - if contract != nil { - state.states[string(tx.CreationAddress())] = contract.state - return contract - } - - return nil + return contract } func (self *StateTransition) UseGas(amount *big.Int) error { @@ -137,6 +132,8 @@ func (self *StateTransition) TransitionState() (err error) { receiver *StateObject ) + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(~) %x\n", tx.Hash()) + // Make sure this transaction's nonce is correct if sender.Nonce != tx.Nonce { return NonceError(tx.Nonce, sender.Nonce) @@ -152,15 +149,17 @@ func (self *StateTransition) TransitionState() (err error) { defer func() { self.RefundGas() - if sender != nil { - self.state.UpdateStateObject(sender) - } + /* + if sender != nil { + self.state.UpdateStateObject(sender) + } - if receiver != nil { - self.state.UpdateStateObject(receiver) - } + if receiver != nil { + self.state.UpdateStateObject(receiver) + } - self.state.UpdateStateObject(self.Coinbase()) + self.state.UpdateStateObject(self.Coinbase()) + */ }() // Increment the nonce for the next transaction @@ -209,6 +208,7 @@ func (self *StateTransition) TransitionState() (err error) { receiver.script = code } else { if len(receiver.Script()) > 0 { + fmt.Println(receiver.Script()) _, err := self.Eval(receiver.Script(), receiver) if err != nil { return fmt.Errorf("Error during code execution %v", err) diff --git a/ethchain/vm.go b/ethchain/vm.go index 2ba0e2ef3..77a08faa6 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -95,9 +95,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro step := 0 prevStep := 0 - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } + ethutil.Config.Log.Debugf("# op\n") for { prevStep = step @@ -109,9 +107,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro val := closure.Get(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()) - } + + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) gas := new(big.Int) addStepGasUsage := func(amount *big.Int) { @@ -525,8 +522,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro vm.state.Revert(snapshot) } else { stack.Push(ethutil.BigD(addr)) - - vm.state.UpdateStateObject(contract) } case CALL: // TODO RE-WRITE @@ -569,8 +564,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } else { stack.Push(ethutil.BigTrue) - vm.state.UpdateStateObject(contract) - mem.Set(retOffset.Int64(), retSize.Int64(), ret) } } else { @@ -589,9 +582,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro receiver := vm.state.GetAccount(stack.Pop().Bytes()) receiver.AddAmount(closure.object.Amount) - vm.state.UpdateStateObject(receiver) - closure.object.state.Purge() + trie := closure.object.state.trie + trie.NewIterator().Each(func(key string, v *ethutil.Value) { + trie.Delete(key) + }) fallthrough case STOP: // Stop the closure diff --git a/ethminer/miner.go b/ethminer/miner.go index 1ef9ca229..4343b4333 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -154,6 +154,8 @@ func (self *Miner) mineNewBlock() { // Accumulate the rewards included for this block stateManager.AccumelateRewards(self.block.State(), self.block) + self.block.State().Update() + ethutil.Config.Log.Infoln("[MINER] Mining on block. Includes", len(self.txs), "transactions") // Find a valid nonce From 3621988e15c025d2bd7b80e4691a6b236574f0a1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 17 Jun 2014 11:07:37 +0200 Subject: [PATCH 2/2] Removed deprecated states --- ethchain/deprecated.go | 236 ----------------------------------------- ethchain/state.go | 6 +- 2 files changed, 2 insertions(+), 240 deletions(-) delete mode 100644 ethchain/deprecated.go diff --git a/ethchain/deprecated.go b/ethchain/deprecated.go deleted file mode 100644 index 0985fa25d..000000000 --- a/ethchain/deprecated.go +++ /dev/null @@ -1,236 +0,0 @@ -package ethchain - -import ( - "bytes" - "fmt" - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { - contract := MakeContract(tx, state) - if contract != nil { - state.states[string(tx.CreationAddress())] = contract.state - - return contract - } - - return nil -} - -func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) { - account := state.GetAccount(tx.Sender()) - - err = account.ConvertGas(tx.Gas, tx.GasPrice) - if err != nil { - ethutil.Config.Log.Debugln(err) - return - } - - closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice) - vm := NewVm(state, sm, RuntimeVars{ - Origin: account.Address(), - BlockNumber: block.BlockInfo().Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: tx.Value, - //Price: tx.GasPrice, - }) - ret, gas, err = closure.Call(vm, tx.Data, nil) - - // Update the account (refunds) - state.UpdateStateObject(account) - state.UpdateStateObject(object) - - return -} - -func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, state *State, toContract bool) (gas *big.Int, err error) { - fmt.Printf("state root before update %x\n", state.Root()) - defer func() { - if r := recover(); r != nil { - ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("%v", r) - } - }() - - gas = new(big.Int) - addGas := func(g *big.Int) { gas.Add(gas, g) } - addGas(GasTx) - - // Get the sender - sender := state.GetAccount(tx.Sender()) - - if sender.Nonce != tx.Nonce { - err = NonceError(tx.Nonce, sender.Nonce) - return - } - - sender.Nonce += 1 - defer func() { - //state.UpdateStateObject(sender) - // Notify all subscribers - self.Ethereum.Reactor().Post("newTx:post", tx) - }() - - txTotalBytes := big.NewInt(int64(len(tx.Data))) - //fmt.Println("txTotalBytes", txTotalBytes) - //txTotalBytes.Div(txTotalBytes, ethutil.Big32) - addGas(new(big.Int).Mul(txTotalBytes, GasData)) - - rGas := new(big.Int).Set(gas) - rGas.Mul(gas, tx.GasPrice) - - // Make sure there's enough in the sender's account. Having insufficient - // funds won't invalidate this transaction but simple ignores it. - totAmount := new(big.Int).Add(tx.Value, rGas) - if sender.Amount.Cmp(totAmount) < 0 { - state.UpdateStateObject(sender) - err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) - return - } - - coinbase.BuyGas(gas, tx.GasPrice) - state.UpdateStateObject(coinbase) - fmt.Printf("1. root %x\n", state.Root()) - - // Get the receiver - receiver := state.GetAccount(tx.Recipient) - - // Send Tx to self - if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { - // Subtract the fee - sender.SubAmount(rGas) - } else { - // Subtract the amount from the senders account - sender.SubAmount(totAmount) - state.UpdateStateObject(sender) - fmt.Printf("3. root %x\n", state.Root()) - - // Add the amount to receivers account which should conclude this transaction - receiver.AddAmount(tx.Value) - state.UpdateStateObject(receiver) - fmt.Printf("2. root %x\n", state.Root()) - } - - ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) - - return -} - -func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { - /* - Applies transactions to the given state and creates new - state objects where needed. - - If said objects needs to be created - run the initialization script provided by the transaction and - assume there's a return value. The return value will be set to - the script section of the state object. - */ - var ( - addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } - gas = new(big.Int) - script []byte - ) - totalGasUsed = big.NewInt(0) - snapshot := state.Snapshot() - - ca := state.GetAccount(coinbase) - // Apply the transaction to the current state - gas, err = sm.ProcessTransaction(tx, ca, state, false) - addTotalGas(gas) - fmt.Println("gas used by tx", gas) - - if tx.CreatesContract() { - if err == nil { - // Create a new state object and the transaction - // as it's data provider. - contract := sm.MakeStateObject(state, tx) - if contract != nil { - fmt.Println(Disassemble(contract.Init())) - // Evaluate the initialization script - // and use the return value as the - // script section for the state object. - script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block) - fmt.Println("gas used by eval", gas) - addTotalGas(gas) - fmt.Println("total =", totalGasUsed) - - fmt.Println("script len =", len(script)) - - if err != nil { - err = fmt.Errorf("[STATE] Error during init script run %v", err) - return - } - contract.script = script - state.UpdateStateObject(contract) - } else { - err = fmt.Errorf("[STATE] Unable to create contract") - } - } else { - err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender()) - } - } else { - // Find the state object at the "recipient" address. If - // there's an object attempt to run the script. - stateObject := state.GetStateObject(tx.Recipient) - if err == nil && stateObject != nil && len(stateObject.Script()) > 0 { - _, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block) - addTotalGas(gas) - } - } - - parent := sm.bc.GetBlock(block.PrevHash) - total := new(big.Int).Add(block.GasUsed, totalGasUsed) - limit := block.CalcGasLimit(parent) - if total.Cmp(limit) > 0 { - state.Revert(snapshot) - err = GasLimitError(total, limit) - } - - return -} - -// Apply transactions uses the transaction passed to it and applies them onto -// the current processing state. -func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { - // Process each transaction/contract - var receipts []*Receipt - var validTxs []*Transaction - var ignoredTxs []*Transaction // Transactions which go over the gasLimit - - totalUsedGas := big.NewInt(0) - - for _, tx := range txs { - usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx) - if err != nil { - if IsNonceErr(err) { - continue - } - if IsGasLimitErr(err) { - ignoredTxs = append(ignoredTxs, tx) - // We need to figure out if we want to do something with thse txes - ethutil.Config.Log.Debugln("Gastlimit:", err) - continue - } - - ethutil.Config.Log.Infoln(err) - } - - accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) - receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} - - receipts = append(receipts, receipt) - validTxs = append(validTxs, tx) - } - - fmt.Println("################# MADE\n", receipts, "\n############################") - - // Update the total gas used for the block (to be mined) - block.GasUsed = totalUsedGas - - return receipts, validTxs -} diff --git a/ethchain/state.go b/ethchain/state.go index f5a3d3071..993f1fb08 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -13,8 +13,6 @@ import ( type State struct { // The trie for this structure trie *ethutil.Trie - // Nested states - states map[string]*State stateObjects map[string]*StateObject @@ -23,7 +21,7 @@ type State struct { // Create a new state from a given trie func NewState(trie *ethutil.Trie) *State { - return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()} + return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} } // Resets the trie and all siblings @@ -155,7 +153,7 @@ func (s *State) Snapshot() *State { func (s *State) Revert(snapshot *State) { s.trie = snapshot.trie - s.states = snapshot.states + s.stateObjects = snapshot.stateObjects } func (s *State) Put(key, object []byte) {