fe5fefbd8e
* fix panic when transaction is reverted * update changelog * Update x/evm/keeper/context_stack.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
110 lines
3.5 KiB
Go
110 lines
3.5 KiB
Go
package keeper
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
// cachedContext is a pair of cache context and its corresponding commit method.
|
|
// They are obtained from the return value of `context.CacheContext()`.
|
|
type cachedContext struct {
|
|
ctx sdk.Context
|
|
commit func()
|
|
}
|
|
|
|
// ContextStack manages the initial context and a stack of cached contexts,
|
|
// to support the `StateDB.Snapshot` and `StateDB.RevertToSnapshot` methods.
|
|
type ContextStack struct {
|
|
// Context of the initial state before transaction execution.
|
|
// It's the context used by `StateDB.CommitedState`.
|
|
initialCtx sdk.Context
|
|
cachedContexts []cachedContext
|
|
}
|
|
|
|
// CurrentContext returns the top context of cached stack,
|
|
// if the stack is empty, returns the initial context.
|
|
func (cs *ContextStack) CurrentContext() sdk.Context {
|
|
l := len(cs.cachedContexts)
|
|
if l == 0 {
|
|
return cs.initialCtx
|
|
}
|
|
return cs.cachedContexts[l-1].ctx
|
|
}
|
|
|
|
// Reset sets the initial context and clear the cache context stack.
|
|
func (cs *ContextStack) Reset(ctx sdk.Context) {
|
|
cs.initialCtx = ctx
|
|
if len(cs.cachedContexts) > 0 {
|
|
cs.cachedContexts = []cachedContext{}
|
|
}
|
|
}
|
|
|
|
// IsEmpty returns true if the cache context stack is empty.
|
|
func (cs *ContextStack) IsEmpty() bool {
|
|
return len(cs.cachedContexts) == 0
|
|
}
|
|
|
|
// Commit commits all the cached contexts from top to bottom in order and clears the stack by setting an empty slice of cache contexts.
|
|
func (cs *ContextStack) Commit() {
|
|
// commit in order from top to bottom
|
|
for i := len(cs.cachedContexts) - 1; i >= 0; i-- {
|
|
// keep all the cosmos events
|
|
cs.initialCtx.EventManager().EmitEvents(cs.cachedContexts[i].ctx.EventManager().Events())
|
|
if cs.cachedContexts[i].commit == nil {
|
|
panic(fmt.Sprintf("commit function at index %d should not be nil", i))
|
|
} else {
|
|
cs.cachedContexts[i].commit()
|
|
}
|
|
}
|
|
cs.cachedContexts = []cachedContext{}
|
|
}
|
|
|
|
// CommitToRevision commit the cache after the target revision,
|
|
// to improve efficiency of db operations.
|
|
func (cs *ContextStack) CommitToRevision(target int) error {
|
|
if target < 0 || target >= len(cs.cachedContexts) {
|
|
return fmt.Errorf("snapshot index %d out of bound [%d..%d)", target, 0, len(cs.cachedContexts))
|
|
}
|
|
|
|
targetCtx := cs.cachedContexts[target].ctx
|
|
// commit in order from top to bottom
|
|
for i := len(cs.cachedContexts) - 1; i > target; i-- {
|
|
// keep all the cosmos events
|
|
targetCtx.EventManager().EmitEvents(cs.cachedContexts[i].ctx.EventManager().Events())
|
|
if cs.cachedContexts[i].commit == nil {
|
|
return fmt.Errorf("commit function at index %d should not be nil", i)
|
|
}
|
|
cs.cachedContexts[i].commit()
|
|
}
|
|
cs.cachedContexts = cs.cachedContexts[0 : target+1]
|
|
|
|
return nil
|
|
}
|
|
|
|
// Snapshot pushes a new cached context to the stack,
|
|
// and returns the index of it.
|
|
func (cs *ContextStack) Snapshot() int {
|
|
i := len(cs.cachedContexts)
|
|
ctx, commit := cs.CurrentContext().CacheContext()
|
|
cs.cachedContexts = append(cs.cachedContexts, cachedContext{ctx: ctx, commit: commit})
|
|
return i
|
|
}
|
|
|
|
// RevertToSnapshot pops all the cached contexts after the target index (inclusive).
|
|
// the target should be snapshot index returned by `Snapshot`.
|
|
// This function panics if the index is out of bounds.
|
|
func (cs *ContextStack) RevertToSnapshot(target int) {
|
|
if target < 0 || target >= len(cs.cachedContexts) {
|
|
panic(fmt.Errorf("snapshot index %d out of bound [%d..%d)", target, 0, len(cs.cachedContexts)))
|
|
}
|
|
cs.cachedContexts = cs.cachedContexts[:target]
|
|
}
|
|
|
|
// RevertAll discards all the cache contexts.
|
|
func (cs *ContextStack) RevertAll() {
|
|
if len(cs.cachedContexts) > 0 {
|
|
cs.RevertToSnapshot(0)
|
|
}
|
|
}
|