diff options
| author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-11-15 20:46:47 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-11-15 20:46:47 +0800 |
| commit | 81d9d7d38555a63602b9da3d07955ad4e5a62f02 (patch) | |
| tree | 9ce0d55bfe182f6493867ea5497bf2c8cd9e8523 /core/state | |
| parent | ef9265d0d7abf6614c1d2fb977989ab0d400a590 (diff) | |
| parent | 822355f8a6e8826561433392fd94a8bde7e4dbf3 (diff) | |
| download | go-tangerine-1.4.19.tar go-tangerine-1.4.19.tar.gz go-tangerine-1.4.19.tar.bz2 go-tangerine-1.4.19.tar.lz go-tangerine-1.4.19.tar.xz go-tangerine-1.4.19.tar.zst go-tangerine-1.4.19.zip | |
Merge pull request #3252 from obscuren/release/1.4v1.4.19
1.4 HF
Diffstat (limited to 'core/state')
| -rw-r--r-- | core/state/state_object.go | 13 | ||||
| -rw-r--r-- | core/state/state_test.go | 6 | ||||
| -rw-r--r-- | core/state/statedb.go | 26 | ||||
| -rw-r--r-- | core/state/statedb_test.go | 8 | ||||
| -rw-r--r-- | core/state/sync_test.go | 2 |
5 files changed, 37 insertions, 18 deletions
diff --git a/core/state/state_object.go b/core/state/state_object.go index edb073173..2b5dfea7d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -91,6 +91,11 @@ type StateObject struct { onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } +// empty returns whether the account is considered empty. +func (s *StateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + // Account is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. type Account struct { @@ -221,8 +226,12 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e return err } +// AddBalance removes amount from c's balance. +// It is used to add funds to the destination account of a transfer. func (c *StateObject) AddBalance(amount *big.Int) { - if amount.Cmp(common.Big0) == 0 { + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. + if amount.Cmp(common.Big0) == 0 && !c.empty() { return } c.SetBalance(new(big.Int).Add(c.Balance(), amount)) @@ -232,6 +241,8 @@ func (c *StateObject) AddBalance(amount *big.Int) { } } +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. func (c *StateObject) SubBalance(amount *big.Int) { if amount.Cmp(common.Big0) == 0 { return diff --git a/core/state/state_test.go b/core/state/state_test.go index f188bc271..435d1d829 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -48,7 +48,7 @@ func (s *StateSuite) TestDump(c *checker.C) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - s.state.Commit() + s.state.Commit(false) // check that dump contains the state objects that are in trie got := string(s.state.Dump()) @@ -100,7 +100,7 @@ func TestNull(t *testing.T) { //value := common.FromHex("0x823140710bf13990e4500136726d8b55") var value common.Hash state.SetState(address, common.Hash{}, value) - state.Commit() + state.Commit(false) value = state.GetState(address, common.Hash{}) if !common.EmptyHash(value) { t.Errorf("expected empty hash. got %x", value) @@ -160,7 +160,7 @@ func TestSnapshot2(t *testing.T) { so0.deleted = false state.setStateObject(so0) - root, _ := state.Commit() + root, _ := state.Commit(false) state.Reset(root) // and one with deleted == true diff --git a/core/state/statedb.go b/core/state/statedb.go index dcb897628..31957a6fe 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -213,6 +213,11 @@ func (self *StateDB) Exist(addr common.Address) bool { return self.GetStateObject(addr) != nil } +func (self *StateDB) Empty(addr common.Address) bool { + so := self.GetStateObject(addr) + return so == nil || so.empty() +} + func (self *StateDB) GetAccount(addr common.Address) vm.Account { return self.GetStateObject(addr) } @@ -516,10 +521,10 @@ func (self *StateDB) GetRefund() *big.Int { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot() common.Hash { +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { for addr, _ := range s.stateObjectsDirty { stateObject := s.stateObjects[addr] - if stateObject.suicided { + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) } else { stateObject.updateRoot(s.db) @@ -553,17 +558,18 @@ func (s *StateDB) DeleteSuicides() { } // Commit commits all state changes to the database. -func (s *StateDB) Commit() (root common.Hash, err error) { - root, batch := s.CommitBatch() +func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { + root, batch := s.CommitBatch(deleteEmptyObjects) 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 (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { +func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { batch = s.db.NewBatch() - root, _ = s.commit(batch) + root, _ = s.commit(batch, deleteEmptyObjects) + return root, batch } @@ -573,16 +579,18 @@ func (s *StateDB) clearJournalAndRefund() { s.refund = new(big.Int) } -func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) { +func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() // Commit objects to the trie. for addr, stateObject := range s.stateObjects { - if stateObject.suicided { + _, isDirty := s.stateObjectsDirty[addr] + switch { + case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. s.deleteStateObject(stateObject) - } else if _, ok := s.stateObjectsDirty[addr]; ok { + case isDirty: // 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 { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 5d041c740..a44818b7c 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -51,7 +51,7 @@ func TestUpdateLeaks(t *testing.T) { if i%3 == 0 { state.SetCode(addr, []byte{i, i, i, i, i}) } - state.IntermediateRoot() + state.IntermediateRoot(false) } // Ensure that no data was leaked into the database for _, key := range db.Keys() { @@ -86,7 +86,7 @@ func TestIntermediateLeaks(t *testing.T) { modify(transState, common.Address{byte(i)}, i, 0) } // Write modifications to trie. - transState.IntermediateRoot() + transState.IntermediateRoot(false) // Overwrite all the data with new values in the transient database. for i := byte(0); i < 255; i++ { @@ -95,10 +95,10 @@ func TestIntermediateLeaks(t *testing.T) { } // Commit and cross check the databases. - if _, err := transState.Commit(); err != nil { + if _, err := transState.Commit(false); err != nil { t.Fatalf("failed to commit transition state: %v", err) } - if _, err := finalState.Commit(); err != nil { + if _, err := finalState.Commit(false); err != nil { t.Fatalf("failed to commit final state: %v", err) } for _, key := range finalDb.Keys() { diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 949df7301..c40bbabaa 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -60,7 +60,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { state.updateStateObject(obj) accounts = append(accounts, acc) } - root, _ := state.Commit() + root, _ := state.Commit(false) // Return the generated state return db, root, accounts |
