Skip to content

Commit

Permalink
fix: Add MigrationModuleManager to handle migration of upgrade module…
Browse files Browse the repository at this point in the history
… before other modules (#16583)

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
(cherry picked from commit 0c1f6fc)

# Conflicts:
#	CHANGELOG.md
#	UPGRADING.md
#	baseapp/baseapp.go
#	docs/docs/building-modules/01-module-manager.md
#	docs/docs/core/00-baseapp.md
#	testutil/mock/types_mock_appmodule.go
#	types/module/module.go
#	types/module/module_test.go
  • Loading branch information
mmsqe authored and mergify[bot] committed Aug 16, 2023
1 parent ded6b47 commit c09b9e2
Show file tree
Hide file tree
Showing 11 changed files with 1,398 additions and 3 deletions.
358 changes: 358 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

361 changes: 361 additions & 0 deletions UPGRADING.md

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ 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
}

const (
runTxModeCheck runTxMode = iota // Check a transaction
runTxModeReCheck // Recheck a (pending) transaction after a commit
Expand Down Expand Up @@ -75,6 +81,9 @@ type BaseApp struct { //nolint: maligned
// 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 @@ -226,13 +235,19 @@ func (app *BaseApp) SetMsgServiceRouter(msgServiceRouter *MsgServiceRouter) {
app.msgServiceRouter = msgServiceRouter
}

<<<<<<< HEAD

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / dependency-review

expected declaration, found '<<'

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected declaration, found '<<' (typecheck)

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: non-declaration statement outside function body

Check failure on line 238 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: non-declaration statement outside function body
// SetCircuitBreaker sets the circuit breaker for the BaseApp.
// The circuit breaker is checked on every message execution to verify if a transaction should be executed or not.
func (app *BaseApp) SetCircuitBreaker(cb CircuitBreaker) {
if app.msgServiceRouter == nil {
panic("must be called after message server is set")
}
app.msgServiceRouter.SetCircuit(cb)
=======

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: unexpected ==, expecting }

Check failure on line 246 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: unexpected ==, expecting }
// SetMigrationModuleManager sets the MigrationModuleManager of a BaseApp.
func (app *BaseApp) SetMigrationModuleManager(migrationModuleManager MigrationModuleManager) {
app.migrationModuleManager = migrationModuleManager
>>>>>>> 0c1f6fc16 (fix: Add MigrationModuleManager to handle migration of upgrade module before other modules (#16583))

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / dependency-review

illegal character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: unexpected >>, expecting }

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

invalid character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

syntax error: unexpected >>, expecting }

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

invalid character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

syntax error: unexpected >>, expecting }

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

invalid character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: unexpected >>, expecting }

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

invalid character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

illegal character U+0023 '#' (typecheck)

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: unexpected >>, expecting }

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

invalid character U+0023 '#'

Check failure on line 250 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: unexpected >>, expecting }
}

// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp
Expand Down Expand Up @@ -614,6 +629,109 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context
return ctx.WithMultiStore(msCache), msCache
}

<<<<<<< HEAD

Check failure on line 632 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: non-declaration statement outside function body

Check failure on line 632 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

syntax error: non-declaration statement outside function body

Check failure on line 632 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

syntax error: non-declaration statement outside function body

Check failure on line 632 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: non-declaration statement outside function body

Check failure on line 632 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: non-declaration statement outside function body
=======
func (app *BaseApp) beginBlock(req *abci.RequestFinalizeBlock) (sdk.BeginBlock, error) {
var (
resp sdk.BeginBlock
err error
)

if app.beginBlocker != nil {

Check failure on line 640 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / dependency-review

expected declaration, found 'if'
ctx := app.finalizeBlockState.ctx
if app.migrationModuleManager != nil && app.migrationModuleManager.RunMigrationBeginBlock(ctx) {
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)
}
}
}
resp, err = app.beginBlocker(ctx)
if err != nil {
return resp, err
}

// append BeginBlock attributes to all events in the EndBlock response
for i, event := range resp.Events {
resp.Events[i].Attributes = append(
event.Attributes,
abci.EventAttribute{Key: "mode", Value: "BeginBlock"},
)
}

resp.Events = sdk.MarkEventsToIndex(resp.Events, app.indexEvents)
}

return resp, nil
}

func (app *BaseApp) deliverTx(tx []byte) *abci.ExecTxResult {
gInfo := sdk.GasInfo{}
resultStr := "successful"

var resp *abci.ExecTxResult

defer func() {

Check failure on line 677 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / dependency-review

expected declaration, found 'defer'
telemetry.IncrCounter(1, "tx", "count")
telemetry.IncrCounter(1, "tx", resultStr)
telemetry.SetGauge(float32(gInfo.GasUsed), "tx", "gas", "used")
telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted")
}()

gInfo, result, anteEvents, err := app.runTx(execModeFinalize, tx)
if err != nil {
resultStr = "failed"
resp = sdkerrors.ResponseExecTxResultWithEvents(
err,
gInfo.GasWanted,
gInfo.GasUsed,
sdk.MarkEventsToIndex(anteEvents, app.indexEvents),
app.trace,
)
return resp
}

resp = &abci.ExecTxResult{
GasWanted: int64(gInfo.GasWanted),
GasUsed: int64(gInfo.GasUsed),
Log: result.Log,
Data: result.Data,
Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents),
}

return resp
}

// endBlock is an application-defined function that is called after transactions
// have been processed in FinalizeBlock.
func (app *BaseApp) endBlock(ctx context.Context) (sdk.EndBlock, error) {
var endblock sdk.EndBlock

if app.endBlocker != nil {

Check failure on line 713 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / dependency-review

expected declaration, found 'if'
eb, err := app.endBlocker(app.finalizeBlockState.ctx)
if err != nil {
return endblock, err
}

// append EndBlock attributes to all events in the EndBlock response
for i, event := range eb.Events {
eb.Events[i].Attributes = append(
event.Attributes,
abci.EventAttribute{Key: "mode", Value: "EndBlock"},
)
}

eb.Events = sdk.MarkEventsToIndex(eb.Events, app.indexEvents)
endblock = eb
}

return endblock, nil
}

>>>>>>> 0c1f6fc16 (fix: Add MigrationModuleManager to handle migration of upgrade module before other modules (#16583))

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

syntax error: non-declaration statement outside function body

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (01)

invalid character U+0023 '#'

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

syntax error: non-declaration statement outside function body

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (03)

invalid character U+0023 '#'

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

syntax error: non-declaration statement outside function body

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (02)

invalid character U+0023 '#'

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: non-declaration statement outside function body

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / golangci-lint

invalid character U+0023 '#' (typecheck)

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

syntax error: non-declaration statement outside function body

Check failure on line 734 in baseapp/baseapp.go

View workflow job for this annotation

GitHub Actions / tests (00)

invalid character U+0023 '#'
// runTx processes a transaction within a given execution mode, encoded transaction
// bytes, and the decoded transaction itself. All state transitions occur through
// a cached Context depending on the mode provided. State only gets persisted
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/building-modules/01-module-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,23 @@ The module manager is used throughout the application whenever an action on a co
* `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./07-invariants.md) of module implementing the `HasInvariants` interface.
* `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./02-messages-and-queries.md#messages) and [`querier`](./04-query-services.md#legacy-queriers) routes.
* `RegisterServices(cfg Configurator)`: Registers the services of modules implementing the `HasServices` interface.
<<<<<<< HEAD
* `InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates.
* `ExportGenesis(ctx sdk.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 sdk.Context, cdc codec.JSONCodec, modulesToExport []string)`: Behaves the same as `ExportGenesis`, except takes a list of modules to export.
* `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: 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 `BeginBlockAppModule` 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 all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events.
* `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: 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 `EndBlockAppModule` 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.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any).
=======
* `InitGenesis(ctx context.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates.
* `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`: 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 successfully executed or not.
* `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 `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.
* `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 `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 `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).
* `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).
* `PrepareCheckState(ctx context.Context)`: During [`Commit`](../core/00-baseapp.md#commit), this function is called from `BaseApp` immediately after the [`deliverState`](../core/00-baseapp.md#state-updates) is written to the underlying [`rootMultiStore`](../core/04-store.md#commitmultistore) and, in turn calls the `PrepareCheckState` function of each module implementing the `HasPrepareCheckState` interface, in the order defined in `OrderPrepareCheckStaters`. It creates a child [context](../core/02-context.md) where the underlying `CacheMultiStore` is that of the next block's [`checkState`](../core/00-baseapp.md#state-updates). Writes to this state will be present in the [`checkState`](../core/00-baseapp.md#state-updates) of the next block, and therefore this method can be used to prepare the `checkState` for the next block.
>>>>>>> 0c1f6fc16 (fix: Add MigrationModuleManager to handle migration of upgrade module before other modules (#16583))
Here's an example of a concrete integration within an `simapp`:

Expand Down
5 changes: 5 additions & 0 deletions docs/docs/core/00-baseapp.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,13 @@ The [`BeginBlock` ABCI message](https://github.com/cometbft/cometbft/blob/v0.37.
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.
<<<<<<< HEAD
* 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 application's 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 `DeliverTx` and `EndBlock`.
=======
* 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.
* 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.
>>>>>>> 0c1f6fc16 (fix: Add MigrationModuleManager to handle migration of upgrade module before other modules (#16583))

### EndBlock

Expand Down
1 change: 1 addition & 0 deletions runtime/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func (a *AppBuilder) Build(
bApp.SetVersion(version.Version)
bApp.SetInterfaceRegistry(a.app.interfaceRegistry)
bApp.MountStores(a.app.storeKeys...)
bApp.SetMigrationModuleManager(a.app.ModuleManager)

a.app.BaseApp = bApp
a.app.configurator = module.NewConfigurator(a.app.cdc, a.app.MsgServiceRouter(), a.app.GRPCQueryRouter())
Expand Down
1 change: 1 addition & 0 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ func NewSimApp(
nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper),
)
bApp.SetMigrationModuleManager(app.ModuleManager)

// During begin block slashing happens after distr.BeginBlocker so that
// there is nothing left over in the validator fee pool, so as to keep the
Expand Down
Loading

0 comments on commit c09b9e2

Please sign in to comment.