Skip to content

Commit 1d56ec2

Browse files
committed
fix(x/epochs): di wiring
1 parent 9c4af5a commit 1d56ec2

File tree

8 files changed

+295
-11
lines changed

8 files changed

+295
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
6969

7070
### Bug Fixes
7171

72+
* (x/epochs) [#25425](https://github.com/cosmos/cosmos-sdk/pull/25425) Fix `InvokeSetHooks` being called with a nil keeper and `AppModule` containing a copy instead of a pointer (hooks set post creating the `AppModule` like with depinject didn't apply because it's a different instance).
7273
* (client, client/rpc, x/auth/tx) [#24551](https://github.com/cosmos/cosmos-sdk/pull/24551) Handle cancellation properly when supplying context to client methods.
7374
* (x/authz) [#24638](https://github.com/cosmos/cosmos-sdk/pull/24638) Fixed a minor bug where the grant key was cast as a string and dumped directly into the error message leading to an error string possibly containing invalid UTF-8.
7475
* (client, client/rpc, x/auth/tx) [#24551](https://github.com/cosmos/cosmos-sdk/pull/24551) Handle cancellation properly when supplying context to client methods.

simapp/app.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ type SimApp struct {
150150
// supplementary keepers
151151
FeeGrantKeeper feegrantkeeper.Keeper
152152
AuthzKeeper authzkeeper.Keeper
153-
EpochsKeeper epochskeeper.Keeper
153+
EpochsKeeper *epochskeeper.Keeper
154154
ProtocolPoolKeeper protocolpoolkeeper.Keeper
155155

156156
// the module manager
@@ -448,11 +448,13 @@ func NewSimApp(
448448
// If evidence needs to be handled for the app, set routes in router here and seal
449449
app.EvidenceKeeper = *evidenceKeeper
450450

451-
app.EpochsKeeper = epochskeeper.NewKeeper(
451+
epochsKeeper := epochskeeper.NewKeeper(
452452
runtime.NewKVStoreService(keys[epochstypes.StoreKey]),
453453
appCodec,
454454
)
455455

456+
app.EpochsKeeper = &epochsKeeper
457+
456458
app.EpochsKeeper.SetHooks(
457459
epochstypes.NewMultiEpochHooks(
458460
// insert epoch hooks receivers here

simapp/app_di.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ type SimApp struct {
7676
// supplementary keepers
7777
FeeGrantKeeper feegrantkeeper.Keeper
7878
AuthzKeeper authzkeeper.Keeper
79-
EpochsKeeper epochskeeper.Keeper
79+
EpochsKeeper *epochskeeper.Keeper
8080
ProtocolPoolKeeper protocolpoolkeeper.Keeper
8181

8282
// simulation manager

x/epochs/README.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,176 @@ func (k MyModuleKeeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, e
9595
}
9696
```
9797

98+
### Wiring Hooks
99+
100+
**Manual Wiring:**
101+
102+
Import the following:
103+
104+
```go
105+
import (
106+
// ...
107+
"github.com/cosmos/cosmos-sdk/x/epochs"
108+
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
109+
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
110+
)
111+
```
112+
113+
Add the epochs keeper to your application struct:
114+
115+
```go
116+
EpochsKeeper *epochskeeper.Keeper
117+
```
118+
119+
Add the store key:
120+
121+
```go
122+
keys := storetypes.NewKVStoreKeys(
123+
// ...
124+
epochstypes.StoreKey,
125+
)
126+
```
127+
128+
Instantiate the keeper:
129+
130+
```go
131+
epochsKeeper := epochskeeper.NewKeeper(
132+
runtime.NewKVStoreService(keys[epochstypes.StoreKey]),
133+
appCodec,
134+
)
135+
136+
app.EpochsKeeper = &epochsKeeper
137+
```
138+
139+
Set up hooks for the epochs keeper:
140+
141+
```go
142+
app.EpochsKeeper.SetHooks(
143+
epochstypes.NewMultiEpochHooks(
144+
// insert epoch hooks receivers here
145+
app.SomeOtherModule
146+
),
147+
)
148+
```
149+
150+
Add the epochs module to the module manager:
151+
152+
```go
153+
app.ModuleManager = module.NewManager(
154+
// ...
155+
epochs.NewAppModule(appCodec, app.EpochsKeeper),
156+
)
157+
```
158+
159+
Add entries for SetOrderBeginBlockers and SetOrderInitGenesis:
160+
161+
```go
162+
app.ModuleManager.SetOrderBeginBlockers(
163+
// ...
164+
epochstypes.ModuleName,
165+
)
166+
```
167+
168+
```go
169+
app.ModuleManager.SetOrderInitGenesis(
170+
// ...
171+
epochstypes.ModuleName,
172+
)
173+
```
174+
175+
**DI Wiring:**
176+
177+
First, set up the keeper for the application.
178+
179+
Import the epochs keeper:
180+
181+
```go
182+
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
183+
```
184+
185+
Add the keeper to your application struct:
186+
187+
```go
188+
EpochsKeeper *epochskeeper.Keeper
189+
```
190+
191+
Add the keeper to the depinject system:
192+
193+
```go
194+
depinject.Inject(
195+
appConfig,
196+
&appBuilder,
197+
&app.appCodec,
198+
&app.legacyAmino,
199+
&app.txConfig,
200+
&app.interfaceRegistry,
201+
// ... other modules
202+
&app.EpochsKeeper, // NEW MODULE!
203+
)
204+
```
205+
206+
Next, set up configuration for the module.
207+
208+
Import the following:
209+
210+
```go
211+
import (
212+
epochsmodulev1 "cosmossdk.io/api/cosmos/epochs/module/v1"
213+
214+
_ "github.com/cosmos/cosmos-sdk/x/epochs" // import for side-effects
215+
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
216+
)
217+
```
218+
219+
Add an entry for BeginBlockers and InitGenesis:
220+
221+
```go
222+
BeginBlockers: []string{
223+
// ...
224+
epochstypes.ModuleName,
225+
},
226+
```
227+
228+
```go
229+
InitGenesis: []string{
230+
// ...
231+
epochstypes.ModuleName,
232+
},
233+
```
234+
235+
Add an entry for epochs in the ModuleConfig:
236+
237+
```go
238+
{
239+
Name: epochstypes.ModuleName,
240+
Config: appconfig.WrapAny(&epochsmodulev1.Module{}),
241+
},
242+
```
243+
244+
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:
245+
246+
```go
247+
type TestInputs struct {
248+
depinject.In
249+
}
250+
251+
type TestOutputs struct {
252+
depinject.Out
253+
254+
Hooks types.EpochHooksWrapper
255+
}
256+
257+
func DummyProvider(in TestInputs) TestOutputs {
258+
return TestOutputs{
259+
Hooks: types.EpochHooksWrapper{
260+
EpochHooks: testEpochHooks{},
261+
},
262+
}
263+
}
264+
```
265+
266+
for an example see [`depinject_test.go`](https://github.com/cosmos/cosmos-sdk/tree/main/x/epochs/depinject_test.go)
267+
98268
### Panic isolation
99269

100270
If a given epoch hook panics, its state update is reverted, but we keep

x/epochs/depinject.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ type ModuleInputs struct {
3939
type ModuleOutputs struct {
4040
depinject.Out
4141

42-
EpochKeeper keeper.Keeper
42+
EpochKeeper *keeper.Keeper
4343
Module appmodule.AppModule
4444
}
4545

4646
func ProvideModule(in ModuleInputs) ModuleOutputs {
4747
k := keeper.NewKeeper(in.StoreService, in.Cdc)
48-
m := NewAppModule(k)
49-
return ModuleOutputs{EpochKeeper: k, Module: m}
48+
m := NewAppModule(&k)
49+
return ModuleOutputs{EpochKeeper: m.keeper, Module: m}
5050
}
5151

5252
func InvokeSetHooks(keeper *keeper.Keeper, hooks map[string]types.EpochHooksWrapper) error {

x/epochs/depinject_test.go

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,41 @@ import (
66

77
"github.com/stretchr/testify/require"
88

9+
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
10+
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
11+
modulev1 "cosmossdk.io/api/cosmos/epochs/module/v1"
12+
"cosmossdk.io/core/appmodule"
13+
"cosmossdk.io/core/store"
14+
"cosmossdk.io/depinject"
15+
"cosmossdk.io/depinject/appconfig"
916
storetypes "cosmossdk.io/store/types"
1017

18+
"github.com/cosmos/cosmos-sdk/codec"
1119
"github.com/cosmos/cosmos-sdk/runtime"
1220
"github.com/cosmos/cosmos-sdk/types/module/testutil"
21+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
1322
"github.com/cosmos/cosmos-sdk/x/epochs"
1423
"github.com/cosmos/cosmos-sdk/x/epochs/keeper"
1524
"github.com/cosmos/cosmos-sdk/x/epochs/types"
1625
)
1726

27+
var _ types.EpochHooks = testEpochHooks{}
28+
1829
type testEpochHooks struct{}
1930

20-
func (h testEpochHooks) AfterEpochEnd(ctx context.Context, epochIdentifier string, epochNumber int64) error {
31+
func (h testEpochHooks) AfterEpochEnd(
32+
ctx context.Context,
33+
epochIdentifier string,
34+
epochNumber int64,
35+
) error {
2136
return nil
2237
}
2338

24-
func (h testEpochHooks) BeforeEpochStart(ctx context.Context, epochIdentifier string, epochNumber int64) error {
39+
func (h testEpochHooks) BeforeEpochStart(
40+
ctx context.Context,
41+
epochIdentifier string,
42+
epochNumber int64,
43+
) error {
2544
return nil
2645
}
2746

@@ -58,3 +77,88 @@ func TestInvokeSetHooks(t *testing.T) {
5877
require.Equal(t, hook1, multiHooks[0])
5978
require.Equal(t, hook2, multiHooks[1])
6079
}
80+
81+
type TestInputs struct {
82+
depinject.In
83+
}
84+
85+
type TestOutputs struct {
86+
depinject.Out
87+
88+
Hooks types.EpochHooksWrapper
89+
}
90+
91+
func DummyProvider(in TestInputs) TestOutputs {
92+
return TestOutputs{
93+
Hooks: types.EpochHooksWrapper{
94+
EpochHooks: testEpochHooks{},
95+
},
96+
}
97+
}
98+
99+
func ProvideDeps(depinject.In) struct {
100+
depinject.Out
101+
Cdc codec.Codec
102+
StoreService store.KVStoreService
103+
} {
104+
encCfg := testutil.MakeTestEncodingConfig()
105+
106+
key := storetypes.NewKVStoreKey(types.StoreKey)
107+
return struct {
108+
depinject.Out
109+
Cdc codec.Codec
110+
StoreService store.KVStoreService
111+
}{
112+
Cdc: encCfg.Codec,
113+
StoreService: runtime.NewKVStoreService(key),
114+
}
115+
}
116+
117+
func TestDepinject(t *testing.T) {
118+
/// we just need any module's proto to register the provider here, no specific reason to use bank
119+
appconfig.RegisterModule(&bankmodulev1.Module{}, appconfig.Provide(DummyProvider))
120+
var appModules map[string]appmodule.AppModule
121+
keeper := new(keeper.Keeper)
122+
require.NoError(t,
123+
depinject.Inject(
124+
depinject.Configs(
125+
appconfig.Compose(
126+
&appv1alpha1.Config{
127+
Modules: []*appv1alpha1.ModuleConfig{
128+
{
129+
Name: banktypes.ModuleName,
130+
Config: appconfig.WrapAny(&bankmodulev1.Module{}),
131+
},
132+
{
133+
Name: types.ModuleName,
134+
Config: appconfig.WrapAny(&modulev1.Module{}),
135+
},
136+
},
137+
},
138+
),
139+
depinject.Provide(ProvideDeps),
140+
),
141+
&appModules,
142+
&keeper,
143+
),
144+
)
145+
146+
require.NotNil(t, keeper, "expected keeper to not be nil after depinject")
147+
multihooks, ok := keeper.Hooks().(types.MultiEpochHooks)
148+
require.True(t, ok, "expected keeper to have MultiEpochHooks after depinject")
149+
require.Len(t, multihooks, 1, "expected MultiEpochHooks to have 1 element after depinject")
150+
require.Equal(
151+
t,
152+
types.EpochHooksWrapper{EpochHooks: testEpochHooks{}},
153+
multihooks[0],
154+
"expected the only hook in MultiEpochHooks to be the test hook",
155+
)
156+
module, ok := appModules[types.ModuleName].(epochs.AppModule)
157+
require.True(t, ok, "expected depinject to fill map with the epochs AppModule")
158+
require.Equal(
159+
t,
160+
types.MultiEpochHooks{types.EpochHooksWrapper{EpochHooks: testEpochHooks{}}},
161+
module.Keeper().Hooks(),
162+
)
163+
require.Same(t, keeper, module.Keeper()) // pointers pointing to the same instance
164+
}

x/epochs/export_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package epochs
2+
3+
import "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
4+
5+
func (am AppModule) Keeper() *keeper.Keeper {
6+
return am.keeper
7+
}

x/epochs/module.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ const ConsensusVersion = 1
3333

3434
// AppModule implements the AppModule interface for the epochs module.
3535
type AppModule struct {
36-
keeper keeper.Keeper
36+
keeper *keeper.Keeper
3737
}
3838

3939
// NewAppModule creates a new AppModule object.
40-
func NewAppModule(keeper keeper.Keeper) AppModule {
40+
func NewAppModule(keeper *keeper.Keeper) AppModule {
4141
return AppModule{
4242
keeper: keeper,
4343
}
@@ -66,7 +66,7 @@ func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwrunt
6666

6767
// RegisterServices registers module services.
6868
func (am AppModule) RegisterServices(registrar grpc.ServiceRegistrar) error {
69-
types.RegisterQueryServer(registrar, keeper.NewQuerier(am.keeper))
69+
types.RegisterQueryServer(registrar, keeper.NewQuerier(*am.keeper))
7070
return nil
7171
}
7272

0 commit comments

Comments
 (0)