diff --git a/core/blockchain.go b/core/blockchain.go index 1fbcdfc6f7..4b3838eefa 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -929,7 +929,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { return i, err } // Write state changes to database - _, err = self.stateCache.Commit() + _, err = state.Commit(self.stateCache) + //_, err = self.stateCache.Commit() if err != nil { return i, err } diff --git a/core/chain_makers.go b/core/chain_makers.go index 0b9a5f75d3..cfcb12b3ef 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -105,8 +105,8 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { if b.gasPool == nil { b.SetCoinbase(common.Address{}) } - b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + b.statedb.TransitionState(tx.Hash(), common.Hash{}, len(b.txs)) + _, receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -203,7 +203,8 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, gen(i, b) } AccumulateRewards(statedb, h, b.uncles) - root, err := statedb.Commit() + //root, err := statedb.Commit() + root, err := state.Commit(statedb) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } diff --git a/core/execution.go b/core/execution.go index 1bc02f7fb2..5bc2146b00 100644 --- a/core/execution.go +++ b/core/execution.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -169,7 +170,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA } // generic transfer method -func Transfer(from, to vm.Account, amount *big.Int) { - from.SubBalance(amount) - to.AddBalance(amount) +func Transfer(statedb *state.StateDB, from, to common.Address, amount *big.Int) { + statedb.SubBalance(from, amount) + statedb.AddBalance(to, amount) } diff --git a/core/genesis.go b/core/genesis.go index 637b633206..6b22b8661e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -73,7 +73,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) } } - root, stateBatch := statedb.CommitBatch() + root, stateBatch := state.CommitBatch(statedb) + //root, stateBatch := statedb.CommitBatch() difficulty := common.String2Big(genesis.Difficulty) block := types.NewBlock(&types.Header{ @@ -128,7 +129,8 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big statedb, _ := state.New(common.Hash{}, db) obj := statedb.GetOrNewStateObject(addr) obj.SetBalance(balance) - root, err := statedb.Commit() + root, err := state.Commit(statedb) + //root, err := statedb.Commit() if err != nil { panic(fmt.Sprintf("cannot write state: %v", err)) } diff --git a/core/state/managed_state.go b/core/state/managed_state.go index ad73dc0dc6..1634e0a694 100644 --- a/core/state/managed_state.go +++ b/core/state/managed_state.go @@ -39,7 +39,7 @@ type ManagedState struct { // ManagedState returns a new managed state with the statedb as it's backing layer func ManageState(statedb *StateDB) *ManagedState { return &ManagedState{ - StateDB: statedb.Copy(), + StateDB: Fork(statedb), accounts: make(map[common.Address]*account), } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 4204c456e0..3980fade72 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -46,6 +46,11 @@ const ( codeSizeCacheSize = 100000 ) +type intermediateInfo struct { + txHash, blockHash common.Hash + txIdx uint +} + // StateDBs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -56,22 +61,70 @@ type StateDB struct { trie *trie.SecureTrie pastTries []*trie.SecureTrie codeSizeCache *lru.Cache + parent *StateDB // This map holds 'live' objects, which will get modified while processing a state transition. stateObjects map[common.Address]*StateObject stateObjectsDirty map[common.Address]struct{} + localStateObjects map[common.Address]bool + // The refund counter, also used by state transitioning. refund *big.Int - thash, bhash common.Hash - txIndex int - logs map[common.Hash]vm.Logs - logSize uint + logIdx uint + logs []*vm.Log + + interInfo intermediateInfo + MarkedTransition bool // marks the transition between transactions lock sync.Mutex } +func (s *StateDB) TransitionState(txHash, blockHash common.Hash, txIdx int) { + if s.parent != nil { + s.parent.MarkedTransition = true + } + s.interInfo = intermediateInfo{txHash: txHash, blockHash: blockHash, txIdx: uint(txIdx)} + s.refund = new(big.Int) +} + +// Read fetches the state object with the given address by first checking its own cache of state objects. If that didn't result in an object it will attempt to check it's line of ancestors. +func (s *StateDB) Read(address common.Address) (object *StateObject, inCache bool) { + stateObject := s.stateObjects[address] + if stateObject == nil && s.parent != nil { + stateObject, _ = s.parent.Read(address) + if stateObject != nil { + s.SetStateObject(stateObject) + } + } else if stateObject != nil { + inCache = true + } + + if stateObject != nil { + if stateObject.deleted { + return nil, false + } + return stateObject, inCache + } + + enc := s.trie.Get(address[:]) + if len(enc) == 0 { + return nil, false + } + + var data Account + if err := rlp.DecodeBytes(enc, &data); err != nil { + glog.Errorf("can't decode object at %x: %v", address[:], err) + return nil, false + } + // Insert into the live set. + obj := NewObject(address, data, s.MarkStateObjectDirty) + s.SetStateObject(obj) + //s.stateObjects[address] = obj + return obj, false +} + // Create a new state from a given trie func New(root common.Hash, db ethdb.Database) (*StateDB, error) { tr, err := trie.NewSecure(root, db) @@ -85,8 +138,8 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) { codeSizeCache: csc, stateObjects: make(map[common.Address]*StateObject), stateObjectsDirty: make(map[common.Address]struct{}), + localStateObjects: make(map[common.Address]bool), refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), }, nil } @@ -106,30 +159,27 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) { codeSizeCache: self.codeSizeCache, stateObjects: make(map[common.Address]*StateObject), stateObjectsDirty: make(map[common.Address]struct{}), + localStateObjects: make(map[common.Address]bool), refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), }, nil } // Reset clears out all emphemeral state objects from the state db, but keeps // the underlying state trie to avoid reloading data for the next operations. -func (self *StateDB) Reset(root common.Hash) error { - self.lock.Lock() - defer self.lock.Unlock() +func (s *StateDB) Reset(root common.Hash) error { + s.lock.Lock() + defer s.lock.Unlock() - tr, err := self.openTrie(root) + tr, err := s.openTrie(root) if err != nil { return err } - self.trie = tr - self.stateObjects = make(map[common.Address]*StateObject) - self.stateObjectsDirty = make(map[common.Address]struct{}) - self.refund = new(big.Int) - self.thash = common.Hash{} - self.bhash = common.Hash{} - self.txIndex = 0 - self.logs = make(map[common.Hash]vm.Logs) - self.logSize = 0 + s.trie = tr + s.stateObjects = make(map[common.Address]*StateObject) + s.stateObjectsDirty = make(map[common.Address]struct{}) + s.localStateObjects = make(map[common.Address]bool) + s.refund = new(big.Int) + s.logs = nil return nil } @@ -158,31 +208,27 @@ func (self *StateDB) pushTrie(t *trie.SecureTrie) { } } -func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { - self.thash = thash - self.bhash = bhash - self.txIndex = ti +func (s *StateDB) AddLog(log *vm.Log) { + log.TxIndex = s.interInfo.txIdx + log.TxHash = s.interInfo.txHash + log.BlockHash = s.interInfo.blockHash + log.Index = s.logIdx + + s.logs = append(s.logs, log) + + s.logIdx++ } -func (self *StateDB) AddLog(log *vm.Log) { - log.TxHash = self.thash - log.BlockHash = self.bhash - log.TxIndex = uint(self.txIndex) - log.Index = self.logSize - self.logs[self.thash] = append(self.logs[self.thash], log) - self.logSize++ -} - -func (self *StateDB) GetLogs(hash common.Hash) vm.Logs { - return self.logs[hash] -} - -func (self *StateDB) Logs() vm.Logs { - var logs vm.Logs - for _, lgs := range self.logs { - logs = append(logs, lgs...) +// Logs returns the logs of it's entire ancestory chain or until +// the marked transition is found (state between transactions). +func (s *StateDB) Logs() []*vm.Log { + var logs []*vm.Log + if s.MarkedTransition { + return nil + } else if s.parent != nil { + logs = s.parent.Logs() } - return logs + return append(logs, s.logs...) } func (self *StateDB) AddRefund(gas *big.Int) { @@ -282,6 +328,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { } } +func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { @@ -336,46 +389,56 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) { self.trie.Delete(addr[:]) } -// Retrieve a state object given my the address. Returns nil if not found. -func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) { - // Prefer 'live' objects. - if obj := self.stateObjects[addr]; obj != nil { - if obj.deleted { - return nil - } - return obj - } - - // Load the object from the database. - enc := self.trie.Get(addr[:]) - if len(enc) == 0 { - return nil - } - var data Account - if err := rlp.DecodeBytes(enc, &data); err != nil { - glog.Errorf("can't decode object at %x: %v", addr[:], err) - return nil - } - // Insert into the live set. - obj := NewObject(addr, data, self.MarkStateObjectDirty) - self.SetStateObject(obj) - return obj -} - func (self *StateDB) SetStateObject(object *StateObject) { + if object == nil { + panic("is nil") + } self.stateObjects[object.Address()] = object } -// Retrieve a state object or create a new state object if nil -func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { - stateObject := self.GetStateObject(addr) - if stateObject == nil || stateObject.deleted { - stateObject = self.CreateStateObject(addr) +func (s *StateDB) SetOwnedStateObject(address common.Address, object *StateObject) { + if object == nil { + panic("is nil") + } + s.stateObjects[address] = object + s.localStateObjects[address] = true +} + +func (s *StateDB) GetOrNewStateObject(address common.Address) *StateObject { + stateObject, _ := s.Read(address) + if stateObject != nil { + if !s.localStateObjects[address] { + stateObject = stateObject.Copy(s.db, s.MarkStateObjectDirty) + s.SetOwnedStateObject(address, stateObject) + //s.stateObjects[address] = stateObject + //s.localStateObjects[address] = true + } + return stateObject } + if stateObject == nil || stateObject.deleted { + stateObject = NewObject(address, Account{}, s.MarkStateObjectDirty) + stateObject.SetNonce(StartingNonce) + + s.SetOwnedStateObject(address, stateObject) + //s.stateObjects[address] = stateObject + //s.localStateObjects[address] = true + } return stateObject } +func (s *StateDB) GetStateObject(address common.Address) *StateObject { + account, _ := s.Read(address) + if account != nil { + if s.stateObjects[address] == nil { + s.SetStateObject(account) + //s.stateObjects[address] = account + } + return account + } + return nil +} + // NewStateObject create a state object whether it exist in the trie or not func (self *StateDB) newStateObject(addr common.Address) *StateObject { if glog.V(logger.Core) { @@ -383,7 +446,8 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject { } obj := NewObject(addr, Account{}, self.MarkStateObjectDirty) obj.SetNonce(StartingNonce) // sets the object to dirty - self.stateObjects[addr] = obj + //self.stateObjects[addr] = obj + self.SetOwnedStateObject(addr, obj) return obj } @@ -412,51 +476,21 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account { return self.CreateStateObject(addr) } -// -// Setting, copying of the state methods -// - -func (self *StateDB) Copy() *StateDB { - self.lock.Lock() - defer self.lock.Unlock() - - // Copy all the basic fields, initialize the memory ones - state := &StateDB{ - db: self.db, - trie: self.trie, - pastTries: self.pastTries, - codeSizeCache: self.codeSizeCache, - stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), - refund: new(big.Int).Set(self.refund), - logs: make(map[common.Hash]vm.Logs, len(self.logs)), - logSize: self.logSize, - } - // Copy the dirty states and logs - for addr, _ := range self.stateObjectsDirty { - state.stateObjects[addr] = self.stateObjects[addr].Copy(self.db, state.MarkStateObjectDirty) - state.stateObjectsDirty[addr] = struct{}{} - } - for hash, logs := range self.logs { - state.logs[hash] = make(vm.Logs, len(logs)) - copy(state.logs[hash], logs) - } - return state -} - func (self *StateDB) Set(state *StateDB) { self.lock.Lock() defer self.lock.Unlock() - self.db = state.db - self.trie = state.trie - self.pastTries = state.pastTries - self.stateObjects = state.stateObjects - self.stateObjectsDirty = state.stateObjectsDirty - self.codeSizeCache = state.codeSizeCache - self.refund = state.refund - self.logs = state.logs - self.logSize = state.logSize + *self = *state + /* + self.db = state.db + self.trie = state.trie + self.pastTries = state.pastTries + self.stateObjects = state.stateObjects + self.stateObjectsDirty = state.stateObjectsDirty + self.codeSizeCache = state.codeSizeCache + self.refund = state.refund + self.logs = state.logs + */ } func (self *StateDB) GetRefund() *big.Int { @@ -501,7 +535,8 @@ func (s *StateDB) DeleteSuicides() { } } -// Commit commits all state changes to the database. +/* +// commit commits all state changes to the database. func (s *StateDB) Commit() (root common.Hash, err error) { root, batch := s.CommitBatch() return root, batch.Write() @@ -549,7 +584,116 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) } return root, err } +*/ func (self *StateDB) Refunds() *big.Int { return self.refund } + +// Fork preserve the given state and returns a handle to a new modifiable state +// that does not affect the preserved state. +func Fork(parent *StateDB) *StateDB { + return &StateDB{ + db: parent.db, + trie: parent.trie, + pastTries: parent.pastTries, + codeSizeCache: parent.codeSizeCache, + + parent: parent, + stateObjects: make(map[common.Address]*StateObject), + localStateObjects: make(map[common.Address]bool), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logIdx: parent.logIdx, + } +} + +// Reduce flattens the state in to a single new state, including all changes of all ancestors. +func Reduce(s *StateDB) *StateDB { + if s.parent == nil { + return s + } + state := Reduce(s.parent) + + for address, object := range s.stateObjects { + if s.localStateObjects[address] { + state.SetOwnedStateObject(address, object) + } else { + state.SetStateObject(object) + } + } + + state.logs = append(state.logs, s.logs...) + state.refund.Add(state.refund, s.refund) + + return state +} + +func IntermediateRoot(state *StateDB) common.Hash { + for address, _ := range state.localStateObjects { + stateObject := state.stateObjects[address] + if _, isdirty := state.stateObjectsDirty[address]; isdirty { + if stateObject.remove { + state.DeleteStateObject(stateObject) + } else { + stateObject.UpdateRoot(state.db) + state.UpdateStateObject(stateObject) + } + } + } + state.stateObjectsDirty = make(map[common.Address]struct{}) + return state.trie.Hash() +} + +// Commit commits all state changes to the database. +func Commit(state *StateDB) (common.Hash, error) { + root, batch := CommitBatch(state) + return root, batch.Write() +} + +// CommitBatch commits all state changes to a write batch but does not +// execute the batch. It is used to validate state changes against +// the root hash stored in a block. +func CommitBatch(state *StateDB) (common.Hash, ethdb.Batch) { + batch := state.db.NewBatch() + root, _ := stateCommit(state, batch) + return root, batch +} + +func stateCommit(state *StateDB, dbw trie.DatabaseWriter) (root common.Hash, err error) { + // make sure the state is flattened before committing + state = Reduce(state) + state.refund = new(big.Int) + + for address, _ := range state.localStateObjects { + stateObject := state.stateObjects[address] + //for _, stateObject := range state.stateObjects { + if stateObject.remove { + // If the object has been removed, don't bother syncing it + // and just mark it for deletion in the trie. + stateObject.deleted = true + state.trie.Delete(stateObject.Address().Bytes()[:]) + } else { + // Write any contract code associated with the state object + if stateObject.code != nil && stateObject.dirtyCode { + if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { + return common.Hash{}, err + } + stateObject.dirtyCode = false + } + // Write any storage changes in the state object to its storage trie. + if err := stateObject.CommitTrie(state.db, dbw); err != nil { + return common.Hash{}, err + } + // Update the object in the main account trie. + state.UpdateStateObject(stateObject) + } + } + state.stateObjectsDirty = make(map[common.Address]struct{}) + // Write trie changes. + root, err = state.trie.CommitTo(dbw) + if err == nil { + state.pushTrie(state.trie) + } + return root, err +} diff --git a/core/state_processor.go b/core/state_processor.go index fd8e9762e9..6cba104bbf 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -64,6 +64,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg header = block.Header() allLogs vm.Logs gp = new(GasPool).AddGas(block.GasLimit()) + forkState = state.Fork(statedb) ) // Mutate the the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { @@ -71,16 +72,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - statedb.StartRecord(tx.Hash(), block.Hash(), i) - receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) + forkState.TransitionState(tx.Hash(), block.Hash(), i) + txPostState, receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, forkState, header, tx, totalUsedGas, cfg) if err != nil { return nil, nil, totalUsedGas, err } receipts = append(receipts, receipt) allLogs = append(allLogs, logs...) + + forkState = state.Fork(txPostState) } AccumulateRewards(statedb, header, block.Uncles()) + statedb.Set(state.Reduce(forkState)) + return receipts, allLogs, totalUsedGas, err } @@ -89,15 +94,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // // ApplyTransactions returns the generated receipts and vm logs during the // execution of the state transition phase. -func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) +func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*state.StateDB, *types.Receipt, vm.Logs, *big.Int, error) { + env := NewEnv(statedb, config, bc, tx, header, cfg) + _, gas, err := ApplyMessage(env, tx, gp) if err != nil { - return nil, nil, nil, err + return statedb, nil, nil, nil, err } + statedb = env.Db().(*state.StateDB) + // Update the state with pending changes usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) + receipt := types.NewReceipt(state.IntermediateRoot(statedb).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) if MessageCreatesContract(tx) { @@ -105,13 +113,12 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) } - logs := statedb.GetLogs(tx.Hash()) - receipt.Logs = logs + receipt.Logs = statedb.Logs() receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) glog.V(logger.Debug).Infoln(receipt) - return receipt, logs, gas, err + return statedb, receipt, receipt.Logs, gas, err } // AccumulateRewards credits the coinbase of the given block with the diff --git a/core/state_transition.go b/core/state_transition.go index 9e6b2f567e..7616df1986 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -273,24 +274,24 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b requiredGas = new(big.Int).Set(self.gasUsed()) - self.refundGas() - self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + self.refundGas(vmenv.Db().(*state.StateDB)) + vmenv.Db().AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) return ret, requiredGas, self.gasUsed(), err } -func (self *StateTransition) refundGas() { +func (self *StateTransition) refundGas(state *state.StateDB) { // Return eth for remaining gas to the sender account, // exchanged at the original rate. sender, _ := self.from() // err already checked remaining := new(big.Int).Mul(self.gas, self.gasPrice) - sender.AddBalance(remaining) + state.AddBalance(sender.Address(), remaining) // Apply refund counter, capped to half of the used gas. uhalf := remaining.Div(self.gasUsed(), common.Big2) - refund := common.BigMin(uhalf, self.state.GetRefund()) + refund := common.BigMin(uhalf, state.GetRefund()) self.gas.Add(self.gas, refund) - self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) + state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) // Also return remaining gas to the block gas counter so it is // available for the next transaction. diff --git a/core/vm_env.go b/core/vm_env.go index e541eaef47..5a207fd38f 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -90,15 +90,17 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool { } func (self *VMEnv) MakeSnapshot() vm.Database { - return self.state.Copy() + pstate := self.state + self.state = state.Fork(pstate) + return pstate } func (self *VMEnv) SetSnapshot(copy vm.Database) { - self.state.Set(copy.(*state.StateDB)) + self.state = copy.(*state.StateDB) } func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { - Transfer(from, to, amount) + Transfer(self.state, from.Address(), to.Address(), amount) } func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { diff --git a/eth/api_backend.go b/eth/api_backend.go index 4adeb0aa08..a9b7daa6bc 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -97,8 +97,8 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { - stateDb := state.(EthApiState).state.Copy() +func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) { + stateDb := state.Fork(st.(EthApiState).state) addr, _ := msg.From() from := stateDb.GetOrNewStateObject(addr) from.SetBalance(common.MaxBig) diff --git a/miner/worker.go b/miner/worker.go index ac1ef5ba3d..f5627c9316 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -583,7 +583,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB continue } // Start executing the transaction - env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) + env.state.TransitionState(tx.Hash(), common.Hash{}, env.tcount) err, logs := env.commitTransaction(tx, bc, gp) switch { @@ -618,8 +618,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB } func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { - snap := env.state.Copy() - // this is a bit of a hack to force jit for the miners config := env.config.VmConfig if !(config.EnableJit && config.ForceJit) { @@ -627,9 +625,10 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g } config.ForceJit = false // disable forcing jit - receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + defer func() { env.state = state }() + if err != nil { - env.state.Set(snap) return err, nil } env.txs = append(env.txs, tx) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 7096b866d4..4119f5372d 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -228,7 +228,8 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { } } - root, err := statedb.Commit() + //root, err := statedb.Commit() + root, err := state.Commit(statedb) if err != nil { return nil, fmt.Errorf("error writing state: %v", err) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 67e4bf832e..dbe7918db4 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -98,7 +98,7 @@ func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *test statedb, _ := state.New(common.Hash{}, db) for addr, account := range test.Pre { obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty) - statedb.SetStateObject(obj) + statedb.SetOwnedStateObject(common.HexToAddress(addr), obj) for a, v := range account.Storage { obj.SetState(common.HexToHash(a), common.HexToHash(v)) } @@ -109,13 +109,16 @@ func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *test } func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) error { + //glog.SetToStderr(true) + //glog.SetV(6) + skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true } for name, test := range tests { - if skipTest[name] /*|| name != "callcodecallcode_11" */ { + if skipTest[name] /*|| name != "log1_logMemsizeTooHigh"*/ { glog.Infoln("Skipping state test", name) continue } @@ -137,7 +140,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { statedb, _ := state.New(common.Hash{}, db) for addr, account := range test.Pre { obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty) - statedb.SetStateObject(obj) + statedb.SetOwnedStateObject(common.HexToAddress(addr), obj) for a, v := range account.Storage { obj.SetState(common.HexToHash(a), common.HexToHash(v)) } @@ -196,7 +199,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { } } - root, _ := statedb.Commit() + root, _ := state.Commit(statedb) if common.HexToHash(test.PostStateRoot) != root { return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root) } @@ -227,19 +230,22 @@ func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string } // Set pre compiled contracts vm.Precompiled = vm.PrecompiledContracts() - snapshot := statedb.Copy() + snapshot := statedb + nstatedb := state.Fork(snapshot) + gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) key, _ := hex.DecodeString(tx["secretKey"]) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) message := NewMessage(addr, to, data, value, gas, price, nonce) - vmenv := NewEnvFromMap(ruleSet, statedb, env, tx) + vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx) vmenv.origin = addr ret, _, err := core.ApplyMessage(vmenv, message, gaspool) if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { - statedb.Set(snapshot) + vmenv.SetSnapshot(snapshot) } - statedb.Commit() + statedb.Set(state.Reduce(vmenv.Db().(*state.StateDB))) + state.Commit(statedb) - return ret, vmenv.state.Logs(), vmenv.Gas, err + return ret, statedb.Logs(), vmenv.Gas, err } diff --git a/tests/util.go b/tests/util.go index ffbcb9d56d..13285ec2cb 100644 --- a/tests/util.go +++ b/tests/util.go @@ -229,18 +229,22 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { return self.state.GetBalance(from).Cmp(balance) >= 0 } + func (self *Env) MakeSnapshot() vm.Database { - return self.state.Copy() + pstate := self.state + self.state = state.Fork(pstate) + return pstate } + func (self *Env) SetSnapshot(copy vm.Database) { - self.state.Set(copy.(*state.StateDB)) + self.state = copy.(*state.StateDB) } func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { if self.skipTransfer { return } - core.Transfer(from, to, amount) + core.Transfer(self.state, from.Address(), to.Address(), amount) } func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {