You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The journal is our appendable tracker for changes to state, making it possible to revert callscopes.
Originally, we did copy-on-write: whenever we entered a new call, we copied the entire statedb and all objects, and operated on the copy. This bit us back in the shanghai attacks, and we switched to a journal. Every time we do a modification, we add e.g.
0: account 0xA balancechange, was 1
1: account 0xA balancechange, was 2
2: account 0xA storagechange, key 0x123, was 0x00
3: account 0xA storagechange, key 0x123, was 0x01
And at any point, we can revert, applying the changes in the reverse order.
The journal is not aware of different scopes; it is just aware of a long list. The statedb tracks which indexes correlate to scopes:
func (s*StateDB) Snapshot() int {
id:=s.nextRevisionIds.nextRevisionId++s.validRevisions=append(s.validRevisions, revision{id, s.journal.length()})
returnid
}
Also, the journal is very basic, the events are added by the state package:
In order to make changes to the journal possible, a few changes should be introduced. First of all, instead of external callers just appending changes, they should invoke methods, such as:
By doing this, we leave it up to the journal internals exactly how to store changes.
Marking scopes
Secondly, we should move the scope-awareness into the Journal.
// Marks that a new scope has started. This methord returns an identifier,// which can be used to revert the changes in this scopeNewScope() int// Marks that the scope has ended. An ended scope is either not reverted,// or reverted in full when/if the parent scope reverts.EndScope(int)
// RevertScope reverts the changes in the given scope.RevertScope(*StateDB, int)
So the callers would change from
func (s*StateDB) Snapshot() int {
id:=s.nextRevisionIds.nextRevisionId++s.validRevisions=append(s.validRevisions, revision{id, s.journal.length()})
returnid
}
// RevertToSnapshot reverts all state changes made since the given revision.func (s*StateDB) RevertToSnapshot(revidint) {
// Find the snapshot in the stack of valid snapshots.idx:=sort.Search(len(s.validRevisions), func(iint) bool {
returns.validRevisions[i].id>=revid
})
ifidx==len(s.validRevisions) ||s.validRevisions[idx].id!=revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
snapshot:=s.validRevisions[idx].journalIndex// Replay the journal to undo changes and remove invalidated snapshotss.journal.revert(s, snapshot)
s.validRevisions=s.validRevisions[:idx]
}
into
// Snapshot returns an identifier for the current revision of the state.func (s*StateDB) Snapshot() int {
returns.journal.NewScope()
}
// RevertToSnapshot reverts all state changes made since the given revision.func (s*StateDB) RevertToSnapshot(revidint) {
s.journal.Revert(s, snapshot)
}
Using Sets
After these changes are in place, we can start collecting changesets based on scope, instead of linearly. For example, a contract which re-uses a storage slot will have several journal-entries
2: account 0xA storagechange, key 0x123, was 0x00
3: account 0xA storagechange, key 0x123, was 0x01
4: account 0xA storagechange, key 0x123, was 0x02
These can all be represented by only one journal-entry. Either naively by merging journal-entries, or by using a more elaborate scope. For example:
These changes are possible as long as the changes do not interfere with eachother. It does not matter whether nonceChange is reverted before or after the balanceChange. Some care needs to be taken with selfdestruct-change in this respect.
Also, the case when a child-scope finished is a bit finicky. It can be "merged up" to the parent scope, which is possibly wasted work. However, if it is not "merged up", then the work performed after the call returns needs to be considered it's own, new, scope.
1. sstore(0,1)
2. call( b) // might call sstore(0,2) on this same adress,
3. sstore(0,3)
The text was updated successfully, but these errors were encountered:
The journal is our appendable tracker for changes to state, making it possible to revert callscopes.
Originally, we did copy-on-write: whenever we entered a new call, we copied the entire statedb and all objects, and operated on the copy. This bit us back in the shanghai attacks, and we switched to a journal. Every time we do a modification, we add e.g.
And at any point, we can revert, applying the changes in the reverse order.
The journal is not aware of different scopes; it is just aware of a long list. The statedb tracks which indexes correlate to scopes:
Also, the journal is very basic, the events are added by the
state
package:Changing the API of the journal
In order to make changes to the journal possible, a few changes should be introduced. First of all, instead of external callers just
append
ing changes, they should invoke methods, such as:(the method
JournalReset
has been left out, I'm thinking we'll merge #28666 ) .Then external callers would do
By doing this, we leave it up to the journal internals exactly how to store changes.
Marking scopes
Secondly, we should move the scope-awareness into the Journal.
So the callers would change from
into
Using Sets
After these changes are in place, we can start collecting changesets based on scope, instead of linearly. For example, a contract which re-uses a storage slot will have several journal-entries
These can all be represented by only one journal-entry. Either naively by merging journal-entries, or by using a more elaborate scope. For example:
These changes are possible as long as the changes do not interfere with eachother. It does not matter whether nonceChange is reverted before or after the balanceChange. Some care needs to be taken with selfdestruct-change in this respect.
Also, the case when a child-scope finished is a bit finicky. It can be "merged up" to the parent scope, which is possibly wasted work. However, if it is not "merged up", then the work performed after the call returns needs to be considered it's own, new, scope.
The text was updated successfully, but these errors were encountered: