refactor(state): split state into State and StateReader#3563
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3563 +/- ##
==========================================
+ Coverage 75.72% 75.76% +0.03%
==========================================
Files 384 386 +2
Lines 33757 33741 -16
==========================================
Hits 25563 25563
+ Misses 6398 6392 -6
+ Partials 1796 1786 -10 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
rodrodros
left a comment
There was a problem hiding this comment.
Why are the folders inside the state called "statesmth" instead of just "smth". Example:
statefactoryinstead offactorystatetestutilsinstead oftestutils
| return contract.Nonce, nil | ||
| } | ||
|
|
||
| func (s *State) ContractStorage(addr, key *felt.Felt) (felt.Felt, error) { |
There was a problem hiding this comment.
This method was moved to the StateReader in a different form. Instead of reading the dirty storage and falling back to the trie, it immediately reaches the storage trie for this value.
REASON: The dirty cache is only populated during the Update - which is writing. State instances are not shared, previously for all the read operations a new State object instance was generated using blockchain.HeadState or blockchain.StateAtBlockHash. In Juno, we never read the state, that is currently being written to the DB
| if !newComm.Equal(update.OldRoot) { | ||
| return fmt.Errorf("state commitment mismatch: %v (expected) != %v (actual)", update.OldRoot, &newComm) | ||
| } | ||
| if s.batch != nil { |
There was a problem hiding this comment.
Those non-nil guards are not needed anymore, State always enforces a non-nil batch
| s.contract.Nonce = *nonce | ||
| } | ||
|
|
||
| func (s *stateObject) getStorage(key *felt.Felt) (felt.Felt, error) { |
There was a problem hiding this comment.
This method was only used in the (s *State) ContractStorage. Removal reason explained here
7be724b to
4ab162c
Compare
This PR finishes the already started split of the new
State. We already have the proper constructorsNewandNewStateReader, however both produced the sameStateobject with and withoutBatch. Even though, the caller was safe from wrongly utilising the constructors by the existing common state interfaces, technically if the interfaces were not used, the caller could call state mutation methods on theStateReader.In this PR, the
Stateis decoupled intoStateReader- which only holds the read methods, andStatewhich extends theStateReaderwith the state mutation methods. The diff is quite big due to methods reordering - theStatedeclaration, constructor and methods remain instate.go,StateReaderdeclaration, constructor and methods were moved to thestate_reader.go.stateHistoryuses theStateReaderas the read layer now.The only modification worth paying attention to is the
ContractStoragemethod, which exists both in theStateandStateReader. Previously, theContractStoragewas first reading from the dirty cache on the state object, later falling back to the DB. But this scenario was effectively a dead code - the only time the dirty buffer is filled is during the sync (state.Updatecalled from theblockchain.Store()). We don't share the state objects, whenever we wanted read, we created a brand newStateobject using theblockchain.HeadState()orblockchain.StateAtBlockNumber- with an empty buffer. Now, that we split theStateinto theStateandStateReader, we can drop this logic and make theStateReaderread directly from the storage trie in the DB.