From 7502810ec97c33ca2d9232a4fc12c458a18ccc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 23 Sep 2019 17:58:18 +0300 Subject: [PATCH] core/state: reintroduce net sstore tracking, extend tests for it --- core/state/state_object.go | 44 ++++++++++++++++++++++++++---------- core/state/statedb.go | 2 ++ core/state/statedb_test.go | 46 +++++++++++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index e354cdf208..8680de021f 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -79,9 +79,10 @@ type stateObject struct { trie Trie // storage trie, which becomes non-nil on first access code Code // contract bytecode, which gets set when code is loaded - originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction - dirtyStorage Storage // Storage entries that have been modified since the last intermediate root or commit - fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. + originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction + pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block + dirtyStorage Storage // Storage entries that have been modified in the current transaction execution + fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. // Cache flags. // When an object is marked suicided it will be delete from the trie @@ -117,12 +118,13 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject { data.Root = emptyRoot } return &stateObject{ - db: db, - address: address, - addrHash: crypto.Keccak256Hash(address[:]), - data: data, - originStorage: make(Storage), - dirtyStorage: make(Storage), + db: db, + address: address, + addrHash: crypto.Keccak256Hash(address[:]), + data: data, + originStorage: make(Storage), + pendingStorage: make(Storage), + dirtyStorage: make(Storage), } } @@ -187,6 +189,9 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has return s.fakeStorage[key] } // If we have a pending write or clean cached, return that + if value, pending := s.pendingStorage[key]; pending { + return value + } if value, cached := s.originStorage[key]; cached { return value } @@ -255,15 +260,29 @@ func (s *stateObject) setState(key, value common.Hash) { s.dirtyStorage[key] = value } +// finalise moves all dirty storage slots into the pending area to be hashed or +// committed later. It is invoked at the end of every transaction. +func (s *stateObject) finalise() { + for key, value := range s.dirtyStorage { + s.pendingStorage[key] = value + } + if len(s.dirtyStorage) > 0 { + s.dirtyStorage = make(Storage) + } +} + // updateTrie writes cached storage modifications into the object's storage trie. func (s *stateObject) updateTrie(db Database) Trie { + // Make sure all dirty slots are finalized into the pending storage area + s.finalise() + // Track the amount of time wasted on updating the storge trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) } // Insert all the pending updates into the trie tr := s.getTrie(db) - for key, value := range s.dirtyStorage { + for key, value := range s.pendingStorage { // Skip noop changes, persist actual changes if value == s.originStorage[key] { continue @@ -278,8 +297,8 @@ func (s *stateObject) updateTrie(db Database) Trie { v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) s.setError(tr.TryUpdate(key[:], v)) } - if len(s.dirtyStorage) > 0 { - s.dirtyStorage = make(Storage) + if len(s.pendingStorage) > 0 { + s.pendingStorage = make(Storage) } return tr } @@ -360,6 +379,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject { stateObject.code = s.code stateObject.dirtyStorage = s.dirtyStorage.Copy() stateObject.originStorage = s.originStorage.Copy() + stateObject.pendingStorage = s.pendingStorage.Copy() stateObject.suicided = s.suicided stateObject.dirtyCode = s.dirtyCode stateObject.deleted = s.deleted diff --git a/core/state/statedb.go b/core/state/statedb.go index ec239ca1ac..4b4f374c92 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -672,6 +672,8 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { } if obj.suicided || (deleteEmptyObjects && obj.empty()) { obj.deleted = true + } else { + obj.finalise() } s.stateObjectsPending[addr] = struct{}{} s.stateObjectsDirty[addr] = struct{}{} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 145efbc893..5f58a234ca 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -473,7 +473,10 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) } if val := state.GetState(addr, skey); val != sval { - t.Fatalf("initial storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { + t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the non-committed state database and check pre/post commit balance copyOne := state.Copy() @@ -484,7 +487,10 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("first copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyOne.GetState(addr, skey); val != sval { - t.Fatalf("first copy pre-commit storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("first copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { + t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } copyOne.Commit(false) @@ -495,7 +501,10 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyOne.GetState(addr, skey); val != sval { - t.Fatalf("first copy post-commit storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyOne.GetCommittedState(addr, skey); val != sval { + t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) } // Copy the copy and check the balance once more copyTwo := copyOne.Copy() @@ -506,7 +515,10 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("second copy code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyTwo.GetState(addr, skey); val != sval { - t.Fatalf("second copy storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyTwo.GetCommittedState(addr, skey); val != sval { + t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) } } @@ -533,7 +545,10 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) } if val := state.GetState(addr, skey); val != sval { - t.Fatalf("initial storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { + t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the non-committed state database and check pre/post commit balance copyOne := state.Copy() @@ -544,7 +559,10 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("first copy code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyOne.GetState(addr, skey); val != sval { - t.Fatalf("first copy storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("first copy non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { + t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the copy and check the balance once more copyTwo := copyOne.Copy() @@ -555,9 +573,11 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("second copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyTwo.GetState(addr, skey); val != sval { - t.Fatalf("second copy pre-commit storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("second copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { + t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } - copyTwo.Commit(false) if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42) @@ -566,7 +586,10 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyTwo.GetState(addr, skey); val != sval { - t.Fatalf("second copy post-commit storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyTwo.GetCommittedState(addr, skey); val != sval { + t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) } // Copy the copy-copy and check the balance once more copyThree := copyTwo.Copy() @@ -577,7 +600,10 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("third copy code mismatch: have %x, want %x", code, []byte("hello")) } if val := copyThree.GetState(addr, skey); val != sval { - t.Fatalf("third copy storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyThree.GetCommittedState(addr, skey); val != sval { + t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval) } }