Skip to content

Commit 040950f

Browse files
karalabejorgemmsilva
authored andcommitted
core/state: storage journal entry should revert dirtyness too (ethereum#29641)
Currently our state journal tracks each storage update to a contract, having the ability to revert those changes to the previously set value. For the very first modification however, it behaves a bit wonky. Reverting the update doesn't actually remove the dirty-ness of the slot, rather leaves it as "change this slot to it's original value". This can cause issues down the line with for example write witnesses needing to gather an unneeded proof. This PR modifies the storageChange journal entry to not only track the previous value of a slot, but also whether there was any previous value at all set in the current execution context. In essence, the PR changes the semantic of storageChange so it does not simply track storage changes, rather it tracks dirty storage changes, an important distinction for being able to cleanly revert the journal item.
1 parent c9759f2 commit 040950f

File tree

2 files changed

+37
-16
lines changed

2 files changed

+37
-16
lines changed

core/state/journal.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ type (
129129
prev uint64
130130
}
131131
storageChange struct {
132-
account *common.Address
133-
key, prevalue common.Hash
132+
account *common.Address
133+
key common.Hash
134+
prevvalue *common.Hash
134135
}
135136
codeChange struct {
136137
account *common.Address
@@ -277,7 +278,7 @@ func (ch codeChange) copy() journalEntry {
277278
}
278279

279280
func (ch storageChange) revert(s *StateDB) {
280-
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
281+
s.getStateObject(*ch.account).setState(ch.key, ch.prevvalue)
281282
}
282283

283284
func (ch storageChange) dirtied() *common.Address {
@@ -286,9 +287,9 @@ func (ch storageChange) dirtied() *common.Address {
286287

287288
func (ch storageChange) copy() journalEntry {
288289
return storageChange{
289-
account: ch.account,
290-
key: ch.key,
291-
prevalue: ch.prevalue,
290+
account: ch.account,
291+
key: ch.key,
292+
prevvalue: ch.prevvalue,
292293
}
293294
}
294295

core/state/state_object.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,20 @@ func (s *stateObject) getTrie() (Trie, error) {
140140

141141
// GetState retrieves a value from the account storage trie.
142142
func (s *stateObject) GetState(key common.Hash) common.Hash {
143+
value, _ := s.getState(key)
144+
return value
145+
}
146+
147+
// getState retrieves a value from the account storage trie and also returns if
148+
// the slot is already dirty or not.
149+
func (s *stateObject) getState(key common.Hash) (common.Hash, bool) {
143150
// If we have a dirty value for this state entry, return it
144151
value, dirty := s.dirtyStorage[key]
145152
if dirty {
146-
return value
153+
return value, true
147154
}
148155
// Otherwise return the entry's original value
149-
return s.GetCommittedState(key)
156+
return s.GetCommittedState(key), false
150157
}
151158

152159
// GetCommittedState retrieves a value from the committed account storage trie.
@@ -209,25 +216,38 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
209216

210217
// SetState updates a value in account storage.
211218
func (s *stateObject) SetState(key, value common.Hash) {
212-
// If the new value is the same as old, don't set
213-
prev := s.GetState(key)
219+
// If the new value is the same as old, don't set. Otherwise, track only the
220+
// dirty changes, supporting reverting all of it back to no change.
221+
prev, dirty := s.getState(key)
214222
if prev == value {
215223
return
216224
}
225+
var prevvalue *common.Hash
226+
if dirty {
227+
prevvalue = &prev
228+
}
217229
// New value is different, update and journal the change
218230
s.db.journal.append(storageChange{
219-
account: &s.address,
220-
key: key,
221-
prevalue: prev,
231+
account: &s.address,
232+
key: key,
233+
prevvalue: prevvalue,
222234
})
223235
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
224236
s.db.logger.OnStorageChange(s.address, key, prev, value)
225237
}
226-
s.setState(key, value)
238+
s.setState(key, &value)
227239
}
228240

229-
func (s *stateObject) setState(key, value common.Hash) {
230-
s.dirtyStorage[key] = value
241+
// setState updates a value in account dirty storage. If the value being set is
242+
// nil (assuming journal revert), the dirtyness is removed.
243+
func (s *stateObject) setState(key common.Hash, value *common.Hash) {
244+
// If the first set is being reverted, undo the dirty marker
245+
if value == nil {
246+
delete(s.dirtyStorage, key)
247+
return
248+
}
249+
// Otherwise restore the previous value
250+
s.dirtyStorage[key] = *value
231251
}
232252

233253
// finalise moves all dirty storage slots into the pending area to be hashed or

0 commit comments

Comments
 (0)