Skip to content

Commit 86638c2

Browse files
colin-axnerseantkingmergify[bot]
authored
feat: Add GetAppVersion to ICS4Wrapper (#1022)
* feat: Add ICS4Wrapper function GetChannelVersion Add a function to 04-channel keeper which can be used in the ICS4Wrapper interface for obtaining the unwrapped channel verison * add docs * chore: rename GetChannelVersion to GetUnwrappedChannelVersion * add changelog entry * Update CHANGELOG.md * Update docs/ibc/middleware/develop.md * Update docs/ibc/middleware/develop.md * chore: GetUnwrappedChannelVersion -> GetAppVersion * add GetAppVersion for ics29 * add GetAppVersion to ics27 * add extra test for 29-fee * update docs * Apply suggestions from code review Co-authored-by: Sean King <seantking@users.noreply.github.com> Co-authored-by: Sean King <seantking@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 23fccc5 commit 86638c2

File tree

15 files changed

+269
-1
lines changed

15 files changed

+269
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
4848

4949
### Improvements
5050

51+
* (middleware) [\#1022](https://github.com/cosmos/ibc-go/pull/1022) Add `GetAppVersion` to the ICS4Wrapper interface. This function should be used by IBC applications to obtain their own version since the version set in the channel structure may be wrapped many times by middleware.
5152
* (modules/core/04-channel) [\#1160](https://github.com/cosmos/ibc-go/pull/1160) Improve `uint64 -> string` performance in `Logger`.
5253
* (modules/core/04-channel) [\#1232](https://github.com/cosmos/ibc-go/pull/1232) Updating params on `NewPacketId` and moving to bottom of file.
5354

docs/ibc/middleware/develop.md

+21
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Middleware interface {
4949
type ICS4Wrapper interface {
5050
SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet) error
5151
WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet, ack []byte) error
52+
GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool)
5253
}
5354
```
5455

@@ -239,4 +240,24 @@ func SendPacket(appPacket channeltypes.Packet) {
239240

240241
return ics4Keeper.SendPacket(packet)
241242
}
243+
244+
// middleware must return the underlying application version
245+
func GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
246+
version, found := ics4Keeper.GetAppVersion(ctx, portID, channelID)
247+
if !found {
248+
return "", false
249+
}
250+
251+
if !MiddlewareEnabled {
252+
return version, true
253+
}
254+
255+
// unwrap channel version
256+
metadata, err := Unmarshal(version)
257+
if err != nil {
258+
panic(fmt.Errof("unable to unmarshal version: %w", err))
259+
}
260+
261+
return metadata.AppVersion, true
262+
}
242263
```

modules/apps/27-interchain-accounts/controller/ibc_module.go

+5
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,8 @@ func (im IBCModule) OnTimeoutPacket(
163163

164164
return im.app.OnTimeoutPacket(ctx, packet, relayer)
165165
}
166+
167+
// GetAppVersion returns the interchain accounts metadata.
168+
func (im IBCModule) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
169+
return im.keeper.GetAppVersion(ctx, portID, channelID)
170+
}

modules/apps/27-interchain-accounts/controller/ibc_module_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/stretchr/testify/suite"
1010
"github.com/tendermint/tendermint/crypto"
1111

12+
icacontroller "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller"
1213
"github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types"
1314
icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"
1415
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
@@ -702,3 +703,23 @@ func (suite *InterchainAccountsTestSuite) TestSingleHostMultipleControllers() {
702703
})
703704
}
704705
}
706+
707+
func (suite *InterchainAccountsTestSuite) TestGetAppVersion() {
708+
path := NewICAPath(suite.chainA, suite.chainB)
709+
suite.coordinator.SetupConnections(path)
710+
711+
err := SetupICAPath(path, TestOwnerAddress)
712+
suite.Require().NoError(err)
713+
714+
module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID)
715+
suite.Require().NoError(err)
716+
717+
cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module)
718+
suite.Require().True(ok)
719+
720+
controllerModule := cbs.(icacontroller.IBCModule)
721+
722+
appVersion, found := controllerModule.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
723+
suite.Require().True(found)
724+
suite.Require().Equal(path.EndpointA.ChannelConfig.Version, appVersion)
725+
}

modules/apps/27-interchain-accounts/controller/keeper/keeper.go

+5
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability
102102
return k.scopedKeeper.ClaimCapability(ctx, cap, name)
103103
}
104104

105+
// GetAppVersion calls the ICS4Wrapper GetAppVersion function.
106+
func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
107+
return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID)
108+
}
109+
105110
// GetActiveChannelID retrieves the active channelID from the store, keyed by the provided connectionID and portID
106111
func (k Keeper) GetActiveChannelID(ctx sdk.Context, connectionID, portID string) (string, bool) {
107112
store := ctx.KVStore(k.storeKey)

modules/apps/27-interchain-accounts/types/expected_keepers.go

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type AccountKeeper interface {
2121
// ICS4Wrapper defines the expected ICS4Wrapper for middleware
2222
type ICS4Wrapper interface {
2323
SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error
24+
GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool)
2425
}
2526

2627
// ChannelKeeper defines the expected IBC channel keeper

modules/apps/29-fee/ibc_module.go

+5
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,8 @@ func (im IBCModule) OnTimeoutPacket(
267267
// call underlying callback
268268
return im.app.OnTimeoutPacket(ctx, packet, relayer)
269269
}
270+
271+
// GetAppVersion returns the application version of the underlying application
272+
func (im IBCModule) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
273+
return im.keeper.GetAppVersion(ctx, portID, channelID)
274+
}

modules/apps/29-fee/ibc_module_test.go

+76
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
sdk "github.com/cosmos/cosmos-sdk/types"
77
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
88

9+
fee "github.com/cosmos/ibc-go/v3/modules/apps/29-fee"
910
"github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"
1011
transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"
1112
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
@@ -834,3 +835,78 @@ func (suite *FeeTestSuite) TestOnTimeoutPacket() {
834835
})
835836
}
836837
}
838+
839+
func (suite *FeeTestSuite) TestGetAppVersion() {
840+
var (
841+
portID string
842+
channelID string
843+
expAppVersion string
844+
)
845+
testCases := []struct {
846+
name string
847+
malleate func()
848+
expFound bool
849+
}{
850+
{
851+
"success for fee enabled channel",
852+
func() {
853+
expAppVersion = ibcmock.Version
854+
},
855+
true,
856+
},
857+
{
858+
"success for non fee enabled channel",
859+
func() {
860+
path := ibctesting.NewPath(suite.chainA, suite.chainB)
861+
path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort
862+
path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort
863+
// by default a new path uses a non fee channel
864+
suite.coordinator.Setup(path)
865+
portID = path.EndpointA.ChannelConfig.PortID
866+
channelID = path.EndpointA.ChannelID
867+
868+
expAppVersion = ibcmock.Version
869+
},
870+
true,
871+
},
872+
{
873+
"channel does not exist",
874+
func() {
875+
channelID = "does not exist"
876+
},
877+
false,
878+
},
879+
}
880+
881+
for _, tc := range testCases {
882+
tc := tc
883+
suite.Run(tc.name, func() {
884+
suite.SetupTest()
885+
suite.coordinator.Setup(suite.path)
886+
887+
portID = suite.path.EndpointA.ChannelConfig.PortID
888+
channelID = suite.path.EndpointA.ChannelID
889+
890+
// malleate test case
891+
tc.malleate()
892+
893+
module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort)
894+
suite.Require().NoError(err)
895+
896+
cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module)
897+
suite.Require().True(ok)
898+
899+
feeModule := cbs.(fee.IBCModule)
900+
901+
appVersion, found := feeModule.GetAppVersion(suite.chainA.GetContext(), portID, channelID)
902+
903+
if tc.expFound {
904+
suite.Require().True(found)
905+
suite.Require().Equal(expAppVersion, appVersion)
906+
} else {
907+
suite.Require().False(found)
908+
suite.Require().Empty(appVersion)
909+
}
910+
})
911+
}
912+
}

modules/apps/29-fee/keeper/relay.go

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package keeper
22

33
import (
4+
"fmt"
5+
46
sdk "github.com/cosmos/cosmos-sdk/types"
57
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
68
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
@@ -42,3 +44,22 @@ func (k Keeper) WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.C
4244
// ics4Wrapper may be core IBC or higher-level middleware
4345
return k.ics4Wrapper.WriteAcknowledgement(ctx, chanCap, packet, ack)
4446
}
47+
48+
// GetAppVersion returns the underlying application version.
49+
func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
50+
version, found := k.ics4Wrapper.GetAppVersion(ctx, portID, channelID)
51+
if !found {
52+
return "", false
53+
}
54+
55+
if !k.IsFeeEnabled(ctx, portID, channelID) {
56+
return version, true
57+
}
58+
59+
var metadata types.Metadata
60+
if err := types.ModuleCdc.UnmarshalJSON([]byte(version), &metadata); err != nil {
61+
panic(fmt.Errorf("unable to unmarshal metadata for fee enabled channel: %w", err))
62+
}
63+
64+
return metadata.AppVersion, true
65+
}

modules/apps/29-fee/keeper/relay_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"
55
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
66
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
7+
ibctesting "github.com/cosmos/ibc-go/v3/testing"
8+
ibcmock "github.com/cosmos/ibc-go/v3/testing/mock"
79
)
810

911
func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() {
@@ -101,3 +103,70 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgementAsyncFeeDisabled() {
101103
packetAck, _ := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.DestinationPort, packet.DestinationChannel, 1)
102104
suite.Require().Equal(packetAck, channeltypes.CommitAcknowledgement(ack.Acknowledgement()))
103105
}
106+
107+
func (suite *KeeperTestSuite) TestGetAppVersion() {
108+
var (
109+
portID string
110+
channelID string
111+
expAppVersion string
112+
)
113+
testCases := []struct {
114+
name string
115+
malleate func()
116+
expFound bool
117+
}{
118+
{
119+
"success for fee enabled channel",
120+
func() {
121+
expAppVersion = ibcmock.Version
122+
},
123+
true,
124+
},
125+
{
126+
"success for non fee enabled channel",
127+
func() {
128+
path := ibctesting.NewPath(suite.chainA, suite.chainB)
129+
path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort
130+
path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort
131+
// by default a new path uses a non fee channel
132+
suite.coordinator.Setup(path)
133+
portID = path.EndpointA.ChannelConfig.PortID
134+
channelID = path.EndpointA.ChannelID
135+
136+
expAppVersion = ibcmock.Version
137+
},
138+
true,
139+
},
140+
{
141+
"channel does not exist",
142+
func() {
143+
channelID = "does not exist"
144+
},
145+
false,
146+
},
147+
}
148+
149+
for _, tc := range testCases {
150+
tc := tc
151+
suite.Run(tc.name, func() {
152+
suite.SetupTest()
153+
suite.coordinator.Setup(suite.path)
154+
155+
portID = suite.path.EndpointA.ChannelConfig.PortID
156+
channelID = suite.path.EndpointA.ChannelID
157+
158+
// malleate test case
159+
tc.malleate()
160+
161+
appVersion, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetAppVersion(suite.chainA.GetContext(), portID, channelID)
162+
163+
if tc.expFound {
164+
suite.Require().True(found)
165+
suite.Require().Equal(expAppVersion, appVersion)
166+
} else {
167+
suite.Require().False(found)
168+
suite.Require().Empty(appVersion)
169+
}
170+
})
171+
}
172+
}

modules/apps/29-fee/types/expected_keepers.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type AccountKeeper interface {
1818
type ICS4Wrapper interface {
1919
WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement) error
2020
SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error
21+
GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool)
2122
}
2223

2324
// ChannelKeeper defines the expected IBC channel keeper

modules/core/04-channel/keeper/keeper.go

+10
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ func (k Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel ty
8686
store.Set(host.ChannelKey(portID, channelID), bz)
8787
}
8888

89+
// GetAppVersion gets the version for the specified channel.
90+
func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
91+
channel, found := k.GetChannel(ctx, portID, channelID)
92+
if !found {
93+
return "", false
94+
}
95+
96+
return channel.Version, true
97+
}
98+
8999
// GetNextChannelSequence gets the next channel sequence from the store.
90100
func (k Keeper) GetNextChannelSequence(ctx sdk.Context) uint64 {
91101
store := ctx.KVStore(k.storeKey)

modules/core/04-channel/keeper/keeper_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
99
ibctesting "github.com/cosmos/ibc-go/v3/testing"
10+
ibcmock "github.com/cosmos/ibc-go/v3/testing/mock"
1011
)
1112

1213
// KeeperTestSuite is a testing suite to test keeper functions.
@@ -62,6 +63,24 @@ func (suite *KeeperTestSuite) TestSetChannel() {
6263
suite.Equal(expectedCounterparty, storedChannel.Counterparty)
6364
}
6465

66+
func (suite *KeeperTestSuite) TestGetAppVersion() {
67+
// create client and connections on both chains
68+
path := ibctesting.NewPath(suite.chainA, suite.chainB)
69+
suite.coordinator.SetupConnections(path)
70+
71+
version, found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
72+
suite.Require().False(found)
73+
suite.Require().Empty(version)
74+
75+
// init channel
76+
err := path.EndpointA.ChanOpenInit()
77+
suite.NoError(err)
78+
79+
channelVersion, found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
80+
suite.Require().True(found)
81+
suite.Require().Equal(ibcmock.Version, channelVersion)
82+
}
83+
6584
// TestGetAllChannels creates multiple channels on chain A through various connections
6685
// and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1
6786
func (suite KeeperTestSuite) TestGetAllChannels() {

modules/core/05-port/types/module.go

+6
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ type ICS4Wrapper interface {
113113
packet exported.PacketI,
114114
ack exported.Acknowledgement,
115115
) error
116+
117+
GetAppVersion(
118+
ctx sdk.Context,
119+
portID,
120+
channelID string,
121+
) (string, bool)
116122
}
117123

118124
// Middleware must implement IBCModule to wrap communication from core IBC to underlying application

testing/endpoint.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,14 @@ func (endpoint *Endpoint) ChanOpenAck() error {
328328
proof, height,
329329
endpoint.Chain.SenderAccount.GetAddress().String(),
330330
)
331-
return endpoint.Chain.sendMsgs(msg)
331+
332+
if err = endpoint.Chain.sendMsgs(msg); err != nil {
333+
return err
334+
}
335+
336+
endpoint.ChannelConfig.Version = endpoint.GetChannel().Version
337+
338+
return nil
332339
}
333340

334341
// ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated endpoint.

0 commit comments

Comments
 (0)