feat: support core API genesis in module manager (#14582)

Co-authored-by: Facundo Medica <facundomedica@gmail.com>
Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
Aaron Craelius 2023-01-27 12:14:56 -05:00 committed by GitHub
parent 8ab4389906
commit aceadb0b7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 558 additions and 43 deletions

View File

@ -5,9 +5,11 @@
package mock
import (
context "context"
json "encoding/json"
reflect "reflect"
appmodule "cosmossdk.io/core/appmodule"
client "github.com/cosmos/cosmos-sdk/client"
codec "github.com/cosmos/cosmos-sdk/codec"
types "github.com/cosmos/cosmos-sdk/codec/types"
@ -239,3 +241,106 @@ func (mr *MockAppModuleWithAllExtensionsMockRecorder) ValidateGenesis(arg0, arg1
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGenesis", reflect.TypeOf((*MockAppModuleWithAllExtensions)(nil).ValidateGenesis), arg0, arg1, arg2)
}
// MockCoreAppModule is a mock of CoreAppModule interface.
type MockCoreAppModule struct {
ctrl *gomock.Controller
recorder *MockCoreAppModuleMockRecorder
}
// MockCoreAppModuleMockRecorder is the mock recorder for MockCoreAppModule.
type MockCoreAppModuleMockRecorder struct {
mock *MockCoreAppModule
}
// NewMockCoreAppModule creates a new mock instance.
func NewMockCoreAppModule(ctrl *gomock.Controller) *MockCoreAppModule {
mock := &MockCoreAppModule{ctrl: ctrl}
mock.recorder = &MockCoreAppModuleMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCoreAppModule) EXPECT() *MockCoreAppModuleMockRecorder {
return m.recorder
}
// DefaultGenesis mocks base method.
func (m *MockCoreAppModule) DefaultGenesis(arg0 appmodule.GenesisTarget) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DefaultGenesis", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// DefaultGenesis indicates an expected call of DefaultGenesis.
func (mr *MockCoreAppModuleMockRecorder) DefaultGenesis(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultGenesis", reflect.TypeOf((*MockCoreAppModule)(nil).DefaultGenesis), arg0)
}
// ExportGenesis mocks base method.
func (m *MockCoreAppModule) ExportGenesis(arg0 context.Context, arg1 appmodule.GenesisTarget) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ExportGenesis", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// ExportGenesis indicates an expected call of ExportGenesis.
func (mr *MockCoreAppModuleMockRecorder) ExportGenesis(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExportGenesis", reflect.TypeOf((*MockCoreAppModule)(nil).ExportGenesis), arg0, arg1)
}
// InitGenesis mocks base method.
func (m *MockCoreAppModule) InitGenesis(arg0 context.Context, arg1 appmodule.GenesisSource) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InitGenesis", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// InitGenesis indicates an expected call of InitGenesis.
func (mr *MockCoreAppModuleMockRecorder) InitGenesis(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitGenesis", reflect.TypeOf((*MockCoreAppModule)(nil).InitGenesis), arg0, arg1)
}
// IsAppModule mocks base method.
func (m *MockCoreAppModule) IsAppModule() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "IsAppModule")
}
// IsAppModule indicates an expected call of IsAppModule.
func (mr *MockCoreAppModuleMockRecorder) IsAppModule() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAppModule", reflect.TypeOf((*MockCoreAppModule)(nil).IsAppModule))
}
// IsOnePerModuleType mocks base method.
func (m *MockCoreAppModule) IsOnePerModuleType() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "IsOnePerModuleType")
}
// IsOnePerModuleType indicates an expected call of IsOnePerModuleType.
func (mr *MockCoreAppModuleMockRecorder) IsOnePerModuleType() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOnePerModuleType", reflect.TypeOf((*MockCoreAppModule)(nil).IsOnePerModuleType))
}
// ValidateGenesis mocks base method.
func (m *MockCoreAppModule) ValidateGenesis(arg0 appmodule.GenesisSource) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateGenesis", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// ValidateGenesis indicates an expected call of ValidateGenesis.
func (mr *MockCoreAppModuleMockRecorder) ValidateGenesis(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGenesis", reflect.TypeOf((*MockCoreAppModule)(nil).ValidateGenesis), arg0)
}

163
types/module/core_module.go Normal file
View File

@ -0,0 +1,163 @@
package module
import (
"encoding/json"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/genesis"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
_ AppModuleBasic = coreAppModuleBasicAdapator{}
_ HasGenesis = coreAppModuleBasicAdapator{}
)
// CoreAppModuleBasicAdaptor wraps the core API module as an AppModule that this version
// of the SDK can use.
func CoreAppModuleBasicAdaptor(name string, module appmodule.AppModule) AppModuleBasic {
return coreAppModuleBasicAdapator{
name: name,
module: module,
}
}
type coreAppModuleBasicAdapator struct {
name string
module appmodule.AppModule
}
// DefaultGenesis implements HasGenesis
func (c coreAppModuleBasicAdapator) DefaultGenesis(codec.JSONCodec) json.RawMessage {
if mod, ok := c.module.(appmodule.HasGenesis); ok {
target := genesis.RawJSONTarget{}
err := mod.DefaultGenesis(target.Target())
if err != nil {
panic(err)
}
res, err := target.JSON()
if err != nil {
panic(err)
}
return res
}
return nil
}
// ValidateGenesis implements HasGenesis
func (c coreAppModuleBasicAdapator) ValidateGenesis(cdc codec.JSONCodec, txConfig client.TxEncodingConfig, bz json.RawMessage) error {
if mod, ok := c.module.(appmodule.HasGenesis); ok {
source, err := genesis.SourceFromRawJSON(bz)
if err != nil {
return err
}
if err := mod.ValidateGenesis(source); err != nil {
return err
}
}
return nil
}
// ExportGenesis implements HasGenesis
func (c coreAppModuleBasicAdapator) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
if module, ok := c.module.(appmodule.HasGenesis); ok {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
target := genesis.RawJSONTarget{}
err := module.ExportGenesis(ctx, target.Target())
if err != nil {
panic(err)
}
rawJSON, err := target.JSON()
if err != nil {
panic(err)
}
return rawJSON
}
return nil
}
// InitGenesis implements HasGenesis
func (c coreAppModuleBasicAdapator) InitGenesis(ctx sdk.Context, _ codec.JSONCodec, bz json.RawMessage) []abci.ValidatorUpdate {
if module, ok := c.module.(appmodule.HasGenesis); ok {
// core API genesis
source, err := genesis.SourceFromRawJSON(bz)
if err != nil {
panic(err)
}
err = module.InitGenesis(ctx, source)
if err != nil {
panic(err)
}
}
return nil
}
// Name implements AppModuleBasic
func (c coreAppModuleBasicAdapator) Name() string {
return c.name
}
// GetQueryCmd implements AppModuleBasic
func (c coreAppModuleBasicAdapator) GetQueryCmd() *cobra.Command {
if mod, ok := c.module.(interface {
GetQueryCmd() *cobra.Command
}); ok {
return mod.GetQueryCmd()
}
return nil
}
// GetTxCmd implements AppModuleBasic
func (c coreAppModuleBasicAdapator) GetTxCmd() *cobra.Command {
if mod, ok := c.module.(interface {
GetTxCmd() *cobra.Command
}); ok {
return mod.GetTxCmd()
}
return nil
}
// RegisterGRPCGatewayRoutes implements AppModuleBasic
func (c coreAppModuleBasicAdapator) RegisterGRPCGatewayRoutes(ctx client.Context, mux *runtime.ServeMux) {
if mod, ok := c.module.(interface {
RegisterGRPCGatewayRoutes(context client.Context, mux *runtime.ServeMux)
}); ok {
mod.RegisterGRPCGatewayRoutes(ctx, mux)
}
}
// RegisterInterfaces implements AppModuleBasic
func (c coreAppModuleBasicAdapator) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
if mod, ok := c.module.(interface {
RegisterInterfaces(registry codectypes.InterfaceRegistry)
}); ok {
mod.RegisterInterfaces(registry)
}
}
// RegisterLegacyAminoCodec implements AppModuleBasic
func (c coreAppModuleBasicAdapator) RegisterLegacyAminoCodec(amino *codec.LegacyAmino) {
if mod, ok := c.module.(interface {
RegisterLegacyAminoCodec(amino *codec.LegacyAmino)
}); ok {
mod.RegisterLegacyAminoCodec(amino)
}
}

1
types/module/genesis.go Normal file
View File

@ -0,0 +1 @@
package module

View File

@ -1,6 +1,10 @@
package module_test
import "github.com/cosmos/cosmos-sdk/types/module"
import (
"cosmossdk.io/core/appmodule"
"github.com/cosmos/cosmos-sdk/types/module"
)
// AppModuleWithAllExtensions is solely here for the purpose of generating
// mocks to be used in module tests.
@ -13,3 +17,10 @@ type AppModuleWithAllExtensions interface {
module.BeginBlockAppModule
module.EndBlockAppModule
}
// CoreAppModule is solely here for the purpose of generating
// mocks to be used in module tests.
type CoreAppModule interface {
appmodule.AppModule
appmodule.HasGenesis
}

View File

@ -34,6 +34,7 @@ import (
"sort"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/genesis"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
@ -43,7 +44,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -52,7 +53,7 @@ import (
type AppModuleBasic interface {
HasName
RegisterLegacyAminoCodec(*codec.LegacyAmino)
RegisterInterfaces(codectypes.InterfaceRegistry)
RegisterInterfaces(types.InterfaceRegistry)
// client functionality
RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux)
@ -93,7 +94,7 @@ func (bm BasicManager) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
}
// RegisterInterfaces registers all module interface types
func (bm BasicManager) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
func (bm BasicManager) RegisterInterfaces(registry types.InterfaceRegistry) {
for _, m := range bm {
m.RegisterInterfaces(registry)
}
@ -101,21 +102,22 @@ func (bm BasicManager) RegisterInterfaces(registry codectypes.InterfaceRegistry)
// DefaultGenesis provides default genesis information for all modules
func (bm BasicManager) DefaultGenesis(cdc codec.JSONCodec) map[string]json.RawMessage {
genesis := make(map[string]json.RawMessage)
genesisData := make(map[string]json.RawMessage)
for _, b := range bm {
if mod, ok := b.(HasGenesisBasics); ok {
genesis[b.Name()] = mod.DefaultGenesis(cdc)
genesisData[b.Name()] = mod.DefaultGenesis(cdc)
}
}
return genesis
return genesisData
}
// ValidateGenesis performs genesis state validation for all modules
func (bm BasicManager) ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage) error {
func (bm BasicManager) ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesisData map[string]json.RawMessage) error {
for _, b := range bm {
// first check if the module is an adapted Core API Module
if mod, ok := b.(HasGenesisBasics); ok {
if err := mod.ValidateGenesis(cdc, txEncCfg, genesis[b.Name()]); err != nil {
if err := mod.ValidateGenesis(cdc, txEncCfg, genesisData[b.Name()]); err != nil {
return err
}
}
@ -284,6 +286,9 @@ func NewManagerFromMap(moduleMap map[string]appmodule.AppModule) *Manager {
modulesStr = append(modulesStr, name)
}
// Sort the modules by name. Given that we are using a map above we can't guarantee the order.
sort.Strings(modulesStr)
return &Manager{
Modules: simpleModuleMap,
OrderInitGenesis: modulesStr,
@ -297,6 +302,10 @@ func NewManagerFromMap(moduleMap map[string]appmodule.AppModule) *Manager {
func (m *Manager) SetOrderInitGenesis(moduleNames ...string) {
m.assertNoForgottenModules("SetOrderInitGenesis", moduleNames, func(moduleName string) bool {
module := m.Modules[moduleName]
if _, hasGenesis := module.(appmodule.HasGenesis); hasGenesis {
return !hasGenesis
}
_, hasGenesis := module.(HasGenesis)
return !hasGenesis
})
@ -307,6 +316,10 @@ func (m *Manager) SetOrderInitGenesis(moduleNames ...string) {
func (m *Manager) SetOrderExportGenesis(moduleNames ...string) {
m.assertNoForgottenModules("SetOrderExportGenesis", moduleNames, func(moduleName string) bool {
module := m.Modules[moduleName]
if _, hasGenesis := module.(appmodule.HasGenesis); hasGenesis {
return !hasGenesis
}
_, hasGenesis := module.(HasGenesis)
return !hasGenesis
})
@ -371,9 +384,22 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData
continue
}
if module, ok := m.Modules[moduleName].(HasGenesis); ok {
mod := m.Modules[moduleName]
// we might get an adapted module, a native core API module or a legacy module
if module, ok := mod.(appmodule.HasGenesis); ok {
ctx.Logger().Debug("running initialization for module", "module", moduleName)
// core API genesis
source, err := genesis.SourceFromRawJSON(genesisData[moduleName])
if err != nil {
panic(err)
}
err = module.InitGenesis(ctx, source)
if err != nil {
panic(err)
}
} else if module, ok := mod.(HasGenesis); ok {
ctx.Logger().Debug("running initialization for module", "module", moduleName)
moduleValUpdates := module.InitGenesis(ctx, cdc, genesisData[moduleName])
// use these validator updates if provided, the module manager assumes
@ -407,7 +433,6 @@ func (m *Manager) ExportGenesisForModules(ctx sdk.Context, cdc codec.JSONCodec,
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)
@ -415,11 +440,29 @@ func (m *Manager) ExportGenesisForModules(ctx sdk.Context, cdc codec.JSONCodec,
channels := make(map[string]chan json.RawMessage)
for _, moduleName := range modulesToExport {
if module, ok := m.Modules[moduleName].(HasGenesis); ok {
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) {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
target := genesis.RawJSONTarget{}
err := module.ExportGenesis(ctx, target.Target())
if err != nil {
panic(err)
}
rawJSON, err := target.JSON()
if err != nil {
panic(err)
}
ch <- rawJSON
}(module, channels[moduleName])
} else if module, ok := mod.(HasGenesis); ok {
channels[moduleName] = make(chan json.RawMessage)
go func(module HasGenesis, ch chan json.RawMessage) {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
ch <- module.ExportGenesis(ctx, cdc)
}(module, channels[moduleName])
}

View File

@ -1,10 +1,13 @@
package module_test
import (
"context"
"encoding/json"
"errors"
"io"
"testing"
"cosmossdk.io/core/appmodule"
"github.com/golang/mock/gomock"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
@ -28,30 +31,51 @@ func TestBasicManager(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
wantDefaultGenesis := map[string]json.RawMessage{"mockAppModuleBasic1": json.RawMessage(``)}
// Test with a legacy module, a mock core module that doesn't return anything,
// and a core module defined in this file
expDefaultGenesis := map[string]json.RawMessage{
"mockAppModuleBasic1": json.RawMessage(``),
"mockCoreAppModule2": json.RawMessage(`null`),
"mockCoreAppModule3": json.RawMessage(`{
"someField": "someKey"
}`),
}
// legacy module
mockAppModuleBasic1 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModuleBasic1.EXPECT().Name().AnyTimes().Return("mockAppModuleBasic1")
mockAppModuleBasic1.EXPECT().DefaultGenesis(gomock.Eq(cdc)).Times(1).Return(json.RawMessage(``))
mockAppModuleBasic1.EXPECT().ValidateGenesis(gomock.Eq(cdc), gomock.Eq(nil), gomock.Eq(wantDefaultGenesis["mockAppModuleBasic1"])).Times(1).Return(errFoo)
// Allow ValidateGenesis to be called any times because other module can fail before this one is called.
mockAppModuleBasic1.EXPECT().ValidateGenesis(gomock.Eq(cdc), gomock.Eq(nil), gomock.Eq(expDefaultGenesis["mockAppModuleBasic1"])).AnyTimes().Return(nil)
mockAppModuleBasic1.EXPECT().RegisterLegacyAminoCodec(gomock.Eq(legacyAmino)).Times(1)
mockAppModuleBasic1.EXPECT().RegisterInterfaces(gomock.Eq(interfaceRegistry)).Times(1)
mockAppModuleBasic1.EXPECT().GetTxCmd().Times(1).Return(nil)
mockAppModuleBasic1.EXPECT().GetQueryCmd().Times(1).Return(nil)
mm := module.NewBasicManager(mockAppModuleBasic1)
require.Equal(t, mm["mockAppModuleBasic1"], mockAppModuleBasic1)
// mock core API module
mockCoreAppModule2 := mock.NewMockCoreAppModule(mockCtrl)
mockCoreAppModule2.EXPECT().DefaultGenesis(gomock.Any()).AnyTimes().Return(nil)
mockCoreAppModule2.EXPECT().ValidateGenesis(gomock.Any()).AnyTimes().Return(nil)
mockAppModule2 := module.CoreAppModuleBasicAdaptor("mockCoreAppModule2", mockCoreAppModule2)
// mock core API module (but all methods are implemented)
mockCoreAppModule3 := module.CoreAppModuleBasicAdaptor("mockCoreAppModule3", MockCoreAppModule{})
mm := module.NewBasicManager(mockAppModuleBasic1, mockAppModule2, mockCoreAppModule3)
require.Equal(t, mockAppModuleBasic1, mm["mockAppModuleBasic1"])
require.Equal(t, mockAppModule2, mm["mockCoreAppModule2"])
require.Equal(t, mockCoreAppModule3, mm["mockCoreAppModule3"])
mm.RegisterLegacyAminoCodec(legacyAmino)
mm.RegisterInterfaces(interfaceRegistry)
require.Equal(t, wantDefaultGenesis, mm.DefaultGenesis(cdc))
require.Equal(t, expDefaultGenesis, mm.DefaultGenesis(cdc))
var data map[string]string
require.Equal(t, map[string]string(nil), data)
require.True(t, errors.Is(errFoo, mm.ValidateGenesis(cdc, nil, wantDefaultGenesis)))
require.ErrorIs(t, mm.ValidateGenesis(cdc, nil, expDefaultGenesis), errFoo)
mockCmd := &cobra.Command{Use: "root"}
mm.AddTxCommands(mockCmd)
@ -59,7 +83,7 @@ func TestBasicManager(t *testing.T) {
mm.AddQueryCommands(mockCmd)
// validate genesis returns nil
require.Nil(t, module.NewBasicManager().ValidateGenesis(cdc, nil, wantDefaultGenesis))
require.Nil(t, module.NewBasicManager().ValidateGenesis(cdc, nil, expDefaultGenesis))
}
func TestGenesisOnlyAppModule(t *testing.T) {
@ -79,28 +103,29 @@ func TestManagerOrderSetters(t *testing.T) {
t.Cleanup(mockCtrl.Finish)
mockAppModule1 := mock.NewMockAppModule(mockCtrl)
mockAppModule2 := mock.NewMockAppModule(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("module3", mockAppModule3))
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))
require.Equal(t, []string{"module1", "module2"}, mm.OrderInitGenesis)
mm.SetOrderInitGenesis("module2", "module1")
require.Equal(t, []string{"module2", "module1"}, mm.OrderInitGenesis)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderInitGenesis)
mm.SetOrderInitGenesis("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderInitGenesis)
require.Equal(t, []string{"module1", "module2"}, mm.OrderExportGenesis)
mm.SetOrderExportGenesis("module2", "module1")
require.Equal(t, []string{"module2", "module1"}, mm.OrderExportGenesis)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderExportGenesis)
mm.SetOrderExportGenesis("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderExportGenesis)
require.Equal(t, []string{"module1", "module2"}, mm.OrderBeginBlockers)
mm.SetOrderBeginBlockers("module2", "module1")
require.Equal(t, []string{"module2", "module1"}, mm.OrderBeginBlockers)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderBeginBlockers)
mm.SetOrderBeginBlockers("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderBeginBlockers)
require.Equal(t, []string{"module1", "module2"}, mm.OrderEndBlockers)
mm.SetOrderEndBlockers("module2", "module1")
require.Equal(t, []string{"module2", "module1"}, mm.OrderEndBlockers)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderEndBlockers)
mm.SetOrderEndBlockers("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderEndBlockers)
}
func TestManager_RegisterInvariants(t *testing.T) {
@ -109,11 +134,13 @@ func TestManager_RegisterInvariants(t *testing.T) {
mockAppModule1 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule2 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
// TODO: This is not working for Core API modules yet
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("mockAppModule3", mockAppModule3))
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))
// test RegisterInvariants
mockInvariantRegistry := mock.NewMockInvariantRegistry(mockCtrl)
@ -128,11 +155,13 @@ func TestManager_RegisterQueryServices(t *testing.T) {
mockAppModule1 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule2 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
// TODO: This is not working for Core API modules yet
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("mockAppModule3", mockAppModule3))
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))
msgRouter := mock.NewMockServer(mockCtrl)
queryRouter := mock.NewMockServer(mockCtrl)
@ -151,11 +180,12 @@ func TestManager_InitGenesis(t *testing.T) {
mockAppModule1 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule2 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("module3", mockAppModule3))
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))
ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
interfaceRegistry := types.NewInterfaceRegistry()
@ -170,10 +200,19 @@ func TestManager_InitGenesis(t *testing.T) {
genesisData = map[string]json.RawMessage{
"module1": json.RawMessage(`{"key": "value"}`),
"module2": json.RawMessage(`{"key": "value"}`),
"module3": json.RawMessage(`{"key": "value"}`),
}
// 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) })
// happy path
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{})
mockAppModule3.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Any()).Times(1).Return(nil)
mm.InitGenesis(ctx, cdc, genesisData)
}
func TestManager_ExportGenesis(t *testing.T) {
@ -182,11 +221,12 @@ func TestManager_ExportGenesis(t *testing.T) {
mockAppModule1 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule2 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockCoreAppModule := MockCoreAppModule{}
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("mockCoreAppModule", mockCoreAppModule))
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))
ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
interfaceRegistry := types.NewInterfaceRegistry()
@ -197,7 +237,11 @@ func TestManager_ExportGenesis(t *testing.T) {
want := map[string]json.RawMessage{
"module1": json.RawMessage(`{"key1": "value1"}`),
"module2": json.RawMessage(`{"key2": "value2"}`),
"mockCoreAppModule": json.RawMessage(`{
"someField": "someKey"
}`),
}
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"}))
@ -251,3 +295,151 @@ func TestManager_EndBlock(t *testing.T) {
mockAppModule2.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}})
require.Panics(t, func() { mm.EndBlock(sdk.Context{}, req) })
}
// Core API exclusive tests
func TestCoreAPIManager(t *testing.T) {
mockCtrl := gomock.NewController(t)
module1 := mock.NewMockCoreAppModule(mockCtrl)
module2 := MockCoreAppModule{}
mm := module.NewManagerFromMap(map[string]appmodule.AppModule{
"module1": module1,
"module2": module2,
})
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, module1, mm.Modules["module1"])
require.Equal(t, module2, mm.Modules["module2"])
}
func TestCoreAPIManager_InitGenesis(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockAppModule1 := mock.NewMockCoreAppModule(mockCtrl)
mm := module.NewManagerFromMap(map[string]appmodule.AppModule{"module1": mockAppModule1})
require.NotNil(t, mm)
require.Equal(t, 1, len(mm.Modules))
ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
genesisData := map[string]json.RawMessage{"module1": json.RawMessage(`{"key": "value"}`)}
// this should panic since the validator set is empty even after init genesis
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Any()).Times(1).Return(nil)
require.Panics(t, func() { mm.InitGenesis(ctx, cdc, genesisData) })
// TODO: add happy path test. We are not returning any validator updates
}
func TestCoreAPIManager_ExportGenesis(t *testing.T) {
mockAppModule1 := MockCoreAppModule{}
mockAppModule2 := MockCoreAppModule{}
mm := module.NewManagerFromMap(map[string]appmodule.AppModule{
"module1": mockAppModule1,
"module2": mockAppModule2,
})
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
want := map[string]json.RawMessage{
"module1": json.RawMessage(`{
"someField": "someKey"
}`),
"module2": json.RawMessage(`{
"someField": "someKey"
}`),
}
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"}))
require.Panics(t, func() {
mm.ExportGenesisForModules(ctx, cdc, []string{"module1", "modulefoo"})
})
}
func TestCoreAPIManagerOrderSetters(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockAppModule1 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule2 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mm := module.NewManagerFromMap(
map[string]appmodule.AppModule{
"module1": mockAppModule1,
"module2": mockAppModule2,
"module3": mockAppModule3,
})
require.NotNil(t, mm)
require.Equal(t, 3, len(mm.Modules))
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderInitGenesis)
mm.SetOrderInitGenesis("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderInitGenesis)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderExportGenesis)
mm.SetOrderExportGenesis("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderExportGenesis)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderBeginBlockers)
mm.SetOrderBeginBlockers("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderBeginBlockers)
require.Equal(t, []string{"module1", "module2", "module3"}, mm.OrderEndBlockers)
mm.SetOrderEndBlockers("module2", "module1", "module3")
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderEndBlockers)
}
// MockCoreAppModule allows us to test functions like DefaultGenesis
type MockCoreAppModule struct{}
func (MockCoreAppModule) IsOnePerModuleType() {}
func (MockCoreAppModule) IsAppModule() {}
func (MockCoreAppModule) DefaultGenesis(target appmodule.GenesisTarget) error {
someFieldWriter, err := target("someField")
if err != nil {
return err
}
someFieldWriter.Write([]byte(`"someKey"`))
return someFieldWriter.Close()
}
func (MockCoreAppModule) ValidateGenesis(src appmodule.GenesisSource) error {
rdr, err := src("someField")
if err != nil {
return err
}
data, err := io.ReadAll(rdr)
if err != nil {
return err
}
// this check will always fail, but it's just an example
if string(data) != `"dummy validation"` {
return errFoo
}
return nil
}
func (MockCoreAppModule) InitGenesis(context.Context, appmodule.GenesisSource) error { return nil }
func (MockCoreAppModule) ExportGenesis(ctx context.Context, target appmodule.GenesisTarget) error {
wrt, err := target("someField")
if err != nil {
return err
}
wrt.Write([]byte(`"someKey"`))
return wrt.Close()
}
var (
_ appmodule.AppModule = MockCoreAppModule{}
_ appmodule.HasGenesis = MockCoreAppModule{}
)