Skip to content

Commit 67c218b

Browse files
authored
Merge branch 'main' into julien/autocli-protocolpool
2 parents 45e25a7 + 112e228 commit 67c218b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+1086
-1093
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
6161
* (sims) [#21952](https://github.com/cosmos/cosmos-sdk/pull/21952) Use liveness matrix for validator sign status in sims
6262
* (sims) [#21906](https://github.com/cosmos/cosmos-sdk/pull/21906) Skip sims test when running dry on validators
6363
* (cli) [#21919](https://github.com/cosmos/cosmos-sdk/pull/21919) Query address-by-acc-num by account_id instead of id.
64+
* (baseapp) [#21003](https://github.com/cosmos/cosmos-sdk/pull/21003) Align block header when query with latest height.
6465

6566
### API Breaking Changes
6667

baseapp/abci.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func (app *BaseApp) Info(_ *abci.InfoRequest) (*abci.InfoResponse, error) {
143143
lastCommitID := app.cms.LastCommitID()
144144
appVersion := InitialAppVersion
145145
if lastCommitID.Version > 0 {
146-
ctx, err := app.CreateQueryContext(lastCommitID.Version, false)
146+
ctx, err := app.CreateQueryContextWithCheckHeader(lastCommitID.Version, false, false)
147147
if err != nil {
148148
return nil, fmt.Errorf("failed creating query context: %w", err)
149149
}
@@ -1222,6 +1222,12 @@ func checkNegativeHeight(height int64) error {
12221222
// CreateQueryContext creates a new sdk.Context for a query, taking as args
12231223
// the block height and whether the query needs a proof or not.
12241224
func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, error) {
1225+
return app.CreateQueryContextWithCheckHeader(height, prove, true)
1226+
}
1227+
1228+
// CreateQueryContextWithCheckHeader creates a new sdk.Context for a query, taking as args
1229+
// the block height, whether the query needs a proof or not, and whether to check the header or not.
1230+
func (app *BaseApp) CreateQueryContextWithCheckHeader(height int64, prove, checkHeader bool) (sdk.Context, error) {
12251231
if err := checkNegativeHeight(height); err != nil {
12261232
return sdk.Context{}, err
12271233
}
@@ -1245,19 +1251,46 @@ func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, e
12451251
)
12461252
}
12471253

1248-
// when a client did not provide a query height, manually inject the latest
1249-
if height == 0 {
1250-
height = lastBlockHeight
1251-
}
1252-
1253-
if height <= 1 && prove {
1254+
if height > 0 && height <= 1 && prove {
12541255
return sdk.Context{},
12551256
errorsmod.Wrap(
12561257
sdkerrors.ErrInvalidRequest,
12571258
"cannot query with proof when height <= 1; please provide a valid height",
12581259
)
12591260
}
12601261

1262+
var header *cmtproto.Header
1263+
isLatest := height == 0
1264+
for _, state := range []*state{
1265+
app.checkState,
1266+
app.finalizeBlockState,
1267+
} {
1268+
if state != nil {
1269+
// branch the commit multi-store for safety
1270+
h := state.Context().BlockHeader()
1271+
if isLatest {
1272+
lastBlockHeight = qms.LatestVersion()
1273+
}
1274+
if !checkHeader || !isLatest || isLatest && h.Height == lastBlockHeight {
1275+
header = &h
1276+
break
1277+
}
1278+
}
1279+
}
1280+
1281+
if header == nil {
1282+
return sdk.Context{},
1283+
errorsmod.Wrapf(
1284+
sdkerrors.ErrInvalidHeight,
1285+
"header height in all state context is not latest height (%d)", lastBlockHeight,
1286+
)
1287+
}
1288+
1289+
// when a client did not provide a query height, manually inject the latest
1290+
if isLatest {
1291+
height = lastBlockHeight
1292+
}
1293+
12611294
cacheMS, err := qms.CacheMultiStoreWithVersion(height)
12621295
if err != nil {
12631296
return sdk.Context{},
@@ -1275,10 +1308,10 @@ func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, e
12751308
ChainID: app.chainID,
12761309
Height: height,
12771310
}).
1278-
WithBlockHeader(app.checkState.Context().BlockHeader()).
1311+
WithBlockHeader(*header).
12791312
WithBlockHeight(height)
12801313

1281-
if height != lastBlockHeight {
1314+
if !isLatest {
12821315
rms, ok := app.cms.(*rootmulti.Store)
12831316
if ok {
12841317
cInfo, err := rms.GetCommitInfo(height)
@@ -1287,7 +1320,6 @@ func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, e
12871320
}
12881321
}
12891322
}
1290-
12911323
return ctx, nil
12921324
}
12931325

baseapp/baseapp_test.go

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -696,26 +696,26 @@ func TestBaseAppPostHandler(t *testing.T) {
696696
require.NotContains(t, suite.logBuffer.String(), "panic recovered in runTx")
697697
}
698698

699+
type mockABCIListener struct {
700+
ListenCommitFn func(context.Context, abci.CommitResponse, []*storetypes.StoreKVPair) error
701+
}
702+
703+
func (m mockABCIListener) ListenFinalizeBlock(_ context.Context, _ abci.FinalizeBlockRequest, _ abci.FinalizeBlockResponse) error {
704+
return nil
705+
}
706+
707+
func (m *mockABCIListener) ListenCommit(ctx context.Context, commit abci.CommitResponse, pairs []*storetypes.StoreKVPair) error {
708+
return m.ListenCommitFn(ctx, commit, pairs)
709+
}
710+
699711
// Test and ensure that invalid block heights always cause errors.
700712
// See issues:
701713
// - https://github.com/cosmos/cosmos-sdk/issues/11220
702714
// - https://github.com/cosmos/cosmos-sdk/issues/7662
703715
func TestABCI_CreateQueryContext(t *testing.T) {
704716
t.Parallel()
717+
app := getQueryBaseapp(t)
705718

706-
db := coretesting.NewMemDB()
707-
name := t.Name()
708-
app := baseapp.NewBaseApp(name, log.NewTestLogger(t), db, nil)
709-
710-
_, err := app.FinalizeBlock(&abci.FinalizeBlockRequest{Height: 1})
711-
require.NoError(t, err)
712-
_, err = app.Commit()
713-
require.NoError(t, err)
714-
715-
_, err = app.FinalizeBlock(&abci.FinalizeBlockRequest{Height: 2})
716-
require.NoError(t, err)
717-
_, err = app.Commit()
718-
require.NoError(t, err)
719719
testCases := []struct {
720720
name string
721721
height int64
@@ -724,7 +724,7 @@ func TestABCI_CreateQueryContext(t *testing.T) {
724724
expErr bool
725725
}{
726726
{"valid height", 2, 2, true, false},
727-
{"valid height with different initial height", 2, 1, true, false},
727+
{"valid height with different initial height", 2, 1, true, true},
728728
{"future height", 10, 10, true, true},
729729
{"negative height, prove=true", -1, -1, true, true},
730730
{"negative height, prove=false", -1, -1, false, true},
@@ -738,7 +738,11 @@ func TestABCI_CreateQueryContext(t *testing.T) {
738738
})
739739
require.NoError(t, err)
740740
}
741-
ctx, err := app.CreateQueryContext(tc.height, tc.prove)
741+
height := tc.height
742+
if tc.height > tc.headerHeight {
743+
height = 0
744+
}
745+
ctx, err := app.CreateQueryContext(height, tc.prove)
742746
if tc.expErr {
743747
require.Error(t, err)
744748
} else {
@@ -749,6 +753,81 @@ func TestABCI_CreateQueryContext(t *testing.T) {
749753
}
750754
}
751755

756+
func TestABCI_CreateQueryContextWithCheckHeader(t *testing.T) {
757+
t.Parallel()
758+
app := getQueryBaseapp(t)
759+
var height int64 = 2
760+
var headerHeight int64 = 1
761+
762+
testCases := []struct {
763+
checkHeader bool
764+
expErr bool
765+
}{
766+
{true, true},
767+
{false, false},
768+
}
769+
770+
for _, tc := range testCases {
771+
t.Run("valid height with different initial height", func(t *testing.T) {
772+
_, err := app.InitChain(&abci.InitChainRequest{
773+
InitialHeight: headerHeight,
774+
})
775+
require.NoError(t, err)
776+
ctx, err := app.CreateQueryContextWithCheckHeader(0, true, tc.checkHeader)
777+
if tc.expErr {
778+
require.Error(t, err)
779+
} else {
780+
require.NoError(t, err)
781+
require.Equal(t, height, ctx.BlockHeight())
782+
}
783+
})
784+
}
785+
}
786+
787+
func TestABCI_CreateQueryContext_Before_Set_CheckState(t *testing.T) {
788+
t.Parallel()
789+
790+
db := coretesting.NewMemDB()
791+
name := t.Name()
792+
var height int64 = 2
793+
var headerHeight int64 = 1
794+
795+
t.Run("valid height with different initial height", func(t *testing.T) {
796+
app := baseapp.NewBaseApp(name, log.NewTestLogger(t), db, nil)
797+
798+
_, err := app.FinalizeBlock(&abci.FinalizeBlockRequest{Height: 1})
799+
require.NoError(t, err)
800+
_, err = app.Commit()
801+
require.NoError(t, err)
802+
803+
_, err = app.FinalizeBlock(&abci.FinalizeBlockRequest{Height: 2})
804+
require.NoError(t, err)
805+
806+
var queryCtx *sdk.Context
807+
var queryCtxErr error
808+
app.SetStreamingManager(storetypes.StreamingManager{
809+
ABCIListeners: []storetypes.ABCIListener{
810+
&mockABCIListener{
811+
ListenCommitFn: func(context.Context, abci.CommitResponse, []*storetypes.StoreKVPair) error {
812+
qCtx, qErr := app.CreateQueryContext(0, true)
813+
queryCtx = &qCtx
814+
queryCtxErr = qErr
815+
return nil
816+
},
817+
},
818+
},
819+
})
820+
_, err = app.Commit()
821+
require.NoError(t, err)
822+
require.NoError(t, queryCtxErr)
823+
require.Equal(t, height, queryCtx.BlockHeight())
824+
_, err = app.InitChain(&abci.InitChainRequest{
825+
InitialHeight: headerHeight,
826+
})
827+
require.NoError(t, err)
828+
})
829+
}
830+
752831
func TestSetMinGasPrices(t *testing.T) {
753832
minGasPrices := sdk.DecCoins{sdk.NewInt64DecCoin("stake", 5000)}
754833
suite := NewBaseAppSuite(t, baseapp.SetMinGasPrices(minGasPrices.String()))

client/v2/go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,14 @@ require (
153153
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
154154
go.opencensus.io v0.24.0 // indirect
155155
go.uber.org/multierr v1.11.0 // indirect
156-
golang.org/x/crypto v0.27.0 // indirect
156+
golang.org/x/crypto v0.28.0 // indirect
157157
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
158158
golang.org/x/mod v0.17.0 // indirect
159159
golang.org/x/net v0.29.0 // indirect
160160
golang.org/x/sync v0.8.0 // indirect
161-
golang.org/x/sys v0.25.0 // indirect
162-
golang.org/x/term v0.24.0 // indirect
163-
golang.org/x/text v0.18.0 // indirect
161+
golang.org/x/sys v0.26.0 // indirect
162+
golang.org/x/term v0.25.0 // indirect
163+
golang.org/x/text v0.19.0 // indirect
164164
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
165165
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
166166
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect

client/v2/go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
523523
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
524524
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
525525
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
526-
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
527-
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
526+
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
527+
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
528528
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
529529
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
530530
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
@@ -607,19 +607,19 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
607607
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
608608
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
609609
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
610-
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
611-
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
610+
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
611+
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
612612
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
613613
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
614-
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
615-
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
614+
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
615+
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
616616
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
617617
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
618618
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
619619
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
620620
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
621-
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
622-
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
621+
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
622+
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
623623
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
624624
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
625625
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

docs/architecture/adr-003-dynamic-capability-store.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ chain is running.
3434

3535
The `CapabilityKeeper` will include a persistent `KVStore`, a `MemoryStore`, and an in-memory map.
3636
The persistent `KVStore` tracks which capability is owned by which modules.
37-
The `MemoryStore` stores a forward mapping that map from module name, capability tuples to capability names and
38-
a reverse mapping that map from module name, capability name to the capability index.
37+
The `MemoryStore` stores a forward mapping that maps from module name, capability tuples to capability names and
38+
a reverse mapping that maps from module name, capability name to the capability index.
3939
Since we cannot marshal the capability into a `KVStore` and unmarshal without changing the memory location of the capability,
4040
the reverse mapping in the KVStore will simply map to an index. This index can then be used as a key in the ephemeral
4141
go-map to retrieve the capability at the original memory location.
@@ -277,7 +277,7 @@ ck.InitialiseAndSeal(initialContext)
277277

278278
#### Creating, passing, claiming and using capabilities
279279

280-
Consider the case where `mod1` wants to create a capability, associate it with a resource (e.g. an IBC channel) by name, then pass it to `mod2` which will use it later:
280+
Consider the case where `mod1` wants to create a capability, associate it with a resource (e.g. an IBC channel) by name, and then pass it to `mod2` which will use it later:
281281

282282
Module 1 would have the following code:
283283

@@ -327,12 +327,12 @@ Proposed.
327327
### Positive
328328

329329
* Dynamic capability support.
330-
* Allows CapabilityKeeper to return same capability pointer from go-map while reverting any writes to the persistent `KVStore` and in-memory `MemoryStore` on tx failure.
330+
* Allows CapabilityKeeper to return the same capability pointer from go-map while reverting any writes to the persistent `KVStore` and in-memory `MemoryStore` on tx failure.
331331

332332
### Negative
333333

334334
* Requires an additional keeper.
335-
* Some overlap with existing `StoreKey` system (in the future they could be combined, since this is a superset functionality-wise).
335+
* Some overlap with the existing `StoreKey` system (in the future they could be combined, since this is a superset functionality-wise).
336336
* Requires an extra level of indirection in the reverse mapping, since MemoryStore must map to index which must then be used as key in a go map to retrieve the actual capability
337337

338338
### Neutral

docs/architecture/adr-010-modular-antehandler.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Pros:
2929
Cons:
3030

3131
1. Improves granularity but still cannot get more granular than a per-module basis. e.g. If auth's `AnteHandle` function is in charge of validating memo and signatures, users cannot swap the signature-checking functionality while keeping the rest of auth's `AnteHandle` functionality.
32-
2. Module AnteHandler are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another.
32+
2. Module AnteHandlers are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another.
3333

3434
### Decorator Pattern
3535

@@ -157,7 +157,7 @@ app.SetAnteHandler(mm.GetAnteHandler())
157157

158158
#### Custom Workflow
159159

160-
This is an example workflow for a user that wants to implement custom antehandler logic. In this example, the user wants to implement custom signature verification and change the order of antehandler so that validate memo runs before signature verification.
160+
This is an example workflow for a user who wants to implement custom antehandler logic. In this example, the user wants to implement custom signature verification and change the order of antehandler so that validate memo runs before signature verification.
161161

162162
##### User Code
163163

@@ -192,7 +192,7 @@ In addition, this approach will not break any core Cosmos SDK API's. Since we pr
192192

193193
Allow Decorator interface that can be chained together to create a Cosmos SDK AnteHandler.
194194

195-
This allows users to choose between implementing an AnteHandler by themselves and setting it in the baseapp, or use the decorator pattern to chain their custom decorators with the Cosmos SDK provided decorators in the order they wish.
195+
This allows users to choose between implementing an AnteHandler by themselves and setting it in the baseapp, or using the decorator pattern to chain their custom decorators with the Cosmos SDK provided decorators in the order they wish.
196196

197197
```go
198198
// An AnteDecorator wraps an AnteHandler, and can do pre- and post-processing on the next AnteHandler

docs/architecture/adr-012-state-accessors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ value and finally unmarshal. Usually this is done by declaring `Keeper.GetXXX` a
1515
which are repetitive and hard to maintain.
1616

1717
Second, this makes it harder to align with the object capability theorem: the right to access the
18-
state is defined as a `StoreKey`, which gives full access on the entire Merkle tree, so a module cannot
18+
state is defined as a `StoreKey`, which gives full access to the entire Merkle tree, so a module cannot
1919
send the access right to a specific key-value pair (or a set of key-value pairs) to another module safely.
2020

2121
Finally, because the getter/setter functions are defined as methods of a module's `Keeper`, the reviewers

docs/architecture/adr-013-metrics.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Proposed
1010

1111
## Context
1212

13-
Telemetry is paramount into debugging and understanding what the application is doing and how it is
13+
Telemetry is paramount in debugging and understanding what the application is doing and how it is
1414
performing. We aim to expose metrics from modules and other core parts of the Cosmos SDK.
1515

1616
In addition, we should aim to support multiple configurable sinks that an operator may choose from.
@@ -148,7 +148,7 @@ func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
148148

149149
### Positive
150150

151-
* Exposure into the performance and behavior of an application
151+
* Exposure to the performance and behavior of an application
152152

153153
### Negative
154154

0 commit comments

Comments
 (0)