aboutsummaryrefslogtreecommitdiffstats
path: root/core/state
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2016-10-20 19:36:29 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2016-11-13 17:44:04 +0800
commit445feaeef58bd89a113743dccf6fd5df55cde6fa (patch)
tree6c692a0989800f005a94bde2d372fcbe1f7630a1 /core/state
parent932d973e36ff0d41a6005b93d2d4ff1b4430fb04 (diff)
downloaddexon-445feaeef58bd89a113743dccf6fd5df55cde6fa.tar.gz
dexon-445feaeef58bd89a113743dccf6fd5df55cde6fa.tar.zst
dexon-445feaeef58bd89a113743dccf6fd5df55cde6fa.zip
core, core/state, trie: EIP158, reprice & skip empty account write
This commit implements EIP158 part 1, 2, 3 & 4 1. If an account is empty it's no longer written to the trie. An empty account is defined as (balance=0, nonce=0, storage=0, code=0). 2. Delete an empty account if it's touched 3. An empty account is redefined as either non-existent or empty. 4. Zero value calls and zero value suicides no longer consume the 25k reation costs. params: moved core/config to params Signed-off-by: Jeffrey Wilcke <jeffrey@ethereum.org>
Diffstat (limited to 'core/state')
-rw-r--r--core/state/state_object.go13
-rw-r--r--core/state/state_test.go6
-rw-r--r--core/state/statedb.go27
-rw-r--r--core/state/statedb_test.go8
-rw-r--r--core/state/sync_test.go2
5 files changed, 38 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 ae106e03b..1c4af0295 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -213,6 +213,13 @@ func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
+// Empty returns whether the state object is either non-existant
+// or empty according to the EIP161 specification (balance = nonce = code = 0)
+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 +523,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 +560,17 @@ 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)
glog.V(logger.Debug).Infof("Trie cache stats: %d misses, %d unloads", trie.CacheMisses(), trie.CacheUnloads())
return root, batch
@@ -575,16 +582,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 f5390d80f..8111320e6 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