feat(server): make rollback command generic (#23321)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
Marko 2025-01-14 09:19:37 +01:00 committed by GitHub
parent a05ca7841b
commit a40ade9515
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 0 deletions

View File

@ -44,6 +44,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
* (sims) [#23013](https://github.com/cosmos/cosmos-sdk/pull/23013) Integration with app v2
* (x/auth/ante) [#23128](https://github.com/cosmos/cosmos-sdk/pull/23128) Allow custom verifyIsOnCurve when validate tx for public key like ethsecp256k1.
* (server) [#23128](https://github.com/cosmos/cosmos-sdk/pull/23128) Add custom rollback command option. In order to use it, you need to implement the Rollback interface and remove the default rollback command with `cmd.RemoveCommand(cmd.RollbackCmd)` and then add it back with `cmd.AddCommand(cmd.NewCustomRollbackCmd(appCreator, rollbackable))`.
### Improvements

View File

@ -51,3 +51,46 @@ application.
cmd.Flags().BoolVar(&removeBlock, "hard", false, "remove last block as well as state")
return cmd
}
// NewRollbackCmdRollback creates a command to set custom rollback functionality and multistore state by one height.
func NewRollbackCmdRollback[T types.Application, R Rollback](appCreator types.AppCreator[T], rollbackable R) *cobra.Command {
var removeBlock bool
cmd := &cobra.Command{
Use: "rollback",
Short: "rollback Cosmos SDK and CometBFT state by one height",
Long: `
A state rollback is performed to recover from an incorrect application state transition,
when CometBFT has persisted an incorrect app hash and is thus unable to make
progress. Rollback overwrites a state at height n with the state at height n - 1.
The application also rolls back to height n - 1. No blocks are removed, so upon
restarting CometBFT the transactions in block n will be re-executed against the
application.
`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := GetServerContextFromCmd(cmd)
db, err := OpenDB(ctx.Config.RootDir, GetAppDBBackend(ctx.Viper))
if err != nil {
return err
}
app := appCreator(ctx.Logger, db, nil, ctx.Viper)
// rollback CometBFT state
height, hash, err := rollbackable.RollbackToVersion(ctx, removeBlock)
if err != nil {
return fmt.Errorf("failed to rollback CometBFT state: %w", err)
}
// rollback the multistore
if err := app.CommitMultiStore().RollbackToVersion(height); err != nil {
return fmt.Errorf("failed to rollback to version: %w", err)
}
fmt.Printf("Rolled back state to height %d and hash %X\n", height, hash)
return nil
},
}
cmd.Flags().BoolVar(&removeBlock, "hard", false, "remove last block as well as state")
return cmd
}

View File

@ -580,3 +580,25 @@ func GetSnapshotStore(appOpts types.AppOptions) (*snapshots.Store, error) {
return snapshotStore, nil
}
// Rollbackable is an interface that allows for rollback operations.
// It is used to allow for custom rollback operations, such as those provided by the
// DefaultRollbackable implementation.
type Rollback interface {
RollbackToVersion(ctx *Context, removeBlock bool) (int64, []byte, error)
}
// DefaultRollbackable is a default implementation of the Rollbackable interface.
type DefaultRollbackable[T types.Application] struct {
appCreator types.AppCreator[T]
}
// NewDefaultRollbackable creates a new DefaultRollbackable instance.
func NewDefaultRollbackable[T types.Application](appCreator types.AppCreator[T]) *DefaultRollbackable[T] {
return &DefaultRollbackable[T]{appCreator}
}
// RollbackToVersion implements the Rollbackable interface.
func (d DefaultRollbackable[T]) RollbackToVersion(ctx *Context, removeBlock bool) (int64, []byte, error) {
return cmtcmd.RollbackState(ctx.Config, removeBlock)
}