diff --git a/core/state/journal.go b/core/state/journal.go index 5cd41477d9..591ee05fff 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -40,6 +40,7 @@ type ( account *common.Address prev bool // whether account had already suicided prevbalance *big.Int + prevObj *stateObject } // Changes to individual accounts. @@ -71,8 +72,9 @@ type ( hash common.Hash } touchChange struct { - account *common.Address - prev bool + account *common.Address + prev bool + prevDirty bool } ) @@ -86,36 +88,43 @@ func (ch resetObjectChange) undo(s *StateDB) { } func (ch suicideChange) undo(s *StateDB) { - obj := s.getStateObject(*ch.account) - if obj != nil { - obj.suicided = ch.prev - obj.setBalance(ch.prevbalance) + if !ch.prev { + ch.prevObj.suicided = ch.prev + ch.prevObj.setBalance(ch.prevbalance) + s.setStateObject(ch.prevObj) } } var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") func (ch touchChange) undo(s *StateDB) { - if !ch.prev && *ch.account != ripemd { - delete(s.stateObjects, *ch.account) + if !ch.prev && !ch.prevDirty && *ch.account != ripemd { delete(s.stateObjectsDirty, *ch.account) } } func (ch balanceChange) undo(s *StateDB) { - s.getStateObject(*ch.account).setBalance(ch.prev) + if obj := s.getStateObject(*ch.account); obj != nil { + obj.setBalance(ch.prev) + } } func (ch nonceChange) undo(s *StateDB) { - s.getStateObject(*ch.account).setNonce(ch.prev) + if obj := s.getStateObject(*ch.account); obj != nil { + obj.setNonce(ch.prev) + } } func (ch codeChange) undo(s *StateDB) { - s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) + if obj := s.getStateObject(*ch.account); obj != nil { + obj.setCode(common.BytesToHash(ch.prevhash), ch.prevcode) + } } func (ch storageChange) undo(s *StateDB) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) + if obj := s.getStateObject(*ch.account); obj != nil { + obj.setState(ch.key, ch.prevalue) + } } func (ch refundChange) undo(s *StateDB) { diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go index ea5737a089..c2ba82ac80 100644 --- a/core/state/managed_state_test.go +++ b/core/state/managed_state_test.go @@ -29,6 +29,7 @@ func create() (*ManagedState, *account) { db, _ := ethdb.NewMemDatabase() statedb, _ := New(common.Hash{}, db) ms := ManageState(statedb) + ms.StateDB.createObject(addr) ms.StateDB.SetNonce(addr, 100) ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr)) return ms, ms.accounts[addr] @@ -116,6 +117,7 @@ func TestSetNonce(t *testing.T) { } addr[0] = 1 + ms.StateDB.createObject(addr) ms.StateDB.SetNonce(addr, 1) if ms.GetNonce(addr) != 1 { diff --git a/core/state/state_object.go b/core/state/state_object.go index 7f994ee6d6..9c9b1e9164 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -136,9 +136,11 @@ func (self *stateObject) markSuicided() { } func (c *stateObject) touch() { + prevDirty := c.onDirty == nil c.db.journal = append(c.db.journal, touchChange{ - account: &c.address, - prev: c.touched, + account: &c.address, + prev: c.touched, + prevDirty: prevDirty, }) if c.onDirty != nil { c.onDirty(c.Address()) diff --git a/core/state/state_test.go b/core/state/state_test.go index 3bc63c1486..2a9060b7ad 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -113,6 +113,7 @@ func (s *StateSuite) TestSnapshot(c *checker.C) { data1 := common.BytesToHash([]byte{42}) data2 := common.BytesToHash([]byte{43}) + s.state.createObject(stateobjaddr) // set initial state object value s.state.SetState(stateobjaddr, storageaddr, data1) // get snapshot of current state @@ -143,6 +144,8 @@ func TestSnapshot2(t *testing.T) { stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr1 := toAddr([]byte("so1")) + state.createObject(stateobjaddr0) + state.createObject(stateobjaddr1) var storageaddr common.Hash data0 := common.BytesToHash([]byte{17}) diff --git a/core/state/statedb.go b/core/state/statedb.go index 0c72fc6b07..002b81761d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -310,7 +310,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool { // AddBalance adds amount to the account associated with addr func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount) } @@ -318,35 +318,35 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { // SubBalance subtracts amount from the account associated with addr func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount) } } func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount) } } func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.SetNonce(nonce) } } func (self *StateDB) SetCode(addr common.Address, code []byte) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.SetCode(crypto.Keccak256Hash(code), code) } } func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) { - stateObject := self.GetOrNewStateObject(addr) + stateObject := self.getStateObject(addr) if stateObject != nil { stateObject.SetState(self.db, key, value) } @@ -366,6 +366,7 @@ func (self *StateDB) Suicide(addr common.Address) bool { account: &addr, prev: stateObject.suicided, prevbalance: new(big.Int).Set(stateObject.Balance()), + prevObj: stateObject, }) stateObject.markSuicided() stateObject.data.Balance = new(big.Int) @@ -397,7 +398,7 @@ func (self *StateDB) deleteStateObject(stateObject *stateObject) { func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { // Prefer 'live' objects. if obj := self.stateObjects[addr]; obj != nil { - if obj.deleted { + if obj.suicided { return nil } return obj diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 597de3be53..84be7d6733 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -116,7 +116,7 @@ func TestIntermediateLeaks(t *testing.T) { } func TestSnapshotRandom(t *testing.T) { - t.Skip("@fjl fix me please") + //t.Skip("@fjl fix me please") config := &quick.Config{MaxCount: 1000} err := quick.Check((*snapshotTest).run, config) if cerr, ok := err.(*quick.CheckError); ok {