Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
docs: state revert logic (#421)
Browse files Browse the repository at this point in the history
Closes #356

Co-authored-by: Federico Kunze Küllmer <[email protected]>
  • Loading branch information
yihuang and fedekunze authored Sep 13, 2021
1 parent 4f61ecb commit 8bcab7b
Showing 1 changed file with 59 additions and 0 deletions.
59 changes: 59 additions & 0 deletions docs/core/state_snapshot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Snapshot and Revert in Ethermint

EVM uses state-reverting exceptions to handle errors. Such an exception will undo all changes made to the state in the current call (and all its sub-calls), and the caller could handle the error and don't propagate. We need to implement the `Snapshot` and `RevertToSnapshot` apis in `StateDB` interfaces to support this feature.

[go-ethereum implementation](https://github.com/ethereum/go-ethereum/blob/master/core/state/journal.go#L39) manages transient states in memory, and uses a list of journal logs to record all the state modification operations done so far, snapshot is an index in the log list, and to revert to a snapshot it just undo the journal logs after the snapshot index in reversed order.

Ethermint uses cosmos-sdk's storage api to manage states, fortunately the storage api supports creating cached overlays, it works like this:

```golang
// create a cached overlay storage on top of ctx storage.
overlayCtx, commit := ctx.CacheContext()
// Modify states using the overlayed storage
err := doCall(overlayCtx)
if err != nil {
return err
}
// commit will write the dirty states into the underlying storage
commit()

// Now, just drop the overlayCtx and keep using ctx
```

And it can be used in a nested way, like this:

```golang
overlayCtx1, commit1 := ctx.CacheContext()
doCall1(overlayCtx1)
{
overlayCtx2, commit2 := overlayCtx1.CacheContext()
doCall2(overlayCtx2)
commit2()
}
commit1()
```

With this feature, we can use a stake of overlayed contexts to implement nested `Snapshot` and `RevertToSnapshot` calls.

```golang
type cachedContext struct {
ctx sdk.Context
commit func()
}
var contextStack []cachedContext
func Snapshot() int {
ctx, commit := contextStack.Top().CacheContext()
contextStack.Push(cachedContext{ctx, commit})
return len(contextStack) - 1
}
func RevertToSnapshot(int snapshot) {
contextStack = contextStack[:snapshot]
}
func Commit() {
for i := len(contextStack) - 1; i >= 0; i-- {
contextStack[i].commit()
}
contextStack = {}
}
```

0 comments on commit 8bcab7b

Please sign in to comment.