feat!: return errors in module manager ABCI methods (#14847)
This commit is contained in:
+3
-3
@@ -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
|
||||
|
||||
+37
-24
@@ -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
|
||||
|
||||
+40
-17
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user