Skip to content

Commit e5b057d

Browse files
feat(ica)!: support json tx encoding for interchain accounts (#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 1f4550f commit e5b057d

File tree

15 files changed

+954
-134
lines changed

15 files changed

+954
-134
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
@@ -27,7 +27,7 @@ import (
2727
// Keeper defines the IBC interchain accounts controller keeper
2828
type Keeper struct {
2929
storeKey storetypes.StoreKey
30-
cdc codec.BinaryCodec
30+
cdc codec.Codec
3131
legacySubspace paramtypes.Subspace
3232
ics4Wrapper porttypes.ICS4Wrapper
3333
channelKeeper icatypes.ChannelKeeper
@@ -44,7 +44,7 @@ type Keeper struct {
4444

4545
// NewKeeper creates a new interchain accounts controller Keeper instance
4646
func NewKeeper(
47-
cdc codec.BinaryCodec, key storetypes.StoreKey, legacySubspace paramtypes.Subspace,
47+
cdc codec.Codec, key storetypes.StoreKey, legacySubspace paramtypes.Subspace,
4848
ics4Wrapper porttypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper,
4949
scopedKeeper exported.ScopedKeeper, msgRouter icatypes.MessageRouter, authority string,
5050
) 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}, icatypes.EncodingProtobuf)
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func TestGeneratePacketData(t *testing.T) {
125125
require.Equal(t, tc.memo, packetData.Memo)
126126

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

130131
require.NoError(t, err)

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

Lines changed: 1 addition & 1 deletion
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}, icatypes.EncodingProtobuf)
449+
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, icatypes.EncodingProtobuf)
450450
suite.Require().NoError(err)
451451

452452
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
@@ -276,7 +276,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() {
276276
suite.Run(tc.name, func() {
277277
suite.SetupTest() // reset
278278

279-
path = NewICAPath(suite.chainA, suite.chainB)
279+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
280280
suite.coordinator.SetupConnections(path)
281281

282282
err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
@@ -356,7 +356,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenConfirm() {
356356
suite.Run(tc.name, func() {
357357
suite.SetupTest() // reset
358358

359-
path = NewICAPath(suite.chainA, suite.chainB)
359+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
360360
suite.coordinator.SetupConnections(path)
361361

362362
err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
@@ -399,7 +399,7 @@ func (suite *KeeperTestSuite) TestOnChanCloseConfirm() {
399399
suite.Run(tc.name, func() {
400400
suite.SetupTest() // reset
401401

402-
path = NewICAPath(suite.chainA, suite.chainB)
402+
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProtobuf)
403403
suite.coordinator.SetupConnections(path)
404404

405405
err := SetupICAPath(path, TestOwnerAddress)

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

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

7+
errorsmod "cosmossdk.io/errors"
8+
79
"github.com/cosmos/cosmos-sdk/codec"
810
storetypes "github.com/cosmos/cosmos-sdk/store/types"
911
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -18,6 +20,7 @@ import (
1820
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
1921
porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types"
2022
host "github.com/cosmos/ibc-go/v7/modules/core/24-host"
23+
ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors"
2124
"github.com/cosmos/ibc-go/v7/modules/core/exported"
2225
)
2326

@@ -106,6 +109,22 @@ func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string
106109
return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID)
107110
}
108111

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