feat!: return errors in module manager ABCI methods (#14847)

This commit is contained in:
Facundo Medica 2023-01-31 16:17:04 -03:00 committed by GitHub
parent 8eec98dcc5
commit deeb4bd362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 125 additions and 74 deletions

View File

@ -169,8 +169,9 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* [#14847](https://github.com/cosmos/cosmos-sdk/pull/14847) App and ModuleManager methods `InitGenesis`, `ExportGenesis`, `BeginBlock` and `EndBlock` now also return an error.
* (simulation) [#14728](https://github.com/cosmos/cosmos-sdk/pull/14728) Rename the `ParamChanges` field to `LegacyParamChange` and `Contents` to `LegacyProposalContents` in `simulation.SimulationState`. Additionally it adds a `ProposalMsgs` field to `simulation.SimulationState`.
* (x/upgrade) [14764](https://github.com/cosmos/cosmos-sdk/pull/14764) The `x/upgrade` module is extracted to have a separate go.mod file which allows it to be a standalone module.
* (x/upgrade) [#14764](https://github.com/cosmos/cosmos-sdk/pull/14764) The `x/upgrade` module is extracted to have a separate go.mod file which allows it to be a standalone module.
* (x/gov) [#14782](https://github.com/cosmos/cosmos-sdk/pull/14782) Move the `metadata` argument in `govv1.NewProposal` alongside `title` and `summary`.
* (store) [#14746](https://github.com/cosmos/cosmos-sdk/pull/14746) Extract Store in its own go.mod and rename the package to `cosmossdk.io/store`.
* (simulation) [#14751](https://github.com/cosmos/cosmos-sdk/pull/14751) Remove the `MsgType` field from `simulation.OperationInput` struct.

View File

@ -73,7 +73,10 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// add block gas meter for any genesis transactions (allow infinite gas)
app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter())
res = app.initChainer(app.deliverState.ctx, req)
res, err := app.initChainer(app.deliverState.ctx, req)
if err != nil {
panic(err)
}
// sanity check
if len(req.Validators) > 0 {
@ -195,7 +198,11 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
}
if app.beginBlocker != nil {
res = app.beginBlocker(app.deliverState.ctx, req)
var err error
res, err = app.beginBlocker(app.deliverState.ctx, req)
if err != nil {
panic(err)
}
res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents)
}
// set the signed validators for addition to context in deliverTx
@ -218,7 +225,11 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
}
if app.endBlocker != nil {
res = app.endBlocker(app.deliverState.ctx, req)
var err error
res, err = app.endBlocker(app.deliverState.ctx, req)
if err != nil {
panic(err)
}
res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents)
}

View File

@ -54,10 +54,10 @@ func TestABCI_InitChain(t *testing.T) {
// set a value in the store on init chain
key, value := []byte("hello"), []byte("goodbye")
var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
store := ctx.KVStore(capKey)
store.Set(key, value)
return abci.ResponseInitChain{}
return abci.ResponseInitChain{}, nil
}
query := abci.RequestQuery{
@ -579,12 +579,12 @@ func TestABCI_EndBlock(t *testing.T) {
ConsensusParams: cp,
})
app.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
app.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error) {
return abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
{Power: 100},
},
}
}, nil
})
app.Seal()
@ -1384,9 +1384,9 @@ func TestABCI_Proposal_Read_State_PrepareProposal(t *testing.T) {
someKey := []byte("some-key")
setInitChainerOpt := func(bapp *baseapp.BaseApp) {
bapp.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
bapp.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
ctx.KVStore(capKey1).Set(someKey, []byte("foo"))
return abci.ResponseInitChain{}
return abci.ResponseInitChain{}, nil
})
}

View File

@ -116,17 +116,17 @@ func (a *App) Load(loadLatest bool) error {
}
// BeginBlocker application updates every begin block
func (a *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
func (a *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error) {
return a.ModuleManager.BeginBlock(ctx, req)
}
// EndBlocker application updates every end block
func (a *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
func (a *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error) {
return a.ModuleManager.EndBlock(ctx, req)
}
// InitChainer initializes the chain.
func (a *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
func (a *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
var genesisState map[string]json.RawMessage
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
panic(err)

View File

@ -22,13 +22,13 @@ type AppI interface {
LegacyAmino() *codec.LegacyAmino
// Application updates every begin block.
BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock
BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error)
// Application updates every end block.
EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock
EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error)
// Application update at chain (i.e app) initialization.
InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain
InitChainer(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error)
// Loads the app at a given height.
LoadHeight(height int64) error

View File

@ -96,22 +96,21 @@ type GenesisJSON struct {
// InitChainer returns a function that can initialize the chain
// with key/value pairs
func InitChainer(key storetypes.StoreKey) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
func InitChainer(key storetypes.StoreKey) func(sdk.Context, abci.RequestInitChain) (abci.ResponseInitChain, error) {
return func(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
stateJSON := req.AppStateBytes
genesisState := new(GenesisJSON)
err := json.Unmarshal(stateJSON, genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
return abci.ResponseInitChain{}, err
}
for _, val := range genesisState.Values {
store := ctx.KVStore(key)
store.Set([]byte(val.Key), []byte(val.Value))
}
return abci.ResponseInitChain{}
return abci.ResponseInitChain{}, nil
}
}

View File

@ -29,11 +29,12 @@ import (
upgradeclient "cosmossdk.io/x/upgrade/client"
upgradekeeper "cosmossdk.io/x/upgrade/keeper"
upgradetypes "cosmossdk.io/x/upgrade/types"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/feegrant"
feegrantkeeper "cosmossdk.io/x/feegrant/keeper"
feegrantmodule "cosmossdk.io/x/feegrant/module"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
@ -559,12 +560,12 @@ func (app *SimApp) setPostHandler() {
func (app *SimApp) Name() string { return app.BaseApp.Name() }
// BeginBlocker application updates every begin block
func (app *SimApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
func (app *SimApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error) {
return app.ModuleManager.BeginBlock(ctx, req)
}
// EndBlocker application updates every end block
func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error) {
return app.ModuleManager.EndBlock(ctx, req)
}
@ -573,7 +574,7 @@ func (a *SimApp) Configurator() module.Configurator {
}
// InitChainer application update at chain initialization
func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
var genesisState GenesisState
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
panic(err)

View File

@ -30,7 +30,11 @@ func (app *SimApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAd
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}
genState := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
if err != nil {
return servertypes.ExportedApp{}, err
}
appState, err := json.MarshalIndent(genState, "", " ")
if err != nil {
return servertypes.ExportedApp{}, err

View File

@ -78,7 +78,7 @@ func (s *E2ETestSuite) SetupSuite() {
// Make sure not to forget to persist `myParams` into the actual store,
// this is done in InitChain.
app.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
app.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) (abci.ResponseInitChain, error) {
subspace.SetParamSet(ctx, &paramSet)
return app.InitChainer(ctx, req)

View File

@ -1497,7 +1497,6 @@ func (s *E2ETestSuite) TestBlockResults() {
)
return nil
}, 10)
}

View File

@ -5,19 +5,19 @@ import (
)
// InitChainer initializes application state at genesis
type InitChainer func(ctx Context, req abci.RequestInitChain) abci.ResponseInitChain
type InitChainer func(ctx Context, req abci.RequestInitChain) (abci.ResponseInitChain, error)
// BeginBlocker runs code before the transactions in a block
//
// Note: applications which set create_empty_blocks=false will not have regular block timing and should use
// e.g. BFT timestamps rather than block height for any periodic BeginBlock logic
type BeginBlocker func(ctx Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock
type BeginBlocker func(ctx Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error)
// EndBlocker runs code after the transactions in a block and return updates to the validator set
//
// Note: applications which set create_empty_blocks=false will not have regular block timing and should use
// e.g. BFT timestamps rather than block height for any periodic EndBlock logic
type EndBlocker func(ctx Context, req abci.RequestEndBlock) abci.ResponseEndBlock
type EndBlocker func(ctx Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error)
// PeerFilter responds to p2p filtering queries from Tendermint
type PeerFilter func(info string) abci.ResponseQuery

View File

@ -30,6 +30,7 @@ package module
import (
"encoding/json"
"errors"
"fmt"
"sort"
@ -376,7 +377,7 @@ func (m *Manager) RegisterServices(cfg Configurator) {
// InitGenesis performs init genesis functionality for modules. Exactly one
// module must return a non-empty validator set update to correctly initialize
// the chain.
func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage) abci.ResponseInitChain {
func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage) (abci.ResponseInitChain, error) {
var validatorUpdates []abci.ValidatorUpdate
ctx.Logger().Info("initializing blockchain state from genesis.json")
for _, moduleName := range m.OrderInitGenesis {
@ -391,12 +392,12 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData
// core API genesis
source, err := genesis.SourceFromRawJSON(genesisData[moduleName])
if err != nil {
panic(err)
return abci.ResponseInitChain{}, err
}
err = module.InitGenesis(ctx, source)
if err != nil {
panic(err)
return abci.ResponseInitChain{}, err
}
} else if module, ok := mod.(HasGenesis); ok {
ctx.Logger().Debug("running initialization for module", "module", moduleName)
@ -406,7 +407,7 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData
// only one module will update the validator set
if len(moduleValUpdates) > 0 {
if len(validatorUpdates) > 0 {
panic("validator InitGenesis updates already set by a previous module")
return abci.ResponseInitChain{}, errors.New("validator InitGenesis updates already set by a previous module")
}
validatorUpdates = moduleValUpdates
}
@ -420,60 +421,72 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData
return abci.ResponseInitChain{
Validators: validatorUpdates,
}
}, nil
}
// ExportGenesis performs export genesis functionality for modules
func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) map[string]json.RawMessage {
func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) (map[string]json.RawMessage, error) {
return m.ExportGenesisForModules(ctx, cdc, []string{})
}
// ExportGenesisForModules performs export genesis functionality for modules
func (m *Manager) ExportGenesisForModules(ctx sdk.Context, cdc codec.JSONCodec, modulesToExport []string) map[string]json.RawMessage {
func (m *Manager) ExportGenesisForModules(ctx sdk.Context, cdc codec.JSONCodec, modulesToExport []string) (map[string]json.RawMessage, error) {
if len(modulesToExport) == 0 {
modulesToExport = m.OrderExportGenesis
}
// verify modules exists in app, so that we don't panic in the middle of an export
if err := m.checkModulesExists(modulesToExport); err != nil {
panic(err)
return nil, err
}
channels := make(map[string]chan json.RawMessage)
type genesisResult struct {
bz json.RawMessage
err error
}
channels := make(map[string]chan genesisResult)
for _, moduleName := range modulesToExport {
mod := m.Modules[moduleName]
if module, ok := mod.(appmodule.HasGenesis); ok {
// core API genesis
channels[moduleName] = make(chan json.RawMessage)
go func(module appmodule.HasGenesis, ch chan json.RawMessage) {
channels[moduleName] = make(chan genesisResult)
go func(module appmodule.HasGenesis, ch chan genesisResult) {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
target := genesis.RawJSONTarget{}
err := module.ExportGenesis(ctx, target.Target())
if err != nil {
panic(err)
ch <- genesisResult{nil, err}
return
}
rawJSON, err := target.JSON()
if err != nil {
panic(err)
ch <- genesisResult{nil, err}
return
}
ch <- rawJSON
ch <- genesisResult{rawJSON, nil}
}(module, channels[moduleName])
} else if module, ok := mod.(HasGenesis); ok {
channels[moduleName] = make(chan json.RawMessage)
go func(module HasGenesis, ch chan json.RawMessage) {
channels[moduleName] = make(chan genesisResult)
go func(module HasGenesis, ch chan genesisResult) {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
ch <- module.ExportGenesis(ctx, cdc)
ch <- genesisResult{module.ExportGenesis(ctx, cdc), nil}
}(module, channels[moduleName])
}
}
genesisData := make(map[string]json.RawMessage)
for moduleName := range channels {
genesisData[moduleName] = <-channels[moduleName]
res := <-channels[moduleName]
if res.err != nil {
return nil, res.err
}
genesisData[moduleName] = res.bz
}
return genesisData
return genesisData, nil
}
// checkModulesExists verifies that all modules in the list exist in the app
@ -623,7 +636,7 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version
// BeginBlock performs begin block functionality for all modules. It creates a
// child context with an event manager to aggregate events emitted from all
// modules.
func (m *Manager) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
func (m *Manager) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
for _, moduleName := range m.OrderBeginBlockers {
@ -635,13 +648,13 @@ func (m *Manager) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) abci.R
return abci.ResponseBeginBlock{
Events: ctx.EventManager().ABCIEvents(),
}
}, nil
}
// EndBlock performs end block functionality for all modules. It creates a
// child context with an event manager to aggregate events emitted from all
// modules.
func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) (abci.ResponseEndBlock, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
validatorUpdates := []abci.ValidatorUpdate{}
@ -656,7 +669,7 @@ func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo
// only one module will update the validator set
if len(moduleValUpdates) > 0 {
if len(validatorUpdates) > 0 {
panic("validator EndBlock updates already set by a previous module")
return abci.ResponseEndBlock{}, errors.New("validator EndBlock updates already set by a previous module")
}
validatorUpdates = moduleValUpdates
@ -666,7 +679,7 @@ func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Events: ctx.EventManager().ABCIEvents(),
}
}, nil
}
// GetVersionMap gets consensus version from all modules

View File

@ -206,7 +206,8 @@ func TestManager_InitGenesis(t *testing.T) {
// panic because more than one module returns validator set updates
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModule2.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module2"])).Times(1).Return([]abci.ValidatorUpdate{{}})
require.Panics(t, func() { mm.InitGenesis(ctx, cdc, genesisData) })
_, err := mm.InitGenesis(ctx, cdc, genesisData)
require.Error(t, err)
// happy path
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}})
@ -242,14 +243,24 @@ func TestManager_ExportGenesis(t *testing.T) {
}`),
}
require.Equal(t, want, mm.ExportGenesis(ctx, cdc))
require.Equal(t, want, mm.ExportGenesisForModules(ctx, cdc, []string{}))
require.Equal(t, map[string]json.RawMessage{"module1": json.RawMessage(`{"key1": "value1"}`)}, mm.ExportGenesisForModules(ctx, cdc, []string{"module1"}))
require.NotEqual(t, map[string]json.RawMessage{"module1": json.RawMessage(`{"key1": "value1"}`)}, mm.ExportGenesisForModules(ctx, cdc, []string{"module2"}))
res, err := mm.ExportGenesis(ctx, cdc)
require.NoError(t, err)
require.Equal(t, want, res)
require.Panics(t, func() {
mm.ExportGenesisForModules(ctx, cdc, []string{"module1", "modulefoo"})
})
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{})
require.NoError(t, err)
require.Equal(t, want, res)
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module1"})
require.NoError(t, err)
require.Equal(t, map[string]json.RawMessage{"module1": json.RawMessage(`{"key1": "value1"}`)}, res)
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module2"})
require.NoError(t, err)
require.NotEqual(t, map[string]json.RawMessage{"module1": json.RawMessage(`{"key1": "value1"}`)}, res)
_, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module1", "modulefoo"})
require.Error(t, err)
}
func TestManager_BeginBlock(t *testing.T) {
@ -287,13 +298,15 @@ func TestManager_EndBlock(t *testing.T) {
mockAppModule1.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModule2.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1)
ret := mm.EndBlock(sdk.Context{}, req)
ret, err := mm.EndBlock(sdk.Context{}, req)
require.NoError(t, err)
require.Equal(t, []abci.ValidatorUpdate{{}}, ret.ValidatorUpdates)
// test panic
mockAppModule1.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModule2.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}})
require.Panics(t, func() { mm.EndBlock(sdk.Context{}, req) })
_, err = mm.EndBlock(sdk.Context{}, req)
require.Error(t, err)
}
// Core API exclusive tests
@ -355,14 +368,24 @@ func TestCoreAPIManager_ExportGenesis(t *testing.T) {
}`),
}
require.Equal(t, want, mm.ExportGenesis(ctx, cdc))
require.Equal(t, want, mm.ExportGenesisForModules(ctx, cdc, []string{}))
require.Equal(t, map[string]json.RawMessage{"module1": want["module1"]}, mm.ExportGenesisForModules(ctx, cdc, []string{"module1"}))
require.NotEqual(t, map[string]json.RawMessage{"module1": want["module1"]}, mm.ExportGenesisForModules(ctx, cdc, []string{"module2"}))
res, err := mm.ExportGenesis(ctx, cdc)
require.NoError(t, err)
require.Equal(t, want, res)
require.Panics(t, func() {
mm.ExportGenesisForModules(ctx, cdc, []string{"module1", "modulefoo"})
})
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{})
require.NoError(t, err)
require.Equal(t, want, res)
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module1"})
require.NoError(t, err)
require.Equal(t, map[string]json.RawMessage{"module1": want["module1"]}, res)
res, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module2"})
require.NoError(t, err)
require.NotEqual(t, map[string]json.RawMessage{"module1": want["module1"]}, res)
_, err = mm.ExportGenesisForModules(ctx, cdc, []string{"module1", "modulefoo"})
require.Error(t, err)
}
func TestCoreAPIManagerOrderSetters(t *testing.T) {

View File

@ -45,9 +45,9 @@ be a matter of minutes and not even require them to be awake at that time.
Setup an upgrade Keeper for the app and then define a BeginBlocker that calls the upgrade
keeper's BeginBlocker method:
func (app *myApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
func (app *myApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) (abci.ResponseBeginBlock, error) {
app.upgradeKeeper.BeginBlocker(ctx, req)
return abci.ResponseBeginBlock{}
return abci.ResponseBeginBlock{}, nil
}
The app must then integrate the upgrade keeper with its governance module as appropriate. The governance module