Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions simapp/app_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type SimApp struct {
// supplementary keepers
FeeGrantKeeper feegrantkeeper.Keeper
AuthzKeeper authzkeeper.Keeper
EpochsKeeper epochskeeper.Keeper
EpochsKeeper *epochskeeper.Keeper
ProtocolPoolKeeper protocolpoolkeeper.Keeper

// simulation manager
Expand Down Expand Up @@ -232,7 +232,12 @@ func NewSimApp(
// NOTE: this is not required apps that don't use the simulator for fuzz testing
// transactions
overrideModules := map[string]module.AppModuleSimulation{
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil),
authtypes.ModuleName: auth.NewAppModule(
app.appCodec,
app.AccountKeeper,
authsims.RandomGenesisAccounts,
nil,
),
}
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules)

Expand Down
168 changes: 168 additions & 0 deletions x/epochs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,174 @@ func (k MyModuleKeeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, e
}
```

### Wiring Hooks

**Manual Wiring:**

Import the following:

```go
import (
// ...
"github.com/cosmos/cosmos-sdk/x/epochs"
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
)
```

Add the epochs keeper to your application struct:

```go
EpochsKeeper epochskeeper.Keeper
```

Add the store key:

```go
keys := storetypes.NewKVStoreKeys(
// ...
epochstypes.StoreKey,
)
```

Instantiate the keeper:

```go
app.EpochsKeeper = epochskeeper.NewKeeper(
runtime.NewKVStoreService(keys[epochstypes.StoreKey]),
appCodec,
)
```

Set up hooks for the epochs keeper:

```go
app.EpochsKeeper.SetHooks(
epochstypes.NewMultiEpochHooks(
// insert epoch hooks receivers here
app.SomeOtherModule
),
)
```

Add the epochs module to the module manager:

```go
app.ModuleManager = module.NewManager(
// ...
epochs.NewAppModule(appCodec, app.EpochsKeeper),
)
```

Add entries for SetOrderBeginBlockers and SetOrderInitGenesis:

```go
app.ModuleManager.SetOrderBeginBlockers(
// ...
epochstypes.ModuleName,
)
```

```go
app.ModuleManager.SetOrderInitGenesis(
// ...
epochstypes.ModuleName,
)
```

**DI Wiring:**

First, set up the keeper for the application.

Import the epochs keeper:

```go
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
```

Add the keeper to your application struct:

```go
EpochsKeeper *epochskeeper.Keeper // must be a pointer for DI
```

Add the keeper to the depinject system:

```go
depinject.Inject(
appConfig,
&appBuilder,
&app.appCodec,
&app.legacyAmino,
&app.txConfig,
&app.interfaceRegistry,
// ... other modules
&app.EpochsKeeper, // NEW MODULE!
)
```

Next, set up configuration for the module.

Import the following:

```go
import (
epochsmodulev1 "cosmossdk.io/api/cosmos/epochs/module/v1"

_ "github.com/cosmos/cosmos-sdk/x/epochs" // import for side-effects
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
)
```

Add an entry for BeginBlockers and InitGenesis:

```go
BeginBlockers: []string{
// ...
epochstypes.ModuleName,
},
```

```go
InitGenesis: []string{
// ...
epochstypes.ModuleName,
},
```

Add an entry for epochs in the ModuleConfig:

```go
{
Name: epochstypes.ModuleName,
Config: appconfig.WrapAny(&epochsmodulev1.Module{}),
},
```

depinject can automatically add your hooks to the epochs `Keeper`. For it do so, specify an output of your module with the type `epochtypes.EpochHooksWrapper`, ie:

```go
type TestInputs struct {
depinject.In
}

type TestOutputs struct {
depinject.Out

Hooks types.EpochHooksWrapper
}

func DummyProvider(in TestInputs) TestOutputs {
return TestOutputs{
Hooks: types.EpochHooksWrapper{
EpochHooks: testEpochHooks{},
},
}
}
```

for an example see [`depinject_test.go`](https://github.com/cosmos/cosmos-sdk/tree/main/x/epochs/depinject_test.go)

### Panic isolation

If a given epoch hook panics, its state update is reverted, but we keep
Expand Down
6 changes: 3 additions & 3 deletions x/epochs/depinject.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ type ModuleInputs struct {
type ModuleOutputs struct {
depinject.Out

EpochKeeper keeper.Keeper
EpochKeeper *keeper.Keeper
Module appmodule.AppModule
}

func ProvideModule(in ModuleInputs) ModuleOutputs {
k := keeper.NewKeeper(in.StoreService, in.Cdc)
m := NewAppModule(k)
return ModuleOutputs{EpochKeeper: k, Module: m}
m := NewAppModule(&k)
return ModuleOutputs{EpochKeeper: m.keeper, Module: m}
}

func InvokeSetHooks(keeper *keeper.Keeper, hooks map[string]types.EpochHooksWrapper) error {
Expand Down
108 changes: 106 additions & 2 deletions x/epochs/depinject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,41 @@ import (

"github.com/stretchr/testify/require"

appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/store"
"cosmossdk.io/depinject"
"cosmossdk.io/depinject/appconfig"
storetypes "cosmossdk.io/store/types"

modulev1 "cosmossdk.io/api/cosmos/epochs/module/v1"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/types/module/testutil"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/epochs"
"github.com/cosmos/cosmos-sdk/x/epochs/keeper"
"github.com/cosmos/cosmos-sdk/x/epochs/types"
)

var _ types.EpochHooks = testEpochHooks{}

type testEpochHooks struct{}

func (h testEpochHooks) AfterEpochEnd(ctx context.Context, epochIdentifier string, epochNumber int64) error {
func (h testEpochHooks) AfterEpochEnd(
ctx context.Context,
epochIdentifier string,
epochNumber int64,
) error {
return nil
}

func (h testEpochHooks) BeforeEpochStart(ctx context.Context, epochIdentifier string, epochNumber int64) error {
func (h testEpochHooks) BeforeEpochStart(
ctx context.Context,
epochIdentifier string,
epochNumber int64,
) error {
return nil
}

Expand Down Expand Up @@ -58,3 +77,88 @@ func TestInvokeSetHooks(t *testing.T) {
require.Equal(t, hook1, multiHooks[0])
require.Equal(t, hook2, multiHooks[1])
}

type TestInputs struct {
depinject.In
}

type TestOutputs struct {
depinject.Out

Hooks types.EpochHooksWrapper
}

func DummyProvider(in TestInputs) TestOutputs {
return TestOutputs{
Hooks: types.EpochHooksWrapper{
EpochHooks: testEpochHooks{},
},
}
}

func ProvideDeps(depinject.In) struct {
depinject.Out
Cdc codec.Codec
StoreService store.KVStoreService
} {
encCfg := testutil.MakeTestEncodingConfig()

key := storetypes.NewKVStoreKey(types.StoreKey)
return struct {
depinject.Out
Cdc codec.Codec
StoreService store.KVStoreService
}{
Cdc: encCfg.Codec,
StoreService: runtime.NewKVStoreService(key),
}
}

func TestDepinject(t *testing.T) {
/// we just need any module's proto to register the provider here, no specific reason to use bank
appconfig.RegisterModule(&bankmodulev1.Module{}, appconfig.Provide(DummyProvider))
var appModules map[string]appmodule.AppModule
keeper := new(keeper.Keeper)
require.NoError(t,
depinject.Inject(
depinject.Configs(
appconfig.Compose(
&appv1alpha1.Config{
Modules: []*appv1alpha1.ModuleConfig{
{
Name: banktypes.ModuleName,
Config: appconfig.WrapAny(&bankmodulev1.Module{}),
},
{
Name: types.ModuleName,
Config: appconfig.WrapAny(&modulev1.Module{}),
},
},
},
),
depinject.Provide(ProvideDeps),
),
&appModules,
&keeper,
),
)

require.NotNil(t, keeper, "expected keeper to not be nil after depinject")
multihooks, ok := keeper.Hooks().(types.MultiEpochHooks)
require.True(t, ok, "expected keeper to have MultiEpochHooks after depinject")
require.Len(t, multihooks, 1, "expected MultiEpochHooks to have 1 element after depinject")
require.Equal(
t,
types.EpochHooksWrapper{EpochHooks: testEpochHooks{}},
multihooks[0],
"expected the only hook in MultiEpochHooks to be the test hook",
)
module, ok := appModules[types.ModuleName].(epochs.AppModule)
require.True(t, ok, "expected depinject to fill map with the epochs AppModule")
require.Equal(
t,
types.MultiEpochHooks{types.EpochHooksWrapper{EpochHooks: testEpochHooks{}}},
module.Keeper().Hooks(),
)
require.Same(t, keeper, module.Keeper()) // pointers pointing to the same instance
}
7 changes: 7 additions & 0 deletions x/epochs/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package epochs

import "github.com/cosmos/cosmos-sdk/x/epochs/keeper"

func (am AppModule) Keeper() *keeper.Keeper {
return am.keeper
}
12 changes: 8 additions & 4 deletions x/epochs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ const ConsensusVersion = 1

// AppModule implements the AppModule interface for the epochs module.
type AppModule struct {
keeper keeper.Keeper
keeper *keeper.Keeper
}

// NewAppModule creates a new AppModule object.
func NewAppModule(keeper keeper.Keeper) AppModule {
func NewAppModule(keeper *keeper.Keeper) AppModule {
return AppModule{
keeper: keeper,
}
Expand Down Expand Up @@ -66,7 +66,7 @@ func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwrunt

// RegisterServices registers module services.
func (am AppModule) RegisterServices(registrar grpc.ServiceRegistrar) error {
types.RegisterQueryServer(registrar, keeper.NewQuerier(am.keeper))
types.RegisterQueryServer(registrar, keeper.NewQuerier(*am.keeper))
return nil
}

Expand All @@ -80,7 +80,11 @@ func (am AppModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
}

// ValidateGenesis performs genesis state validation for the epochs module.
func (am AppModule) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
func (am AppModule) ValidateGenesis(
cdc codec.JSONCodec,
_ client.TxEncodingConfig,
bz json.RawMessage,
) error {
var gs types.GenesisState
if err := cdc.UnmarshalJSON(bz, &gs); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
Expand Down