Skip to content

Commit

Permalink
replace RunMigrationBeginBlock with PreBeginBlock
Browse files Browse the repository at this point in the history
* to runs before begin blocker, and be more generic than hardcode with migration
* allowed to modify consensus parameters and the changes are visible to the following state machine logics
  • Loading branch information
mmsqe committed Aug 17, 2023
1 parent 09db699 commit 9944fe1
Show file tree
Hide file tree
Showing 20 changed files with 159 additions and 308 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/distribution) [#17236](https://github.com/cosmos/cosmos-sdk/pull/17236) Using "validateCommunityTax" in "Params.ValidateBasic", preventing panic when field "CommunityTax" is nil.
* (types) [#16583](https://github.com/cosmos/cosmos-sdk/pull/16583) Add `MigrationModuleManager` to handle migration of upgrade module before other modules, ensuring access to the updated context with consensus parameters within the same block that executes the migration.
* (baseapp) [#17372](https://github.com/cosmos/cosmos-sdk/pull/17372) Stop state-machine when `RunMigrationBeginBlock` has error.
* (types) [#17421](https://github.com/cosmos/cosmos-sdk/pull/17421) Replace `RunMigrationBeginBlock` with `PreBlock`, which runs before begin blocker other modules, and allows to modify consensus parameters, and the changes are visible to the following state machine logics.

### API Breaking Changes

Expand Down
13 changes: 10 additions & 3 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,20 @@ and `VerifyVoteExtensionHandler` respectively. Please see [here](https://docs.co
for more info.


#### Upgrade
#### Set PreBlocker

**Users using `depinject` / app v2 do not need any changes, this is abstracted for them.**

```diff
+ app.SetPreBlocker(app.PreBlocker)
```
```diff
+ app.BaseApp.SetMigrationModuleManager(app.ModuleManager)
+func (app *SimApp) PreBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (sdk.ResponsePreBlock, error) {
+ return app.ModuleManager.PreBlock(ctx, req)
+}
```
BaseApp added `SetMigrationModuleManager` for apps to set their ModuleManager which implements `RunMigrationBeginBlock`. This is essential for BaseApp to run `BeginBlock` of upgrade module and inject `ConsensusParams` to context for `beginBlocker` during `beginBlock`.

BaseApp added `SetPreBlocker` for apps. This is essential for BaseApp to run `PreBlock` which runs before begin blocker other modules, and allows to modify consensus parameters, and the changes are visible to the following state machine logics.

#### Events

Expand Down
43 changes: 14 additions & 29 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ type (
StoreLoader func(ms storetypes.CommitMultiStore) error
)

// MigrationModuleManager is the interface that a migration module manager should implement to handle
// the execution of migration logic during the beginning of a block.
type MigrationModuleManager interface {
RunMigrationBeginBlock(ctx sdk.Context) (bool, error)
}

const (
execModeCheck execMode = iota // Check a transaction
execModeReCheck // Recheck a (pending) transaction after a commit
Expand Down Expand Up @@ -81,6 +75,7 @@ type BaseApp struct {
postHandler sdk.PostHandler // post handler, optional, e.g. for tips

initChainer sdk.InitChainer // ABCI InitChain handler
preBlocker sdk.PreBlocker // logic to run before BeginBlocker
beginBlocker sdk.BeginBlocker // (legacy ABCI) BeginBlock handler
endBlocker sdk.EndBlocker // (legacy ABCI) EndBlock handler
processProposal sdk.ProcessProposalHandler // ABCI ProcessProposal handler
Expand All @@ -98,9 +93,6 @@ type BaseApp struct {
// manages snapshots, i.e. dumps of app state at certain intervals
snapshotManager *snapshots.Manager

// manages migrate module
migrationModuleManager MigrationModuleManager

// volatile states:
//
// - checkState is set on InitChain and reset on Commit
Expand Down Expand Up @@ -276,11 +268,6 @@ func (app *BaseApp) SetMsgServiceRouter(msgServiceRouter *MsgServiceRouter) {
app.msgServiceRouter = msgServiceRouter
}

// SetMigrationModuleManager sets the MigrationModuleManager of a BaseApp.
func (app *BaseApp) SetMigrationModuleManager(migrationModuleManager MigrationModuleManager) {
app.migrationModuleManager = migrationModuleManager
}

// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp
// multistore.
func (app *BaseApp) MountStores(keys ...storetypes.StoreKey) {
Expand Down Expand Up @@ -684,22 +671,20 @@ func (app *BaseApp) beginBlock(req *abci.RequestFinalizeBlock) (sdk.BeginBlock,
err error
)

if app.beginBlocker != nil {
ctx := app.finalizeBlockState.ctx
if app.migrationModuleManager != nil {
if success, err := app.migrationModuleManager.RunMigrationBeginBlock(ctx); success {
cp := ctx.ConsensusParams()
// Manager skips this step if Block is non-nil since upgrade module is expected to set this params
// and consensus parameters should not be overwritten.
if cp.Block == nil {
if cp = app.GetConsensusParams(ctx); cp.Block != nil {
ctx = ctx.WithConsensusParams(cp)
}
}
} else if err != nil {
return sdk.BeginBlock{}, err
}
ctx := app.finalizeBlockState.ctx
if app.preBlocker != nil {
rsp, err := app.preBlocker(ctx)
if err != nil {
return sdk.BeginBlock{}, err
}
// rsp.ConsensusParamsChanged is true from preBlocker means ConsensusParams in store get changed
// write the consensus parameters in store to context
if rsp.ConsensusParamsChanged {
ctx = ctx.WithConsensusParams(app.GetConsensusParams(ctx))
app.finalizeBlockState.ctx = ctx
}
}
if app.beginBlocker != nil {
resp, err = app.beginBlocker(ctx)
if err != nil {
return resp, err
Expand Down
3 changes: 3 additions & 0 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ func TestBaseAppOptionSeal(t *testing.T) {
require.Panics(t, func() {
suite.baseApp.SetInitChainer(nil)
})
require.Panics(t, func() {
suite.baseApp.SetPreBlocker(nil)
})
require.Panics(t, func() {
suite.baseApp.SetBeginBlocker(nil)
})
Expand Down
8 changes: 8 additions & 0 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
app.initChainer = initChainer
}

func (app *BaseApp) SetPreBlocker(preBlocker sdk.PreBlocker) {
if app.sealed {
panic("SetPreBlocker() on sealed BaseApp")
}

app.preBlocker = preBlocker
}

func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) {
if app.sealed {
panic("SetBeginBlocker() on sealed BaseApp")
Expand Down
16 changes: 9 additions & 7 deletions core/appmodule/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ type HasPrecommit interface {
Precommit(context.Context) error
}

type ResponsePreBlock interface {
IsConsensusParamsChanged() bool
}

type HasPreBlocker interface {
AppModule
PreBlock(context.Context) (ResponsePreBlock, error)
}

// HasBeginBlocker is the extension interface that modules should implement to run
// custom logic before transaction processing in a block.
type HasBeginBlocker interface {
Expand All @@ -71,10 +80,3 @@ type HasEndBlocker interface {
// a block.
EndBlock(context.Context) error
}

// UpgradeModule is the extension interface that upgrade module should implement to differentiate
// it from other modules, migration handler need ensure the upgrade module's migration is executed
// before the rest of the modules.
type UpgradeModule interface {
IsUpgradeModule()
}
12 changes: 9 additions & 3 deletions docs/docs/building-apps/03-app-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,22 @@ the rest of the block as normal. Once 2/3 of the voting power has upgraded, the
resume the consensus mechanism. If the majority of operators add a custom `do-upgrade` script, this should
be a matter of minutes and not even require them to be awake at that time.

## Set Migration Module Manager
## Set PreBlocker

:::tip
Users using `depinject` / app v2 do not need any changes, this is abstracted for them.
:::

After app initiation, call `SetMigrationModuleManager` with ModuleManager to give BaseApp access to `RunMigrationBeginBlock`:
Call `SetPreBlocker` to run `PreBlock`:

```go
app.BaseApp.SetMigrationModuleManager(app.ModuleManager)
app.SetPreBlocker(app.PreBlocker)
```

```go
func (app *SimApp) PreBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (sdk.ResponsePreBlock, error) {
return app.ModuleManager.PreBlock(ctx, req)
}
```

## Integrating With An App
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/building-modules/01-module-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ The module manager is used throughout the application whenever an action on a co
* `ExportGenesis(ctx context.Context, cdc codec.JSONCodec)`: Calls the [`ExportGenesis`](./08-genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.
* `ExportGenesisForModules(ctx context.Context, cdc codec.JSONCodec, modulesToExport []string)`: Behaves the same as `ExportGenesis`, except takes a list of modules to export.
* `RunMigrationBeginBlock(ctx sdk.Context) (bool, error)`: At the beginning of each block, this function is called from [`BaseApp`](../core/00-baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./05-beginblock-endblock.md) function of the upgrade module implementing the `HasBeginBlocker` interface. The function returns a boolean value indicating whether the migration was executed or not and an error if fails.
* `BeginBlock(ctx context.Context) error`: At the beginning of each block, this function is called from [`BaseApp`](../core/00-baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./05-beginblock-endblock.md) function of each non-upgrade modules implementing the `appmodule.HasBeginBlocker` interface, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/08-events.md) emitted from non-upgrade modules.
* `BeginBlock(ctx context.Context) error`: At the beginning of each block, this function is called from [`BaseApp`](../core/00-baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./05-beginblock-endblock.md) function of each modules implementing the `appmodule.HasBeginBlocker` interface, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/08-events.md) emitted from each modules.
* `EndBlock(ctx context.Context) error`: At the end of each block, this function is called from [`BaseApp`](../core/00-baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./05-beginblock-endblock.md) function of each modules implementing the `appmodule.HasEndBlocker` interface, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/08-events.md) emitted from all modules. The function returns an `abci` which contains the aforementioned events, as well as validator set updates (if any).
* `EndBlock(context.Context) ([]abci.ValidatorUpdate, error)`: At the end of each block, this function is called from [`BaseApp`](../core/00-baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./05-beginblock-endblock.md) function of each modules implementing the `module.HasABCIEndblock` interface, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/08-events.md) emitted from all modules. The function returns an `abci` which contains the aforementioned events, as well as validator set updates (if any).
* `Precommit(ctx context.Context)`: During [`Commit`](../core/00-baseapp.md#commit), this function is called from `BaseApp` immediately before the [`deliverState`](../core/00-baseapp.md#state-updates) is written to the underlying [`rootMultiStore`](../core/04-store.md#commitmultistore) and, in turn calls the `Precommit` function of each modules implementing the `HasPrecommit` interface, in the order defined in `OrderPrecommiters`. It creates a child [context](../core/02-context.md) where the underlying `CacheMultiStore` is that of the newly committed block's [`finalizeblockstate`](../core/00-baseapp.md#state-updates).
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/core/00-baseapp.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/baseapp/abci.go#L623
This function also resets the [main gas meter](../basics/04-gas-fees.md#main-gas-meter).

* Initialize the [block gas meter](../basics/04-gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters.
* Run the application's [`beginBlocker()`](../basics/00-app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/05-beginblock-endblock.md#beginblock) method of each of the non-upgrade modules.
* Run the application's [`beginBlocker()`](../basics/00-app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/05-beginblock-endblock.md#beginblock) method of each of the modules.
* Set the [`VoteInfos`](https://github.com/cometbft/cometbft/blob/v0.37.x/spec/abci/abci++_methods.md#voteinfo) of the application, i.e. the list of validators whose _precommit_ for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./02-context.md) so that it can be used during transaction execution and EndBlock.

#### Transaction Execution
Expand Down
5 changes: 5 additions & 0 deletions runtime/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ func (a *App) Load(loadLatest bool) error {
return nil
}

// BeginBlocker application updates every begin block
func (a *App) PreBlocker(ctx sdk.Context) (sdk.ResponsePreBlock, error) {
return a.ModuleManager.PreBlock(ctx)
}

// BeginBlocker application updates every begin block
func (a *App) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) {
return a.ModuleManager.BeginBlock(ctx)
Expand Down
2 changes: 1 addition & 1 deletion runtime/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (a *AppBuilder) Build(db dbm.DB, traceStore io.Writer, baseAppOptions ...fu
bApp.SetVersion(version.Version)
bApp.SetInterfaceRegistry(a.app.interfaceRegistry)
bApp.MountStores(a.app.storeKeys...)
bApp.SetMigrationModuleManager(a.app.ModuleManager)
bApp.SetPreBlocker(a.app.PreBlocker)

a.app.BaseApp = bApp
a.app.configurator = module.NewConfigurator(a.app.cdc, a.app.MsgServiceRouter(), a.app.GRPCQueryRouter())
Expand Down
7 changes: 6 additions & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,6 @@ func NewSimApp(
consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper),
circuit.NewAppModule(appCodec, app.CircuitKeeper),
)
bApp.SetMigrationModuleManager(app.ModuleManager)

// BasicModuleManager defines the module BasicManager is in charge of setting up basic,
// non-dependant module elements, such as codec registration and genesis verification.
Expand Down Expand Up @@ -498,6 +497,7 @@ func NewSimApp(

// initialize BaseApp
app.SetInitChainer(app.InitChainer)
app.SetPreBlocker(app.PreBlocker)
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.setAnteHandler(txConfig)
Expand Down Expand Up @@ -579,6 +579,11 @@ func (app *SimApp) setPostHandler() {
// Name returns the name of the App
func (app *SimApp) Name() string { return app.BaseApp.Name() }

// PreBlocker application updates every begin block
func (app *SimApp) PreBlocker(ctx sdk.Context) (sdk.ResponsePreBlock, error) {
return app.ModuleManager.PreBlock(ctx)
}

// BeginBlocker application updates every begin block
func (app *SimApp) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) {
return app.ModuleManager.BeginBlock(ctx)
Expand Down
Loading

0 comments on commit 9944fe1

Please sign in to comment.