Skip to content

Commit fe098b9

Browse files
srdtrkcrodriguezvega
authored andcommitted
feat(ica)!: support json tx encoding for interchain accounts (cosmos#3796)
* feat(ica): added EncodingJson to supported encodings * imp(ica): changed the type of cdc to Codec in ica/host * imp(ica): changed the type of cdc to Codec in ica/controller * imp(ica.test): added a test cases for EncodingJSON * imp(ica): created invalid encoding err * feat(ica)!: first prototype of json supporting DeserializeCosmosTx * docs(ica): updated godoc of DeserializeCosmosTx * docs(ica): added comments to DeserializeCosmosTx * fix(ica.test): fixed tests for DeserializeCosmosTx * fix(ica): fixed 'OnRecvPacket' in relay.go * fix(ica): fixed unhandled error * style(ica): made DeserializeCosmosTx more compact * fix(ica/host.cli.test): fixed a cli test * style(ica): ran gofumpt * style(ica): changed err message * feat(ica): first prototype of SerializeCosmosTx is implemented * fix(ica): fixed codec tests * fix(ica/host.test): fix test * fix(ica/host.test): fix test * fix(ica/host.cli): cli always uses protobuf * nil(ica/host.test): removed unneeded comment * fix(ica/controller.test): fix test * fix(ica/controller.test): fix test * fix(ica/controller.test): fix test * fix(fee.test): fix test * nit: temporary save commit * fix(ica): fixed json serde tests not passing * fix(ica): fix panic if message does not implement sdk.Msg * imp(ica): improved json serde functions * style(ica): pleased the linter * style(ica): ran gofumpt * fix(e2e): fix compilation errors by adding icatypes.EncodingProtobuf arg to serde functions * feat(ica.test): added important wip test for deserializing directly from cosmwasm * imp(ica.test): added a new test case to cw codec unit test * imp(ica): added another test case * imp(ica.test): added another test case * imp(ica.test): added another test case * style(ica.test): improved test style * style(ica.test): improved test style * style(ica.test): ran gofumpt * imp(ica.test): added json encoding version string for testing * imp(ica.test): added new 'NewJSONICAPath' function * imp(ica.test): added encoding field to ica test setup functions * fix(ica.test): fixed test setups using the new encoding field * feat(ica.test): added json test case * style(ica.test): ran gofumpt * feat(ica.test): got two cases of cosmwasm tests working in relay * style(ica.test): ran gofumpt * feat(ica): started progress on recursive handling of Anys * imp(ica.test): added a new test case for ica json encoding, this fails * feat(ica): achieved total json serialization (excluding any lists) * refactor(ica): made function shorter and removed duplicated code * style(ica): ran gofumpt * imp(ica): added more err handling code * refactor(ica): made deserialize code shorter * style(ica): made linter a bit more happy * fix(ica.test): fixed one codec test case * feat(ica): added []Any handling code * fix(ica): added more safety * nit: deleted testing codec.go * feat(ica): all works * style(ica): ran gofumpt * style(ica): made linter happy * refactor(ica): reduced code duplication * nit(ica): uncommented some test cases * imp(ica.test): added more test cases * feat(ica.test): finished test cases * style(ica.test): reorganized test cases * refactor(ica.test): combined the two test cases into one * style(ica.test): ran gofumpt * style(ica.test): renamed wallet address * fix(ica.test): fixed test case names * imp(ica.test): added more test cases * style(ica.test): ran gofumpt * test(ica): added more codec test cases * style(ica.test): ran gofumpt * feat(ica): removed JSONAny and JSONCosmosTx types * feat(ica): implemented json encoding using module codec * fix(ica.test): tests now match the new codec implementation * fix(ica.test): fixed the tests to the new implementation * style(ica.test): reorgenized the order of tests so that git diff makes sense * imp(ica/controller): controller codec need not be codec.Codec * imp(ica): replaced BinaryCodec with Codec * test(ica): fixed codec test * docs(ica.test): codec comment updated * docs(ica.test): updated comments * style(ica.test): removed 'from cosmwasm' from test case name as it is aparent from test name * style(ica.test): ran gofumpt * fix: fix merge error * deps(ica): replaced sdk.NewInt with sdkmath.NewInt * style(ica): ran 'gofumpt' * imp(ica): removed redundant cosmwasm tests * revert: "imp(ica): removed redundant cosmwasm tests" This reverts commit 5123fba. * imp(ica.test): made codec_test human readable * imp(ica.test): made relay_test human readable * style(ica.test): ran 'golanci-lint run --fix' * imp(ica/host): created 'GetAppMetadata' function * refactor(ica/host): used GetAppMetadata function * imp(ica.test): removed unneeded encoding argument * imp(ica): removed ErrUnsupportedEncoding * imp(ica.test): used suite chainB height instead of clienttypes.NewHeight(1, 100) * imp(ica.test): add nil check for unsupported encoding * imp(ica.test): added a empty/nil checks * style(ica.test): renamed version variable to TestVersionWithJSONEncoding * imp(ica): wrapped some errors * style(ica): ran 'golanci-lint run --fix' * style(ica)!: renamed EncodingJSON to EncodingProto3JSON * docs(ica): improved godocs * imp(ica): passing codec instead of binary codec * style(ica): improved error messages and godocs * docs(ica.test): improved godocs for tests * imp(ica.test): improved unsupported encoding test case slightly * style(ica.test): test style improvements * imp(ica.test): added expError to some codec tests * imp(ica.test): added more error type checks to codec tests * style(ica.test): ran 'golangci-lint run --fix' * imp(ica/host.test): added 'TestMetadataNotFound' * imp(ica/host.test): reduce test size * docs(ica/host.test): updated godocs for test * docs(ica/host): improved godoc * imp(ica/host): made GetAppMetadata private --------- Co-authored-by: Carlos Rodriguez <carlos@interchain.io>
1 parent bc1d1d6 commit fe098b9

File tree

15 files changed

+953
-157
lines changed

15 files changed

+953
-157
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
// Keeper defines the IBC interchain accounts controller keeper
2727
type Keeper struct {
2828
storeKey storetypes.StoreKey
29-
cdc codec.BinaryCodec
29+
cdc codec.Codec
3030
legacySubspace paramtypes.Subspace
3131
ics4Wrapper porttypes.ICS4Wrapper
3232
channelKeeper icatypes.ChannelKeeper
@@ -43,7 +43,7 @@ type Keeper struct {
4343

4444
// NewKeeper creates a new interchain accounts controller Keeper instance
4545
func NewKeeper(
46-
cdc codec.BinaryCodec, key storetypes.StoreKey, legacySubspace paramtypes.Subspace,
46+
cdc codec.Codec, key storetypes.StoreKey, legacySubspace paramtypes.Subspace,
4747
ics4Wrapper porttypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper,
4848
scopedKeeper exported.ScopedKeeper, msgRouter icatypes.MessageRouter, authority string,
4949
) Keeper {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func (suite *KeeperTestSuite) TestSubmitTx() {
171171
Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))),
172172
}
173173

174-
data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []proto.Message{icaMsg})
174+
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{icaMsg}, icatypes.EncodingProtobuf)
175175
suite.Require().NoError(err)
176176

177177
packetData := icatypes.InterchainAccountPacketData{

modules/apps/27-interchain-accounts/host/client/cli/tx_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ func TestGeneratePacketData(t *testing.T) {
125125
require.Equal(t, tc.memo, packetData.Memo)
126126

127127
data := packetData.Data
128-
messages, err := icatypes.DeserializeCosmosTx(cdc, data)
128+
// cli tx commands always use protobuf encoding
129+
messages, err := icatypes.DeserializeCosmosTx(cdc, data, icatypes.EncodingProtobuf)
129130

130131
require.NoError(t, err)
131132
require.NotNil(t, messages)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() {
446446
ToAddress: suite.chainB.SenderAccount.GetAddress().String(),
447447
Amount: amount,
448448
}
449-
data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []proto.Message{msg})
449+
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, icatypes.EncodingProtobuf)
450450
suite.Require().NoError(err)
451451

452452
icaPacketData := icatypes.InterchainAccountPacketData{
@@ -655,7 +655,7 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose()
655655
Amount: tokenAmt,
656656
}
657657

658-
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg})
658+
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, icatypes.EncodingProtobuf)
659659
suite.Require().NoError(err)
660660

661661
icaPacketData := icatypes.InterchainAccountPacketData{
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package keeper
2+
3+
/*
4+
This file is to allow for unexported functions to be accessible to the testing package.
5+
*/
6+
7+
import (
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
10+
icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
11+
)
12+
13+
// GetAppMetadata is a wrapper around getAppMetadata to allow the function to be directly called in tests.
14+
func (k Keeper) GetAppMetadata(ctx sdk.Context, portID, channelID string) (icatypes.Metadata, error) {
15+
return k.getAppMetadata(ctx, portID, channelID)
16+
}

modules/apps/27-interchain-accounts/host/keeper/genesis_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (suite *KeeperTestSuite) TestGenesisParams() {
109109
func (suite *KeeperTestSuite) TestExportGenesis() {
110110
suite.SetupTest()
111111

112-
path := NewICAPath(suite.chainA, suite.chainB)
112+
path := NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
113113
suite.coordinator.SetupConnections(path)
114114

115115
err := SetupICAPath(path, TestOwnerAddress)

modules/apps/27-interchain-accounts/host/keeper/handshake_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() {
278278
suite.Run(tc.name, func() {
279279
suite.SetupTest() // reset
280280

281-
path = NewICAPath(suite.chainA, suite.chainB)
281+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
282282
suite.coordinator.SetupConnections(path)
283283

284284
err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
@@ -358,7 +358,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenConfirm() {
358358
suite.Run(tc.name, func() {
359359
suite.SetupTest() // reset
360360

361-
path = NewICAPath(suite.chainA, suite.chainB)
361+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
362362
suite.coordinator.SetupConnections(path)
363363

364364
err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
@@ -401,7 +401,7 @@ func (suite *KeeperTestSuite) TestOnChanCloseConfirm() {
401401
suite.Run(tc.name, func() {
402402
suite.SetupTest() // reset
403403

404-
path = NewICAPath(suite.chainA, suite.chainB)
404+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
405405
suite.coordinator.SetupConnections(path)
406406

407407
err := SetupICAPath(path, TestOwnerAddress)

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strings"
66

7+
errorsmod "cosmossdk.io/errors"
78
"cosmossdk.io/log"
89
storetypes "cosmossdk.io/store/types"
910

@@ -18,6 +19,7 @@ import (
1819
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
1920
porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types"
2021
host "github.com/cosmos/ibc-go/v7/modules/core/24-host"
22+
ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors"
2123
"github.com/cosmos/ibc-go/v7/modules/core/exported"
2224
)
2325

@@ -106,6 +108,22 @@ func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string
106108
return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID)
107109
}
108110

111+
// GetAppMetadata retrieves the interchain accounts channel metadata from the store associated with the provided portID and channelID
112+
func (k Keeper) getAppMetadata(ctx sdk.Context, portID, channelID string) (icatypes.Metadata, error) {
113+
appVersion, found := k.GetAppVersion(ctx, portID, channelID)
114+
if !found {
115+
return icatypes.Metadata{}, errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", portID, channelID)
116+
}
117+
118+
var metadata icatypes.Metadata
119+
if err := icatypes.ModuleCdc.UnmarshalJSON([]byte(appVersion), &metadata); err != nil {
120+
// UnmarshalJSON errors are indeterminate and therefore are not wrapped and included in failed acks
121+
return icatypes.Metadata{}, errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata")
122+
}
123+
124+
return metadata, nil
125+
}
126+
109127
// GetActiveChannelID retrieves the active channelID from the store keyed by the provided connectionID and portID
110128
func (k Keeper) GetActiveChannelID(ctx sdk.Context, connectionID, portID string) (string, bool) {
111129
store := ctx.KVStore(k.storeKey)

modules/apps/27-interchain-accounts/host/keeper/keeper_test.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper_test
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/stretchr/testify/suite"
@@ -9,6 +10,7 @@ import (
910
"github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types"
1011
icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
1112
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
13+
ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors"
1214
ibctesting "github.com/cosmos/ibc-go/v7/testing"
1315
)
1416

@@ -27,6 +29,15 @@ var (
2729
Encoding: icatypes.EncodingProtobuf,
2830
TxType: icatypes.TxTypeSDKMultiMsg,
2931
}))
32+
33+
// TestVersionWithJSONEncoding defines a reusable interchainaccounts version string that uses JSON encoding for testing purposes
34+
TestVersionWithJSONEncoding = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{
35+
Version: icatypes.Version,
36+
ControllerConnectionId: ibctesting.FirstConnectionID,
37+
HostConnectionId: ibctesting.FirstConnectionID,
38+
Encoding: icatypes.EncodingProto3JSON,
39+
TxType: icatypes.TxTypeSDKMultiMsg,
40+
}))
3041
)
3142

3243
type KeeperTestSuite struct {
@@ -47,14 +58,25 @@ func (suite *KeeperTestSuite) SetupTest() {
4758
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3))
4859
}
4960

50-
func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path {
61+
func NewICAPath(chainA, chainB *ibctesting.TestChain, encoding string) *ibctesting.Path {
5162
path := ibctesting.NewPath(chainA, chainB)
63+
64+
var version string
65+
switch encoding {
66+
case icatypes.EncodingProtobuf:
67+
version = TestVersion
68+
case icatypes.EncodingProto3JSON:
69+
version = TestVersionWithJSONEncoding
70+
default:
71+
panic(fmt.Sprintf("unsupported encoding type: %s", encoding))
72+
}
73+
5274
path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID
5375
path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID
5476
path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED
5577
path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED
56-
path.EndpointA.ChannelConfig.Version = TestVersion
57-
path.EndpointB.ChannelConfig.Version = TestVersion
78+
path.EndpointA.ChannelConfig.Version = version
79+
path.EndpointB.ChannelConfig.Version = version
5880

5981
return path
6082
}
@@ -85,7 +107,7 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro
85107

86108
channelSequence := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(endpoint.Chain.GetContext())
87109

88-
if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, TestVersion); err != nil {
110+
if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, endpoint.ChannelConfig.Version); err != nil {
89111
return err
90112
}
91113

@@ -106,7 +128,7 @@ func TestKeeperTestSuite(t *testing.T) {
106128
func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() {
107129
suite.SetupTest()
108130

109-
path := NewICAPath(suite.chainA, suite.chainB)
131+
path := NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
110132
suite.coordinator.SetupConnections(path)
111133

112134
err := SetupICAPath(path, TestOwnerAddress)
@@ -131,7 +153,7 @@ func (suite *KeeperTestSuite) TestGetAllActiveChannels() {
131153

132154
suite.SetupTest()
133155

134-
path := NewICAPath(suite.chainA, suite.chainB)
156+
path := NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
135157
suite.coordinator.SetupConnections(path)
136158

137159
err := SetupICAPath(path, TestOwnerAddress)
@@ -165,7 +187,7 @@ func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() {
165187

166188
suite.SetupTest()
167189

168-
path := NewICAPath(suite.chainA, suite.chainB)
190+
path := NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
169191
suite.coordinator.SetupConnections(path)
170192

171193
err := SetupICAPath(path, TestOwnerAddress)
@@ -197,7 +219,7 @@ func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() {
197219
func (suite *KeeperTestSuite) TestIsActiveChannel() {
198220
suite.SetupTest()
199221

200-
path := NewICAPath(suite.chainA, suite.chainB)
222+
path := NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
201223
suite.coordinator.SetupConnections(path)
202224

203225
err := SetupICAPath(path, TestOwnerAddress)
@@ -220,6 +242,17 @@ func (suite *KeeperTestSuite) TestSetInterchainAccountAddress() {
220242
suite.Require().Equal(expectedAccAddr, retrievedAddr)
221243
}
222244

245+
func (suite *KeeperTestSuite) TestMetadataNotFound() {
246+
var (
247+
invalidPortID = "invalid-port"
248+
invalidChannelID = "invalid-channel"
249+
)
250+
251+
_, err := suite.chainB.GetSimApp().ICAHostKeeper.GetAppMetadata(suite.chainB.GetContext(), invalidPortID, invalidChannelID)
252+
suite.Require().ErrorIs(err, ibcerrors.ErrNotFound)
253+
suite.Require().Contains(err.Error(), fmt.Sprintf("app version not found for port %s and channel %s", invalidPortID, invalidChannelID))
254+
}
255+
223256
func (suite *KeeperTestSuite) TestParams() {
224257
expParams := types.DefaultParams()
225258

modules/apps/27-interchain-accounts/host/keeper/relay.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) ([]byt
2424
return nil, errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data")
2525
}
2626

27+
metadata, err := k.getAppMetadata(ctx, packet.DestinationPort, packet.DestinationChannel)
28+
if err != nil {
29+
return nil, err
30+
}
31+
2732
switch data.Type {
2833
case icatypes.EXECUTE_TX:
29-
msgs, err := icatypes.DeserializeCosmosTx(k.cdc, data.Data)
34+
msgs, err := icatypes.DeserializeCosmosTx(k.cdc, data.Data, metadata.Encoding)
3035
if err != nil {
3136
return nil, errorsmod.Wrapf(err, "failed to deserialize interchain account transaction")
3237
}

0 commit comments

Comments
 (0)