From 97df8b605ce2839e496cf97ab783f89c72874289 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 20 Aug 2020 18:05:41 -0400 Subject: [PATCH] Verify Client on Connection Handshake (#7057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * verify client state * add client state to msgs and retrieve in handler * fix connection msgs * fixed handshake tests * fix tests * fix sim tests * revert pb edit * add ValidateClient tests * Apply suggestions from code review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * fix tests * fixed msgs test * use ibctesting for client state consts * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * address rest of comments * rename to ValidateSelfClient and update spec * lint * Update x/ibc/02-client/keeper/keeper_test.go * Update x/ibc/02-client/keeper/keeper_test.go * complete rest of review * improve cov Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- proto/ibc/connection/connection.proto | 35 +- simapp/test_helpers.go | 4 +- x/auth/types/query.pb.gw.go | 1 - x/bank/app_test.go | 12 +- x/bank/types/query.pb.gw.go | 1 - x/distribution/types/query.pb.gw.go | 1 - x/evidence/types/query.pb.gw.go | 1 - x/gov/types/query.pb.gw.go | 1 - x/ibc-transfer/types/query.pb.gw.go | 1 - x/ibc/02-client/exported/exported.go | 10 + x/ibc/02-client/keeper/keeper.go | 49 ++ x/ibc/02-client/keeper/keeper_test.go | 68 +++ x/ibc/02-client/types/codec.go | 10 + x/ibc/02-client/types/codec_test.go | 42 ++ x/ibc/02-client/types/errors.go | 36 +- x/ibc/03-connection/client/cli/tx.go | 53 +- x/ibc/03-connection/client/utils/utils.go | 18 + x/ibc/03-connection/handler.go | 18 +- x/ibc/03-connection/keeper/handshake.go | 25 + x/ibc/03-connection/keeper/handshake_test.go | 154 +++++- x/ibc/03-connection/keeper/verify.go | 29 ++ x/ibc/03-connection/keeper/verify_test.go | 53 +- x/ibc/03-connection/types/connection.pb.go | 467 +++++++++++++----- x/ibc/03-connection/types/connection_test.go | 2 + x/ibc/03-connection/types/expected_keepers.go | 1 + x/ibc/03-connection/types/msgs.go | 42 +- x/ibc/03-connection/types/msgs_test.go | 123 +++-- x/ibc/03-connection/types/query.pb.gw.go | 1 - x/ibc/04-channel/types/query.pb.gw.go | 1 - x/ibc/07-tendermint/types/client_state.go | 40 ++ .../07-tendermint/types/client_state_test.go | 45 +- x/ibc/09-localhost/types/client_state.go | 91 ++-- x/ibc/09-localhost/types/client_state_test.go | 428 ++++++++++------ x/ibc/spec/04_messages.md | 18 +- x/ibc/testing/chain.go | 27 +- x/mint/types/query.pb.gw.go | 1 - x/params/types/proposal/query.pb.gw.go | 1 - x/slashing/app_test.go | 4 +- x/slashing/types/query.pb.gw.go | 1 - x/staking/app_test.go | 8 +- x/staking/types/query.pb.gw.go | 1 - x/upgrade/types/query.pb.gw.go | 1 - 42 files changed, 1452 insertions(+), 473 deletions(-) create mode 100644 x/ibc/02-client/types/codec_test.go diff --git a/proto/ibc/connection/connection.proto b/proto/ibc/connection/connection.proto index 81c60263afd5..5135fd0fa403 100644 --- a/proto/ibc/connection/connection.proto +++ b/proto/ibc/connection/connection.proto @@ -4,6 +4,7 @@ package ibc.connection; option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"; import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; import "ibc/commitment/commitment.proto"; // MsgConnectionOpenInit defines the msg sent by an account on Chain A to @@ -29,18 +30,23 @@ message MsgConnectionOpenTry { string connection_id = 2 [ (gogoproto.moretags) = "yaml:\"connection_id\"" ]; - Counterparty counterparty = 3 [(gogoproto.nullable) = false]; - repeated string counterparty_versions = 4 + google.protobuf.Any client_state = 3 [ + (gogoproto.moretags) = "yaml:\"client_state\"" + ]; + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + repeated string counterparty_versions = 5 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""]; + uint64 proof_height = 6 [(gogoproto.moretags) = "yaml:\"proof_height\""]; // proof of the initialization the connection on Chain A: `UNITIALIZED -> // INIT` - bytes proof_init = 5 [(gogoproto.moretags) = "yaml:\"proof_init\""]; - uint64 proof_height = 6; + bytes proof_init = 7 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + // proof of client state included in message + bytes proof_client = 8 [(gogoproto.moretags) = "yaml:\"proof_client\""]; // proof of client consensus state - bytes proof_consensus = 7 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; - uint64 consensus_height = 8 + bytes proof_consensus = 9 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + uint64 consensus_height = 10 [(gogoproto.moretags) = "yaml:\"consensus_height\""]; - bytes signer = 9 + bytes signer = 11 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; } @@ -51,15 +57,20 @@ message MsgConnectionOpenAck { (gogoproto.moretags) = "yaml:\"connection_id\"" ]; string version = 2; + google.protobuf.Any client_state = 3 [ + (gogoproto.moretags) = "yaml:\"client_state\"" + ]; + uint64 proof_height = 4 [(gogoproto.moretags) = "yaml:\"proof_height\""]; // proof of the initialization the connection on Chain B: `UNITIALIZED -> // TRYOPEN` - bytes proof_try = 3 [(gogoproto.moretags) = "yaml:\"proof_try\""]; - uint64 proof_height = 4 [(gogoproto.moretags) = "yaml:\"proof_height\""]; + bytes proof_try = 5 [(gogoproto.moretags) = "yaml:\"proof_try\""]; + // proof of client state included in message + bytes proof_client = 6 [(gogoproto.moretags) = "yaml:\"proof_client\""]; // proof of client consensus state - bytes proof_consensus = 5 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; - uint64 consensus_height = 6 + bytes proof_consensus = 7 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + uint64 consensus_height = 8 [(gogoproto.moretags) = "yaml:\"consensus_height\""]; - bytes signer = 7 + bytes signer = 9 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; } diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 6d96fa05348a..a8675db97409 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -319,7 +319,7 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.C // returned. func SignCheckDeliver( t *testing.T, txGen client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, - accNums, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, + chainID string, accNums, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, ) (sdk.GasInfo, *sdk.Result, error) { tx, err := helpers.GenTx( @@ -327,7 +327,7 @@ func SignCheckDeliver( msgs, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, helpers.DefaultGenTxGas, - "", + chainID, accNums, seq, priv..., diff --git a/x/auth/types/query.pb.gw.go b/x/auth/types/query.pb.gw.go index 3b156842ddc0..7e80fa6fa5c2 100644 --- a/x/auth/types/query.pb.gw.go +++ b/x/auth/types/query.pb.gw.go @@ -106,7 +106,6 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Account_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 42122d0bca9b..bc04ab839a9b 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -109,7 +109,7 @@ func TestSendNotEnoughBalance(t *testing.T) { sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 100)}) header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1) + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1) require.Error(t, err) simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}) @@ -177,7 +177,7 @@ func TestSendToModuleAcc(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{test.msg}, []uint64{origAccNum}, []uint64{origSeq}, test.expSimPass, test.expPass, priv1) + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{test.msg}, "", []uint64{origAccNum}, []uint64{origSeq}, test.expSimPass, test.expPass, priv1) if test.expPass { require.NoError(t, err) } else { @@ -248,7 +248,7 @@ func TestMsgMultiSendWithAccounts(t *testing.T) { for _, tc := range testCases { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) if tc.expPass { require.NoError(t, err) } else { @@ -300,7 +300,7 @@ func TestMsgMultiSendMultipleOut(t *testing.T) { for _, tc := range testCases { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) require.NoError(t, err) for _, eb := range tc.expectedBalances { @@ -355,7 +355,7 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) { for _, tc := range testCases { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) require.NoError(t, err) for _, eb := range tc.expectedBalances { @@ -408,7 +408,7 @@ func TestMsgMultiSendDependent(t *testing.T) { for _, tc := range testCases { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) require.NoError(t, err) for _, eb := range tc.expectedBalances { diff --git a/x/bank/types/query.pb.gw.go b/x/bank/types/query.pb.gw.go index ca9c1ddbfa6c..85d776791747 100644 --- a/x/bank/types/query.pb.gw.go +++ b/x/bank/types/query.pb.gw.go @@ -272,7 +272,6 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Balance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/distribution/types/query.pb.gw.go b/x/distribution/types/query.pb.gw.go index 890e51e40d1e..b3a714f34904 100644 --- a/x/distribution/types/query.pb.gw.go +++ b/x/distribution/types/query.pb.gw.go @@ -488,7 +488,6 @@ func local_request_Query_CommunityPool_0(ctx context.Context, marshaler runtime. // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/evidence/types/query.pb.gw.go b/x/evidence/types/query.pb.gw.go index e15d163524b4..e9346629c24d 100644 --- a/x/evidence/types/query.pb.gw.go +++ b/x/evidence/types/query.pb.gw.go @@ -124,7 +124,6 @@ func local_request_Query_AllEvidence_0(ctx context.Context, marshaler runtime.Ma // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Evidence_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/gov/types/query.pb.gw.go b/x/gov/types/query.pb.gw.go index e9fc72f302b2..4e2c61f5f8c0 100644 --- a/x/gov/types/query.pb.gw.go +++ b/x/gov/types/query.pb.gw.go @@ -528,7 +528,6 @@ func local_request_Query_TallyResult_0(ctx context.Context, marshaler runtime.Ma // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Proposal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/ibc-transfer/types/query.pb.gw.go b/x/ibc-transfer/types/query.pb.gw.go index 23b91aafc162..9d5093a47f15 100644 --- a/x/ibc-transfer/types/query.pb.gw.go +++ b/x/ibc-transfer/types/query.pb.gw.go @@ -142,7 +142,6 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_DenomTrace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index fe4b2d64eb6f..81c2a67d9900 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -32,6 +32,16 @@ type ClientState interface { // State verification functions + VerifyClientState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + root commitmentexported.Root, + height uint64, + prefix commitmentexported.Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState ClientState, + ) error VerifyClientConsensusState( store sdk.KVStore, cdc codec.BinaryMarshaler, diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 909257210553..fac2b3735159 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -2,14 +2,17 @@ package keeper import ( "fmt" + "reflect" "strings" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/light" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" @@ -197,6 +200,52 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height uint64) (exported. return consensusState, true } +// ValidateSelfClient validates the client parameters for a client of the running chain +// This function is only used to validate the client state the counterparty stores for this chain +func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { + tmClient, ok := clientState.(*ibctmtypes.ClientState) + if !ok { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", + &ibctmtypes.ClientState{}, tmClient) + } + + if clientState.IsFrozen() { + return types.ErrClientFrozen + } + + if ctx.ChainID() != tmClient.ChainId { + return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid chain-id. expected: %s, got: %s", + ctx.ChainID(), tmClient.ChainId) + } + + if tmClient.LatestHeight > uint64(ctx.BlockHeight()) { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than chain height %d", + tmClient.LatestHeight, ctx.BlockHeight()) + } + + expectedProofSpecs := commitmenttypes.GetSDKSpecs() + if !reflect.DeepEqual(expectedProofSpecs, tmClient.ProofSpecs) { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client has invalid proof specs. expected: %v got: %v", + expectedProofSpecs, tmClient.ProofSpecs) + } + + if err := light.ValidateTrustLevel(tmClient.TrustLevel.ToTendermint()); err != nil { + return sdkerrors.Wrapf(types.ErrInvalidClient, "trust-level invalid: %v", err) + } + + expectedUbdPeriod := k.stakingKeeper.UnbondingTime(ctx) + if expectedUbdPeriod != tmClient.UnbondingPeriod { + return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid unbonding period. expected: %s, got: %s", + expectedUbdPeriod, tmClient.UnbondingPeriod) + } + + if tmClient.UnbondingPeriod < tmClient.TrustingPeriod { + return sdkerrors.Wrapf(types.ErrInvalidClient, "unbonding period must be greater than trusting period. unbonding period (%d) < trusting period (%d)", + tmClient.UnbondingPeriod, tmClient.TrustingPeriod) + } + return nil +} + // IterateClients provides an iterator over all stored light client State // objects. For each State object, cb will be called. If the cb returns true, // the iterator will close and stop. diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index e9fec73e17aa..a5a60d5fae89 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -15,9 +15,11 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -124,6 +126,72 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") } +func (suite *KeeperTestSuite) TestValidateSelfClient() { + testCases := []struct { + name string + clientState clientexported.ClientState + expPass bool + }{ + { + "success", + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight), commitmenttypes.GetSDKSpecs()), + true, + }, + { + "invalid client type", + localhosttypes.NewClientState(testChainID, testClientHeight), + false, + }, + { + "frozen client", + ibctmtypes.ClientState{testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight), uint64(testClientHeight), commitmenttypes.GetSDKSpecs()}, + false, + }, + { + "incorrect chainID", + ibctmtypes.NewClientState("gaiatestnet", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight), commitmenttypes.GetSDKSpecs()), + false, + }, + { + "invalid client height", + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight)+10, commitmenttypes.GetSDKSpecs()), + false, + }, + { + "invalid proof specs", + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight), nil), + false, + }, + { + "invalid trust level", + ibctmtypes.NewClientState(testChainID, ibctmtypes.Fraction{0, 1}, trustingPeriod, ubdPeriod, maxClockDrift, uint64(testClientHeight), commitmenttypes.GetSDKSpecs()), + false, + }, + { + "invalid unbonding period", + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, uint64(testClientHeight), commitmenttypes.GetSDKSpecs()), + false, + }, + { + "invalid trusting period", + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, uint64(testClientHeight), commitmenttypes.GetSDKSpecs()), + false, + }, + } + + ctx := suite.ctx.WithChainID(testChainID) + ctx = ctx.WithBlockHeight(testClientHeight) + + for _, tc := range testCases { + err := suite.keeper.ValidateSelfClient(ctx, tc.clientState) + if tc.expPass { + suite.Require().NoError(err, "expected valid client for case: %s", tc.name) + } else { + suite.Require().Error(err, "expected invalid client for case: %s", tc.name) + } + } +} + func (suite KeeperTestSuite) TestGetAllClients() { clientIDs := []string{ testClientID2, testClientID3, testClientID, diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index 9b7a793267f5..8990b2ffcb25 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -70,6 +70,16 @@ func PackClientState(clientState exported.ClientState) (*codectypes.Any, error) return anyClientState, nil } +// MustPackClientState calls PackClientState and panics on error. +func MustPackClientState(clientState exported.ClientState) *codectypes.Any { + anyClientState, err := PackClientState(clientState) + if err != nil { + panic(err) + } + + return anyClientState +} + // UnpackClientState unpacks an Any into a ClientState. It returns an error if the // client state can't be unpacked into a ClientState. func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) { diff --git a/x/ibc/02-client/types/codec_test.go b/x/ibc/02-client/types/codec_test.go new file mode 100644 index 000000000000..0264f26d2fba --- /dev/null +++ b/x/ibc/02-client/types/codec_test.go @@ -0,0 +1,42 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" + "github.com/stretchr/testify/require" +) + +func TestPackClientState(t *testing.T) { + clientState := localhosttypes.NewClientState(chainID, height) + + clientAny, err := types.PackClientState(clientState) + require.NoError(t, err, "pack clientstate should not return error") + + cs, err := types.UnpackClientState(clientAny) + require.NoError(t, err, "unpack clientstate should not return error") + + require.Equal(t, clientState, cs, "client states are not equal after packing and unpacking") + + _, err = types.PackClientState(nil) + require.Error(t, err, "did not error after packing nil") +} + +func TestPackConsensusState(t *testing.T) { + consensusState := ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), height, []byte("nextvalshash")) + + consensusAny, err := types.PackConsensusState(consensusState) + require.NoError(t, err, "pack consensusstate should not return error") + + cs, err := types.UnpackConsensusState(consensusAny) + require.NoError(t, err, "unpack consensusstate should not return error") + + require.Equal(t, consensusState, cs, "consensus states are not equal after packing and unpacking") + + _, err = types.PackConsensusState(nil) + require.Error(t, err, "did not error after packing nil") +} diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 4efd701212e4..b51fd25063ad 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -7,21 +7,23 @@ import ( // IBC client sentinel errors var ( ErrClientExists = sdkerrors.Register(SubModuleName, 2, "light client already exists") - ErrClientNotFound = sdkerrors.Register(SubModuleName, 3, "light client not found") - ErrClientFrozen = sdkerrors.Register(SubModuleName, 4, "light client is frozen due to misbehaviour") - ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 5, "consensus state not found") - ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 6, "invalid consensus state") - ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 7, "client type not found") - ErrInvalidClientType = sdkerrors.Register(SubModuleName, 8, "invalid client type") - ErrRootNotFound = sdkerrors.Register(SubModuleName, 9, "commitment root not found") - ErrInvalidHeader = sdkerrors.Register(SubModuleName, 10, "invalid client header") - ErrInvalidEvidence = sdkerrors.Register(SubModuleName, 11, "invalid light client misbehaviour evidence") - ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 12, "client consensus state verification failed") - ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 13, "connection state verification failed") - ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 14, "channel state verification failed") - ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 15, "packet commitment verification failed") - ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 16, "packet acknowledgement verification failed") - ErrFailedPacketAckAbsenceVerification = sdkerrors.Register(SubModuleName, 17, "packet acknowledgement absence verification failed") - ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 18, "next sequence receive verification failed") - ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 19, "self consensus state not found") + ErrInvalidClient = sdkerrors.Register(SubModuleName, 3, "light client is invalid") + ErrClientNotFound = sdkerrors.Register(SubModuleName, 4, "light client not found") + ErrClientFrozen = sdkerrors.Register(SubModuleName, 5, "light client is frozen due to misbehaviour") + ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 6, "consensus state not found") + ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 7, "invalid consensus state") + ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 8, "client type not found") + ErrInvalidClientType = sdkerrors.Register(SubModuleName, 9, "invalid client type") + ErrRootNotFound = sdkerrors.Register(SubModuleName, 10, "commitment root not found") + ErrInvalidHeader = sdkerrors.Register(SubModuleName, 11, "invalid client header") + ErrInvalidEvidence = sdkerrors.Register(SubModuleName, 12, "invalid light client misbehaviour evidence") + ErrFailedClientStateVerification = sdkerrors.Register(SubModuleName, 13, "client state verification failed") + ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 14, "client consensus state verification failed") + ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 15, "connection state verification failed") + ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 16, "channel state verification failed") + ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 17, "packet commitment verification failed") + ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 18, "packet acknowledgement verification failed") + ErrFailedPacketAckAbsenceVerification = sdkerrors.Register(SubModuleName, 19, "packet acknowledgement absence verification failed") + ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 20, "next sequence receive verification failed") + ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 21, "self consensus state not found") ) diff --git a/x/ibc/03-connection/client/cli/tx.go b/x/ibc/03-connection/client/cli/tx.go index c69e3828876b..160531446743 100644 --- a/x/ibc/03-connection/client/cli/tx.go +++ b/x/ibc/03-connection/client/cli/tx.go @@ -67,17 +67,17 @@ func NewConnectionOpenInitCmd() *cobra.Command { func NewConnectionOpenTryCmd() *cobra.Command { cmd := &cobra.Command{ Use: strings.TrimSpace(`open-try [connection-id] [client-id] -[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] -[counterparty-versions] [path/to/proof_init.json] [path/to/proof_consensus.json]`), +[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] [path/to/client_state.json] +[counterparty-versions] [path/to/proof_init.json] [path/to/proof_client.json] [path/to/proof_consensus.json]`), Short: "initiate connection handshake between two chains", Long: "Initialize a connection on chain A with a given counterparty chain B", Example: fmt.Sprintf( `%s tx %s %s open-try connection-id] [client-id] \ -[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] \ -[counterparty-versions] [path/to/proof_init.json] [path/tp/proof_consensus.json]`, +[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] [path/to/client_state.json]\ +[counterparty-versions] [path/to/proof_init.json] [path/to/proof_client.json] [path/tp/proof_consensus.json]`, version.AppName, host.ModuleName, types.SubModuleName, ), - Args: cobra.ExactArgs(8), + Args: cobra.ExactArgs(10), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) @@ -95,15 +95,25 @@ func NewConnectionOpenTryCmd() *cobra.Command { return err } + counterpartyClient, err := utils.ParseClientState(clientCtx.LegacyAmino, args[5]) + if err != nil { + return err + } + // TODO: parse strings? - counterpartyVersions := args[5] + counterpartyVersions := args[6] + + proofInit, err := utils.ParseProof(clientCtx.LegacyAmino, args[7]) + if err != nil { + return err + } - proofInit, err := utils.ParseProof(clientCtx.LegacyAmino, args[6]) + proofClient, err := utils.ParseProof(clientCtx.LegacyAmino, args[8]) if err != nil { return err } - proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[7]) + proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[9]) if err != nil { return err } @@ -116,7 +126,8 @@ func NewConnectionOpenTryCmd() *cobra.Command { msg := types.NewMsgConnectionOpenTry( connectionID, clientID, counterpartyConnectionID, counterpartyClientID, - counterpartyPrefix, []string{counterpartyVersions}, proofInit, proofConsensus, proofHeight, + counterpartyClient, counterpartyPrefix, []string{counterpartyVersions}, + proofInit, proofClient, proofConsensus, proofHeight, consensusHeight, clientCtx.GetFromAddress(), ) @@ -137,14 +148,14 @@ func NewConnectionOpenTryCmd() *cobra.Command { // connection open attempt from chain B to chain A func NewConnectionOpenAckCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "open-ack [connection-id] [path/to/proof_try.json] [path/to/proof_consensus.json] [version]", + Use: "open-ack [connection-id] [path/to/client_state.json] [path/to/proof_try.json] [path/to/proof_client.json] [path/to/proof_consensus.json] [version]", Short: "relay the acceptance of a connection open attempt", Long: "Relay the acceptance of a connection open attempt from chain B to chain A", Example: fmt.Sprintf( - "%s tx %s %s open-ack [connection-id] [path/to/proof_try.json] [path/to/proof_consensus.json] [version]", + "%s tx %s %s open-ack [connection-id] [path/to/client_state.json] [path/to/proof_try.json] [path/to/proof_client.json] [path/to/proof_consensus.json] [version]", version.AppName, host.ModuleName, types.SubModuleName, ), - Args: cobra.ExactArgs(4), + Args: cobra.ExactArgs(6), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) @@ -154,12 +165,22 @@ func NewConnectionOpenAckCmd() *cobra.Command { connectionID := args[0] - proofTry, err := utils.ParseProof(clientCtx.LegacyAmino, args[1]) + counterpartyClient, err := utils.ParseClientState(clientCtx.LegacyAmino, args[1]) + if err != nil { + return err + } + + proofTry, err := utils.ParseProof(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + proofClient, err := utils.ParseProof(clientCtx.LegacyAmino, args[3]) if err != nil { return err } - proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[2]) + proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[4]) if err != nil { return err } @@ -170,10 +191,10 @@ func NewConnectionOpenAckCmd() *cobra.Command { return err } - version := args[3] + version := args[5] msg := types.NewMsgConnectionOpenAck( - connectionID, proofTry, proofConsensus, proofHeight, + connectionID, counterpartyClient, proofTry, proofClient, proofConsensus, proofHeight, consensusHeight, version, clientCtx.GetFromAddress(), ) diff --git a/x/ibc/03-connection/client/utils/utils.go b/x/ibc/03-connection/client/utils/utils.go index 9ae37884082d..2b13f2c6c834 100644 --- a/x/ibc/03-connection/client/utils/utils.go +++ b/x/ibc/03-connection/client/utils/utils.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" clientutils "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" @@ -174,6 +175,23 @@ func QueryConnectionConsensusState( return res, nil } +// ParseClientState unmarshals a cmd input argument from a JSON string to a client state +// If the input is not a JSON, it looks for a path to the JSON file +func ParseClientState(cdc *codec.LegacyAmino, arg string) (clientexported.ClientState, error) { + var clientState clientexported.ClientState + if err := cdc.UnmarshalJSON([]byte(arg), &clientState); err != nil { + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(arg) + if err != nil { + return nil, errors.New("either JSON input nor path to .json file were provided") + } + if err := cdc.UnmarshalJSON(contents, &clientState); err != nil { + return nil, errors.Wrap(err, "error unmarshalling client state") + } + } + return clientState, nil +} + // ParsePrefix unmarshals an cmd input argument from a JSON string to a commitment // Prefix. If the input is not a JSON, it looks for a path to the JSON file. func ParsePrefix(cdc *codec.LegacyAmino, arg string) (commitmenttypes.MerklePrefix, error) { diff --git a/x/ibc/03-connection/handler.go b/x/ibc/03-connection/handler.go index 7c1cf08bf014..4ce2932f1b92 100644 --- a/x/ibc/03-connection/handler.go +++ b/x/ibc/03-connection/handler.go @@ -3,6 +3,7 @@ package connection import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" ) @@ -36,9 +37,14 @@ func HandleMsgConnectionOpenInit(ctx sdk.Context, k keeper.Keeper, msg *types.Ms // HandleMsgConnectionOpenTry defines the sdk.Handler for MsgConnectionOpenTry func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg *types.MsgConnectionOpenTry) (*sdk.Result, error) { + targetClient, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, sdkerrors.Wrapf(err, "client in msg is not exported.ClientState. invalid client: %v.", targetClient) + } + if err := k.ConnOpenTry( - ctx, msg.ConnectionId, msg.Counterparty, msg.ClientId, - msg.CounterpartyVersions, msg.ProofInit, msg.ProofConsensus, + ctx, msg.ConnectionId, msg.Counterparty, msg.ClientId, targetClient, + msg.CounterpartyVersions, msg.ProofInit, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { return nil, sdkerrors.Wrap(err, "connection handshake open try failed") @@ -65,8 +71,14 @@ func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg *types.Msg // HandleMsgConnectionOpenAck defines the sdk.Handler for MsgConnectionOpenAck func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg *types.MsgConnectionOpenAck) (*sdk.Result, error) { + targetClient, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, sdkerrors.Wrapf(err, "client in msg is not exported.ClientState. invalid client: %v", targetClient) + } + if err := k.ConnOpenAck( - ctx, msg.ConnectionId, msg.Version, msg.ProofTry, msg.ProofConsensus, + ctx, msg.ConnectionId, targetClient, msg.Version, + msg.ProofTry, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { return nil, sdkerrors.Wrap(err, "connection handshake open ack failed") diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index c9c8723b1de9..e48a96719ada 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" @@ -48,8 +49,10 @@ func (k Keeper) ConnOpenTry( connectionID string, // desiredIdentifier counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier clientID string, // clientID of chainA + clientState clientexported.ClientState, // clientState that chainA has for chainB counterpartyVersions []string, // supported versions of chain A proofInit []byte, // proof that chainA stored connectionEnd in state (on ConnOpenInit) + proofClient []byte, // proof that chainA stored a light client of chainB proofConsensus []byte, // proof that chainA stored chainB's consensus state at consensus height proofHeight uint64, // height at which relayer constructs proof of A storing connectionEnd in state consensusHeight uint64, // latest height of chain B which chain A has stored in its chain B client @@ -61,6 +64,11 @@ func (k Keeper) ConnOpenTry( ) } + // validate client parameters of a chainB client stored on chainA + if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { + return err + } + expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) if !found { return clienttypes.ErrSelfConsensusStateNotFound @@ -90,6 +98,11 @@ func (k Keeper) ConnOpenTry( return err } + // Check that ChainA stored the clientState provided in the msg + if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { + return err + } + // Check that ChainA stored the correct ConsensusState of chainB at the given consensusHeight if err := k.VerifyClientConsensusState( ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, @@ -128,8 +141,10 @@ func (k Keeper) ConnOpenTry( func (k Keeper) ConnOpenAck( ctx sdk.Context, connectionID string, + clientState clientexported.ClientState, // client state for chainA on chainB encodedVersion string, // version that ChainB chose in ConnOpenTry proofTry []byte, // proof that connectionEnd was added to ChainB state in ConnOpenTry + proofClient []byte, // proof of client state on chainB for chainA proofConsensus []byte, // proof that chainB has stored ConsensusState of chainA on its client proofHeight uint64, // height that relayer constructed proofTry consensusHeight uint64, // latest height of chainA that chainB has stored on its chainA client @@ -175,6 +190,11 @@ func (k Keeper) ConnOpenAck( return err } + // validate client parameters of a chainA client stored on chainB + if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { + return err + } + // Retrieve chainA's consensus state at consensusheight expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) if !found { @@ -193,6 +213,11 @@ func (k Keeper) ConnOpenAck( return err } + // Check that ChainB stored the clientState provided in the msg + if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { + return err + } + // Ensure that ChainB has stored the correct ConsensusState for chainA at the consensusHeight if err := k.VerifyClientConsensusState( ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index ff250e4bbbd7..8df856ffc149 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -60,10 +60,11 @@ func (suite *KeeperTestSuite) TestConnOpenInit() { // connection on chainA is INIT func (suite *KeeperTestSuite) TestConnOpenTry() { var ( - clientA string - clientB string - versions []string - consensusHeight uint64 + clientA string + clientB string + versions []string + consensusHeight uint64 + counterpartyClient clientexported.ClientState ) testCases := []struct { @@ -75,12 +76,33 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) }, true}, + {"invalid counterparty client", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + // Set an invalid client of chainA on chainB + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.ChainId = "wrongchainid" + + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClient) + }, false}, {"consensus height >= latest height", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + consensusHeight = uint64(suite.chainB.GetContext().BlockHeight()) }, false}, {"self consensus state not found", func() { @@ -88,6 +110,9 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + consensusHeight = 1 }, false}, {"counterparty versions is empty", func() { @@ -95,6 +120,9 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + versions = nil }, false}, {"counterparty versions don't have a match", func() { @@ -102,6 +130,9 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + version, err := types.NewVersion("0.0", nil).Encode() suite.Require().NoError(err) versions = []string{version} @@ -109,9 +140,29 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { {"connection state verification failed", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) // chainA connection not created + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + }, false}, + {"client state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + // modify counterparty client without setting in store so it still passes validate but fails proof verification + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.LatestHeight++ }, false}, {"consensus state verification failed", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + // give chainA wrong consensus state for chainB consState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainA.GetContext(), clientA) suite.Require().True(found) @@ -128,6 +179,9 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { {"invalid previous connection is in TRYOPEN", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + // open init chainA connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) @@ -165,9 +219,13 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { consensusKey := host.FullKeyClientPath(clientA, host.KeyConsensusState(consensusHeight)) proofConsensus, _ := suite.chainA.QueryProof(consensusKey) + // retrieve proof of counterparty clientstate on chainA + clientKey := host.FullKeyClientPath(clientA, host.KeyClientState()) + proofClient, _ := suite.chainA.QueryProof(clientKey) + err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenTry( - suite.chainB.GetContext(), connB.ID, counterparty, clientB, - versions, proofInit, proofConsensus, + suite.chainB.GetContext(), connB.ID, counterparty, clientB, counterpartyClient, + versions, proofInit, proofClient, proofConsensus, proofHeight, consensusHeight, ) @@ -184,10 +242,11 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { // the initialization (TRYINIT) of the connection on Chain B (ID #2). func (suite *KeeperTestSuite) TestConnOpenAck() { var ( - clientA string - clientB string - consensusHeight uint64 - version string + clientA string + clientB string + consensusHeight uint64 + version string + counterpartyClient clientexported.ClientState ) testCases := []struct { @@ -200,6 +259,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) }, true}, @@ -220,12 +282,36 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, clientexported.Tendermint) suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) }, true}, + {"invalid counterparty client", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + // Set an invalid client of chainA on chainB + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.ChainId = "wrongchainid" + + suite.chainB.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainB.GetContext(), clientB, tmClient) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + }, false}, {"consensus height >= latest height", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -234,6 +320,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { {"connection not found", func() { // connections are never created clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) }, false}, {"connection state is not INIT", func() { // connection state is already OPEN on chainA @@ -241,6 +330,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -252,6 +344,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -263,6 +358,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -273,6 +371,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -284,6 +385,9 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) suite.Require().NoError(err) @@ -294,12 +398,34 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + }, false}, + {"client state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + // modify counterparty client without setting in store so it still passes validate but fails proof verification + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.LatestHeight++ + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) }, false}, {"consensus state verification failed", func() { clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) suite.Require().NoError(err) + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + // give chainB wrong consensus state for chainA consState, found := suite.chainB.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), clientB) suite.Require().True(found) @@ -340,9 +466,13 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { consensusKey := host.FullKeyClientPath(clientB, host.KeyConsensusState(consensusHeight)) proofConsensus, _ := suite.chainB.QueryProof(consensusKey) + // retrieve proof of counterparty clientstate on chainA + clientKey := host.FullKeyClientPath(clientB, host.KeyClientState()) + proofClient, _ := suite.chainB.QueryProof(clientKey) + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenAck( - suite.chainA.GetContext(), connA.ID, version, - proofTry, proofConsensus, proofHeight, consensusHeight, + suite.chainA.GetContext(), connA.ID, counterpartyClient, version, + proofTry, proofClient, proofConsensus, proofHeight, consensusHeight, ) if tc.expPass { diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 916e53918172..a4f4a222662e 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -9,6 +9,35 @@ import ( channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" ) +// VerifyClientState verifies a proof of a client state of the running machine +// stored on the target machine +func (k Keeper) VerifyClientState( + ctx sdk.Context, + connection exported.ConnectionI, + height uint64, + proof []byte, + clientState clientexported.ClientState, +) error { + clientID := connection.GetClientID() + targetClient, found := k.clientKeeper.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + } + + targetConsState, found := k.clientKeeper.GetClientConsensusState(ctx, clientID, height) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "clientID: %s with height: %d", clientID, height) + } + + if err := targetClient.VerifyClientState( + k.clientKeeper.ClientStore(ctx, clientID), k.cdc, targetConsState.GetRoot(), height, + connection.GetCounterparty().GetPrefix(), connection.GetCounterparty().GetClientID(), proof, clientState); err != nil { + return sdkerrors.Wrapf(err, "failed client state verification for target client: %s", connection.GetClientID()) + } + + return nil +} + // VerifyClientConsensusState verifies a proof of the consensus state of the // specified client stored on the target machine. func (k Keeper) VerifyClientConsensusState( diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index a17fba4acb5b..304bf774829e 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -12,6 +12,57 @@ import ( ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) +// TestVerifyClientState verifies a client state of chainA +// stored on clientB (which is on chainB) +func (suite *KeeperTestSuite) TestVerifyClientState() { + cases := []struct { + msg string + changeClientID bool + heightDiff uint64 + malleateCounterparty bool + expPass bool + }{ + {"verification success", false, 0, false, true}, + {"client state not found", true, 0, false, false}, + {"consensus state for proof height not found", false, 5, false, false}, + {"verification failed", false, 0, true, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + _, clientB, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + + counterpartyClient, clientProof := suite.chainB.QueryClientStateProof(clientB) + proofHeight := uint64(suite.chainB.GetContext().BlockHeight() - 1) + + if tc.malleateCounterparty { + tmClient, _ := counterpartyClient.(*ibctmtypes.ClientState) + tmClient.ChainId = "wrongChainID" + } + + connection := suite.chainA.GetConnection(connA) + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyClientState( + suite.chainA.GetContext(), connection, + proofHeight+tc.heightDiff, clientProof, counterpartyClient, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + // TestVerifyClientConsensusState verifies that the consensus state of // chainA stored on clientB (which is on chainB) matches the consensus // state for chainA at that height. @@ -74,7 +125,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { } proof, consensusHeight := suite.chainB.QueryConsensusStateProof(connB.ClientID) - proofHeight := uint64(suite.chainA.GetContext().BlockHeight() - 1) + proofHeight := uint64(suite.chainB.GetContext().BlockHeight() - 1) consensusState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetSelfConsensusState(suite.chainA.GetContext(), consensusHeight) suite.Require().True(found) diff --git a/x/ibc/03-connection/types/connection.pb.go b/x/ibc/03-connection/types/connection.pb.go index 6fb05f404c9c..023066b0cd87 100644 --- a/x/ibc/03-connection/types/connection.pb.go +++ b/x/ibc/03-connection/types/connection.pb.go @@ -5,8 +5,9 @@ package types import ( fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" - types "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -138,16 +139,19 @@ func (m *MsgConnectionOpenInit) GetSigner() github_com_cosmos_cosmos_sdk_types.A type MsgConnectionOpenTry struct { ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` - Counterparty Counterparty `protobuf:"bytes,3,opt,name=counterparty,proto3" json:"counterparty"` - CounterpartyVersions []string `protobuf:"bytes,4,rep,name=counterparty_versions,json=counterpartyVersions,proto3" json:"counterparty_versions,omitempty" yaml:"counterparty_versions"` + ClientState *types.Any `protobuf:"bytes,3,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + Counterparty Counterparty `protobuf:"bytes,4,opt,name=counterparty,proto3" json:"counterparty"` + CounterpartyVersions []string `protobuf:"bytes,5,rep,name=counterparty_versions,json=counterpartyVersions,proto3" json:"counterparty_versions,omitempty" yaml:"counterparty_versions"` + ProofHeight uint64 `protobuf:"varint,6,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height,omitempty" yaml:"proof_height"` // proof of the initialization the connection on Chain A: `UNITIALIZED -> // INIT` - ProofInit []byte `protobuf:"bytes,5,opt,name=proof_init,json=proofInit,proto3" json:"proof_init,omitempty" yaml:"proof_init"` - ProofHeight uint64 `protobuf:"varint,6,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height,omitempty"` + ProofInit []byte `protobuf:"bytes,7,opt,name=proof_init,json=proofInit,proto3" json:"proof_init,omitempty" yaml:"proof_init"` + // proof of client state included in message + ProofClient []byte `protobuf:"bytes,8,opt,name=proof_client,json=proofClient,proto3" json:"proof_client,omitempty" yaml:"proof_client"` // proof of client consensus state - ProofConsensus []byte `protobuf:"bytes,7,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` - ConsensusHeight uint64 `protobuf:"varint,8,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height,omitempty" yaml:"consensus_height"` - Signer github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,9,opt,name=signer,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"signer,omitempty"` + ProofConsensus []byte `protobuf:"bytes,9,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` + ConsensusHeight uint64 `protobuf:"varint,10,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height,omitempty" yaml:"consensus_height"` + Signer github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,11,opt,name=signer,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"signer,omitempty"` } func (m *MsgConnectionOpenTry) Reset() { *m = MsgConnectionOpenTry{} } @@ -197,6 +201,13 @@ func (m *MsgConnectionOpenTry) GetConnectionId() string { return "" } +func (m *MsgConnectionOpenTry) GetClientState() *types.Any { + if m != nil { + return m.ClientState + } + return nil +} + func (m *MsgConnectionOpenTry) GetCounterparty() Counterparty { if m != nil { return m.Counterparty @@ -211,6 +222,13 @@ func (m *MsgConnectionOpenTry) GetCounterpartyVersions() []string { return nil } +func (m *MsgConnectionOpenTry) GetProofHeight() uint64 { + if m != nil { + return m.ProofHeight + } + return 0 +} + func (m *MsgConnectionOpenTry) GetProofInit() []byte { if m != nil { return m.ProofInit @@ -218,11 +236,11 @@ func (m *MsgConnectionOpenTry) GetProofInit() []byte { return nil } -func (m *MsgConnectionOpenTry) GetProofHeight() uint64 { +func (m *MsgConnectionOpenTry) GetProofClient() []byte { if m != nil { - return m.ProofHeight + return m.ProofClient } - return 0 + return nil } func (m *MsgConnectionOpenTry) GetProofConsensus() []byte { @@ -249,16 +267,19 @@ func (m *MsgConnectionOpenTry) GetSigner() github_com_cosmos_cosmos_sdk_types.Ac // MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to // acknowledge the change of connection state to TRYOPEN on Chain B. type MsgConnectionOpenAck struct { - ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + ClientState *types.Any `protobuf:"bytes,3,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + ProofHeight uint64 `protobuf:"varint,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height,omitempty" yaml:"proof_height"` // proof of the initialization the connection on Chain B: `UNITIALIZED -> // TRYOPEN` - ProofTry []byte `protobuf:"bytes,3,opt,name=proof_try,json=proofTry,proto3" json:"proof_try,omitempty" yaml:"proof_try"` - ProofHeight uint64 `protobuf:"varint,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height,omitempty" yaml:"proof_height"` + ProofTry []byte `protobuf:"bytes,5,opt,name=proof_try,json=proofTry,proto3" json:"proof_try,omitempty" yaml:"proof_try"` + // proof of client state included in message + ProofClient []byte `protobuf:"bytes,6,opt,name=proof_client,json=proofClient,proto3" json:"proof_client,omitempty" yaml:"proof_client"` // proof of client consensus state - ProofConsensus []byte `protobuf:"bytes,5,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` - ConsensusHeight uint64 `protobuf:"varint,6,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height,omitempty" yaml:"consensus_height"` - Signer github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,7,opt,name=signer,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"signer,omitempty"` + ProofConsensus []byte `protobuf:"bytes,7,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` + ConsensusHeight uint64 `protobuf:"varint,8,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height,omitempty" yaml:"consensus_height"` + Signer github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,9,opt,name=signer,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"signer,omitempty"` } func (m *MsgConnectionOpenAck) Reset() { *m = MsgConnectionOpenAck{} } @@ -308,9 +329,9 @@ func (m *MsgConnectionOpenAck) GetVersion() string { return "" } -func (m *MsgConnectionOpenAck) GetProofTry() []byte { +func (m *MsgConnectionOpenAck) GetClientState() *types.Any { if m != nil { - return m.ProofTry + return m.ClientState } return nil } @@ -322,6 +343,20 @@ func (m *MsgConnectionOpenAck) GetProofHeight() uint64 { return 0 } +func (m *MsgConnectionOpenAck) GetProofTry() []byte { + if m != nil { + return m.ProofTry + } + return nil +} + +func (m *MsgConnectionOpenAck) GetProofClient() []byte { + if m != nil { + return m.ProofClient + } + return nil +} + func (m *MsgConnectionOpenAck) GetProofConsensus() []byte { if m != nil { return m.ProofConsensus @@ -520,7 +555,7 @@ type Counterparty struct { // given connection. ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` // commitment merkle prefix of the counterparty chain - Prefix types.MerklePrefix `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix"` + Prefix types1.MerklePrefix `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix"` } func (m *Counterparty) Reset() { *m = Counterparty{} } @@ -716,65 +751,69 @@ func init() { func init() { proto.RegisterFile("ibc/connection/connection.proto", fileDescriptor_3bf62bacf5a27ee9) } var fileDescriptor_3bf62bacf5a27ee9 = []byte{ - // 919 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xcd, 0x6e, 0xe3, 0x54, - 0x14, 0x8e, 0x1d, 0xe7, 0xef, 0x34, 0x69, 0x33, 0x26, 0x65, 0xac, 0x30, 0xd8, 0xc6, 0x6c, 0x22, - 0x50, 0x13, 0x66, 0x06, 0xcd, 0xa2, 0x12, 0x8b, 0x24, 0x93, 0x0a, 0x0b, 0xa6, 0x13, 0xb9, 0x29, - 0x12, 0xdd, 0x44, 0xa9, 0x7d, 0x93, 0x5e, 0xa5, 0xb1, 0x23, 0xfb, 0x16, 0x4d, 0xdf, 0x60, 0xd4, - 0x15, 0x12, 0x2b, 0x24, 0x2a, 0x21, 0xf1, 0x0e, 0x3c, 0x00, 0xab, 0x59, 0xce, 0x72, 0xd8, 0x58, - 0xa8, 0x7d, 0x83, 0x2c, 0xd9, 0x80, 0x7c, 0xaf, 0xff, 0x9a, 0x16, 0x50, 0x9b, 0x91, 0xd0, 0xac, - 0x72, 0xfe, 0x7d, 0xcf, 0xf9, 0xbe, 0x9c, 0x7b, 0x41, 0xc1, 0x87, 0x66, 0xcb, 0x74, 0x6c, 0x1b, - 0x99, 0x04, 0x3b, 0x76, 0x4a, 0x6c, 0xce, 0x5d, 0x87, 0x38, 0xe2, 0x3a, 0x3e, 0x34, 0x9b, 0x89, - 0xb5, 0x5e, 0x9b, 0x38, 0x13, 0x87, 0xba, 0x5a, 0x81, 0xc4, 0xa2, 0xea, 0x61, 0x99, 0xd9, 0x0c, - 0x93, 0x19, 0xb2, 0x49, 0x4a, 0x64, 0x01, 0xda, 0x4f, 0x3c, 0x6c, 0x3e, 0xf3, 0x26, 0xdd, 0xb8, - 0xd0, 0xf3, 0x39, 0xb2, 0x75, 0x1b, 0x13, 0xf1, 0x21, 0x94, 0xcc, 0x63, 0x8c, 0x6c, 0x32, 0xc4, - 0x96, 0xc4, 0xa9, 0x5c, 0xa3, 0xd4, 0xa9, 0x2d, 0x7c, 0xa5, 0x7a, 0x3a, 0x9a, 0x1d, 0x6f, 0x6b, - 0xb1, 0x4b, 0x33, 0x8a, 0x4c, 0xd6, 0x2d, 0xf1, 0x0b, 0xa8, 0x24, 0x27, 0x0a, 0xd2, 0x78, 0x9a, - 0x26, 0x2d, 0x7c, 0xa5, 0x16, 0xa6, 0xa5, 0xdd, 0x9a, 0x51, 0x4e, 0x74, 0xdd, 0x12, 0x77, 0xa0, - 0x6c, 0x3a, 0x27, 0x36, 0x41, 0xee, 0x7c, 0xe4, 0x92, 0x53, 0x29, 0xab, 0x72, 0x8d, 0xb5, 0x47, - 0x0f, 0x9a, 0x57, 0x3b, 0x6d, 0x76, 0x53, 0x31, 0x1d, 0xe1, 0x95, 0xaf, 0x64, 0x8c, 0x2b, 0x79, - 0xa2, 0x0e, 0x79, 0x0f, 0x4f, 0x6c, 0xe4, 0x4a, 0x82, 0xca, 0x35, 0xca, 0x9d, 0x87, 0x7f, 0xfa, - 0xca, 0xd6, 0x04, 0x93, 0xa3, 0x93, 0xc3, 0xa6, 0xe9, 0xcc, 0x5a, 0xa6, 0xe3, 0xcd, 0x1c, 0x2f, - 0xfc, 0xd9, 0xf2, 0xac, 0x69, 0x8b, 0x9c, 0xce, 0x91, 0xd7, 0x6c, 0x9b, 0x66, 0xdb, 0xb2, 0x5c, - 0xe4, 0x79, 0x46, 0x58, 0x40, 0x7b, 0x23, 0x40, 0xed, 0xda, 0x78, 0x06, 0xee, 0xe9, 0x3b, 0x3c, - 0x9d, 0x7d, 0xd8, 0x4c, 0xeb, 0xc3, 0xef, 0x90, 0xeb, 0x61, 0xc7, 0xf6, 0x24, 0x41, 0xcd, 0x36, - 0x4a, 0x1d, 0x75, 0xe1, 0x2b, 0x0f, 0xa2, 0xe3, 0xdc, 0x10, 0xa6, 0x19, 0xb5, 0xb4, 0xfd, 0x9b, - 0xd0, 0x2c, 0x7e, 0x0e, 0x30, 0x77, 0x1d, 0x67, 0x3c, 0xc4, 0x36, 0x26, 0x52, 0x8e, 0x0e, 0x7e, - 0x73, 0xe1, 0x2b, 0xf7, 0x58, 0xad, 0xc4, 0xa7, 0x19, 0x25, 0xaa, 0x50, 0x92, 0x7d, 0x04, 0x65, - 0xe6, 0x39, 0x42, 0x78, 0x72, 0x44, 0xa4, 0xbc, 0xca, 0x35, 0x04, 0x63, 0x8d, 0xda, 0xbe, 0xa4, - 0x26, 0xb1, 0x0b, 0x1b, 0x2c, 0xc4, 0x74, 0x6c, 0x0f, 0xd9, 0xde, 0x89, 0x27, 0x15, 0x68, 0xf5, - 0xfa, 0xc2, 0x57, 0xde, 0x4f, 0x57, 0x8f, 0x03, 0x34, 0x63, 0x9d, 0x5a, 0xba, 0x91, 0x41, 0xdc, - 0x81, 0x6a, 0xec, 0x8d, 0xbe, 0x55, 0x0c, 0xbe, 0xd5, 0xf9, 0x60, 0xe1, 0x2b, 0xf7, 0xe3, 0xf1, - 0x5f, 0x89, 0xd0, 0x8c, 0x8d, 0xd8, 0x14, 0x1e, 0x26, 0xa1, 0x56, 0x69, 0x55, 0x6a, 0xfd, 0x9a, - 0xbd, 0x81, 0x5a, 0x6d, 0x73, 0x7a, 0x9d, 0x27, 0xdc, 0xad, 0x78, 0x22, 0x41, 0x21, 0xc4, 0x8a, - 0x11, 0xcc, 0x88, 0xd4, 0x80, 0xb3, 0x6c, 0x50, 0xc4, 0x65, 0xf4, 0x29, 0xa7, 0x39, 0x1b, 0xbb, - 0x34, 0xa3, 0x48, 0xe5, 0x80, 0xe6, 0xdb, 0x4b, 0xf8, 0x08, 0x74, 0x66, 0xf7, 0x17, 0xbe, 0xf2, - 0x5e, 0x3a, 0x2b, 0x9a, 0xd7, 0x7f, 0x01, 0x97, 0x7b, 0x2b, 0xc0, 0xe5, 0x57, 0x02, 0xae, 0xb0, - 0x2a, 0x70, 0x3f, 0xf0, 0x20, 0x5d, 0x03, 0xae, 0xeb, 0xd8, 0x63, 0xec, 0xce, 0x56, 0x05, 0x2f, - 0x86, 0x68, 0x64, 0x4e, 0x29, 0x7c, 0x37, 0x40, 0x34, 0x32, 0xa7, 0x11, 0x44, 0x01, 0x5d, 0x96, - 0x21, 0xca, 0xde, 0x02, 0xa2, 0xb7, 0xb8, 0x29, 0x7f, 0xe7, 0xa0, 0x92, 0x8c, 0xa4, 0x67, 0x5b, - 0x77, 0x59, 0x91, 0x75, 0x28, 0xc6, 0xeb, 0x88, 0x0f, 0xd6, 0x91, 0x11, 0xeb, 0xe2, 0xa7, 0x90, - 0xf3, 0xc8, 0x88, 0x20, 0xda, 0xe0, 0xfa, 0xa3, 0xcd, 0xe5, 0xc5, 0xb7, 0x17, 0x38, 0x0d, 0x16, - 0x73, 0x6d, 0x59, 0x0a, 0x77, 0x5b, 0x96, 0xdb, 0xc2, 0xcb, 0x9f, 0x95, 0x8c, 0xf6, 0x17, 0x07, - 0x35, 0xdd, 0x42, 0x36, 0xc1, 0x63, 0x8c, 0xac, 0xa4, 0x4b, 0xf1, 0x43, 0xe0, 0xe3, 0xde, 0x2a, - 0x0b, 0x5f, 0x29, 0xb1, 0xde, 0x82, 0xa6, 0x78, 0xbc, 0x34, 0x01, 0xfe, 0xd6, 0x13, 0xc8, 0xfe, - 0xd3, 0x04, 0x84, 0x3b, 0x4c, 0x20, 0xb7, 0xd2, 0x04, 0x7e, 0xe3, 0xa0, 0x9c, 0x0e, 0xfd, 0x1f, - 0xee, 0xbf, 0x6d, 0xc8, 0xcf, 0x5d, 0x34, 0xc6, 0x2f, 0x96, 0x6e, 0xbe, 0xf8, 0x41, 0xf3, 0x0c, - 0xb9, 0xd3, 0x63, 0xd4, 0xa7, 0x31, 0x61, 0x2b, 0x61, 0x46, 0xd8, 0xc4, 0xc7, 0xb0, 0xd6, 0xa5, - 0x87, 0xe9, 0x8f, 0xc8, 0x91, 0x27, 0xd6, 0x20, 0x37, 0x0f, 0x04, 0x89, 0xa3, 0x73, 0x66, 0x8a, - 0x76, 0x00, 0x1b, 0x09, 0xc0, 0x2c, 0xf0, 0x0e, 0xbd, 0xc6, 0xb5, 0xf9, 0x74, 0xed, 0xaf, 0xa0, - 0x10, 0xde, 0x97, 0xa2, 0x0c, 0x80, 0x23, 0x46, 0xb9, 0xac, 0xa8, 0x91, 0xb2, 0x04, 0x3c, 0x18, - 0xa3, 0x11, 0x39, 0x71, 0x51, 0xfc, 0x4f, 0x88, 0x74, 0xd6, 0xcd, 0x27, 0x3f, 0x72, 0x90, 0xa3, - 0x88, 0x8b, 0x4f, 0x40, 0xd9, 0x1b, 0xb4, 0x07, 0xbd, 0xe1, 0xfe, 0xae, 0xbe, 0xab, 0x0f, 0xf4, - 0xf6, 0xd7, 0xfa, 0x41, 0xef, 0xe9, 0x70, 0x7f, 0x77, 0xaf, 0xdf, 0xeb, 0xea, 0x3b, 0x7a, 0xef, - 0x69, 0x35, 0x53, 0xbf, 0x77, 0x76, 0xae, 0x56, 0xae, 0x04, 0x88, 0x12, 0x00, 0xcb, 0x0b, 0x8c, - 0x55, 0xae, 0x5e, 0x3c, 0x3b, 0x57, 0x85, 0x40, 0x16, 0x65, 0xa8, 0x30, 0xcf, 0xc0, 0xf8, 0xf6, - 0x79, 0xbf, 0xb7, 0x5b, 0xe5, 0xeb, 0x6b, 0x67, 0xe7, 0x6a, 0x21, 0x54, 0x93, 0x4c, 0xea, 0xcc, - 0xb2, 0xcc, 0x40, 0xae, 0x0b, 0x2f, 0x7f, 0x91, 0x33, 0x9d, 0xfe, 0xab, 0x0b, 0x99, 0x7b, 0x7d, - 0x21, 0x73, 0x7f, 0x5c, 0xc8, 0xdc, 0xf7, 0x97, 0x72, 0xe6, 0xf5, 0xa5, 0x9c, 0x79, 0x73, 0x29, - 0x67, 0x0e, 0x9e, 0xfc, 0xeb, 0x76, 0x79, 0xd1, 0x0a, 0xde, 0xab, 0x9f, 0x3d, 0xde, 0x4a, 0xbd, - 0x7c, 0xe9, 0xc6, 0x39, 0xcc, 0xd3, 0xe7, 0xea, 0xe3, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe7, - 0x61, 0x72, 0x2b, 0x18, 0x0b, 0x00, 0x00, + // 989 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x57, 0x4f, 0x6f, 0xe2, 0x46, + 0x14, 0xc7, 0xc4, 0xfc, 0x7b, 0x40, 0xc2, 0xba, 0xa4, 0xeb, 0xd2, 0x2d, 0x46, 0xee, 0x05, 0xb5, + 0x0a, 0x34, 0xbb, 0xd5, 0x1e, 0x22, 0xf5, 0x00, 0x2c, 0x51, 0xad, 0x76, 0xb3, 0xc8, 0x21, 0x95, + 0x9a, 0x0b, 0x02, 0x33, 0x90, 0x11, 0x61, 0x8c, 0xec, 0xa1, 0x5a, 0xbe, 0xc1, 0x2a, 0xa7, 0x4a, + 0x3d, 0x55, 0x6a, 0xa4, 0x4a, 0xfd, 0x26, 0x55, 0x0f, 0x7b, 0xdc, 0x63, 0x7b, 0xb1, 0xaa, 0xe4, + 0x1b, 0x70, 0xec, 0x65, 0x2b, 0xcf, 0xd8, 0xc6, 0x40, 0x5a, 0x29, 0x21, 0xab, 0x3d, 0xf1, 0x7e, + 0xf3, 0xde, 0x9b, 0xf1, 0xfb, 0xcd, 0xef, 0xcd, 0x0c, 0xa0, 0xe0, 0x9e, 0x51, 0x35, 0x4c, 0x42, + 0x90, 0x41, 0xb1, 0x49, 0x42, 0x66, 0x65, 0x62, 0x99, 0xd4, 0x94, 0xb6, 0x71, 0xcf, 0xa8, 0x2c, + 0x46, 0x0b, 0xf9, 0xa1, 0x39, 0x34, 0x99, 0xab, 0xea, 0x5a, 0x3c, 0xaa, 0xf0, 0xd1, 0xd0, 0x34, + 0x87, 0xe7, 0xa8, 0xca, 0x50, 0x6f, 0x3a, 0xa8, 0x76, 0xc9, 0xcc, 0x73, 0x79, 0x2b, 0x8c, 0xc7, + 0x98, 0x8e, 0x11, 0xa1, 0x21, 0x93, 0x07, 0xa8, 0xbf, 0x44, 0x61, 0xf7, 0xb9, 0x3d, 0x6c, 0x04, + 0x6b, 0xbc, 0x98, 0x20, 0xa2, 0x11, 0x4c, 0xa5, 0x7d, 0x48, 0x19, 0xe7, 0x18, 0x11, 0xda, 0xc1, + 0x7d, 0x59, 0x28, 0x09, 0xe5, 0x54, 0x3d, 0x3f, 0x77, 0x94, 0xdc, 0xac, 0x3b, 0x3e, 0x3f, 0x50, + 0x03, 0x97, 0xaa, 0x27, 0xb9, 0xad, 0xf5, 0xa5, 0xaf, 0x20, 0xbb, 0xf8, 0x58, 0x37, 0x2d, 0xca, + 0xd2, 0xe4, 0xb9, 0xa3, 0xe4, 0xbd, 0xb4, 0xb0, 0x5b, 0xd5, 0x33, 0x0b, 0xac, 0xf5, 0xa5, 0x43, + 0xc8, 0x18, 0xe6, 0x94, 0x50, 0x64, 0x4d, 0xba, 0x16, 0x9d, 0xc9, 0x5b, 0x25, 0xa1, 0x9c, 0x7e, + 0xfc, 0xa8, 0xb2, 0x4c, 0x42, 0xa5, 0x11, 0x8a, 0xa9, 0x8b, 0xaf, 0x1d, 0x25, 0xa2, 0x2f, 0xe5, + 0x49, 0x1a, 0xc4, 0x6d, 0x3c, 0x24, 0xc8, 0x92, 0xc5, 0x92, 0x50, 0xce, 0xd4, 0xf7, 0xff, 0x71, + 0x94, 0xbd, 0x21, 0xa6, 0x67, 0xd3, 0x5e, 0xc5, 0x30, 0xc7, 0x55, 0xc3, 0xb4, 0xc7, 0xa6, 0xed, + 0xfd, 0xec, 0xd9, 0xfd, 0x51, 0x95, 0xce, 0x26, 0xc8, 0xae, 0xd4, 0x0c, 0xa3, 0xd6, 0xef, 0x5b, + 0xc8, 0xb6, 0x75, 0x6f, 0x02, 0xf5, 0x6d, 0x0c, 0xf2, 0x6b, 0xf4, 0xb4, 0xad, 0xd9, 0x7b, 0x60, + 0xa7, 0x05, 0x19, 0x6f, 0x5a, 0x9b, 0x76, 0x29, 0xf2, 0xd8, 0xc9, 0x57, 0xf8, 0xe6, 0x57, 0xfc, + 0xcd, 0xaf, 0xd4, 0xc8, 0xac, 0xfe, 0x70, 0xee, 0x28, 0x1f, 0x2c, 0x7d, 0x0a, 0xcb, 0x51, 0xf5, + 0x34, 0x87, 0xc7, 0x2e, 0x5a, 0xe3, 0x5b, 0xbc, 0x23, 0xdf, 0x27, 0xb0, 0x1b, 0xc6, 0x9d, 0x1f, + 0x90, 0x65, 0x63, 0x93, 0xd8, 0x72, 0xac, 0xb4, 0x55, 0x4e, 0xd5, 0x4b, 0x73, 0x47, 0x79, 0xe4, + 0x17, 0x78, 0x43, 0x98, 0xaa, 0xe7, 0xc3, 0xe3, 0xdf, 0x79, 0xc3, 0xd2, 0x01, 0x64, 0x26, 0x96, + 0x69, 0x0e, 0x3a, 0x67, 0x08, 0x0f, 0xcf, 0xa8, 0x1c, 0x2f, 0x09, 0x65, 0x31, 0x5c, 0x5a, 0xd8, + 0xab, 0xea, 0x69, 0x06, 0xbf, 0x66, 0x48, 0xfa, 0x12, 0x80, 0x7b, 0x31, 0xc1, 0x54, 0x4e, 0x30, + 0x19, 0xec, 0xce, 0x1d, 0xe5, 0x41, 0x38, 0xd3, 0xf5, 0xa9, 0x7a, 0x8a, 0x01, 0x26, 0xf9, 0x60, + 0x45, 0xce, 0x92, 0x9c, 0x64, 0x79, 0x6b, 0x2b, 0x72, 0xaf, 0xbf, 0x62, 0x83, 0x21, 0xa9, 0x01, + 0x3b, 0x9e, 0xd7, 0x24, 0x36, 0x22, 0xf6, 0xd4, 0x96, 0x53, 0x2c, 0xbd, 0x30, 0x77, 0x94, 0x0f, + 0x97, 0xd2, 0xfd, 0x00, 0x55, 0xdf, 0xe6, 0x33, 0xf8, 0x03, 0xd2, 0x21, 0xe4, 0x02, 0xaf, 0x5f, + 0x36, 0xb0, 0xb2, 0x3f, 0x9e, 0x3b, 0xca, 0xc3, 0x40, 0x25, 0x4b, 0x11, 0xaa, 0xbe, 0x13, 0x0c, + 0x79, 0xe5, 0x2f, 0x3a, 0x20, 0xbd, 0x69, 0x07, 0xfc, 0x21, 0xde, 0xd0, 0x01, 0x35, 0x63, 0xb4, + 0x2e, 0x67, 0xe1, 0x56, 0x72, 0x96, 0x21, 0xe1, 0x09, 0x80, 0xf7, 0x81, 0xee, 0xc3, 0x77, 0x20, + 0xf4, 0x55, 0x25, 0x89, 0xb7, 0x50, 0xd2, 0x3e, 0x70, 0x81, 0x74, 0xa8, 0x35, 0x93, 0x63, 0x8c, + 0xcd, 0x50, 0xa3, 0x07, 0x2e, 0x55, 0x4f, 0x32, 0xdb, 0x3d, 0x1b, 0x56, 0x65, 0x14, 0xdf, 0x4c, + 0x46, 0x89, 0x7b, 0x91, 0x51, 0x72, 0x23, 0x19, 0xa5, 0x36, 0x95, 0xd1, 0x4f, 0x51, 0x90, 0xd7, + 0x64, 0xd4, 0x30, 0xc9, 0x00, 0x5b, 0xe3, 0x4d, 0xa5, 0x14, 0x6c, 0x51, 0xd7, 0x18, 0x31, 0x31, + 0xdd, 0xb0, 0x45, 0x5d, 0x63, 0xe4, 0x6f, 0x91, 0x2b, 0xde, 0x55, 0x45, 0x6c, 0xdd, 0x42, 0x11, + 0xf7, 0x78, 0xbd, 0xfc, 0x25, 0x40, 0x76, 0x41, 0x49, 0x93, 0xf4, 0xef, 0x72, 0xaf, 0x14, 0x20, + 0x19, 0x9c, 0xb8, 0x51, 0xf7, 0xc4, 0xd5, 0x03, 0x2c, 0x7d, 0x0e, 0xb1, 0x45, 0x13, 0x6d, 0x3f, + 0xde, 0x5d, 0x3d, 0xdb, 0x59, 0x7f, 0xe8, 0x3c, 0xe6, 0xbe, 0xee, 0x83, 0x03, 0xf1, 0xd5, 0xaf, + 0x4a, 0x44, 0x7d, 0x2b, 0x40, 0x5e, 0xeb, 0x23, 0x42, 0xf1, 0x00, 0xa3, 0xfe, 0xa2, 0x4a, 0xe9, + 0x13, 0x88, 0x06, 0xb5, 0x65, 0xe7, 0x8e, 0x92, 0xe2, 0xb5, 0xb9, 0x45, 0x45, 0xf1, 0x0a, 0x03, + 0xd1, 0x5b, 0x33, 0xb0, 0xf5, 0x5f, 0x0c, 0x88, 0x77, 0x60, 0x20, 0xb6, 0x11, 0x03, 0xbf, 0x0b, + 0x90, 0x09, 0x87, 0xbe, 0x87, 0x47, 0xc3, 0x01, 0xc4, 0x27, 0x16, 0x1a, 0xe0, 0x97, 0x2b, 0x8f, + 0xa9, 0xe0, 0x15, 0xf8, 0x1c, 0x59, 0xa3, 0x73, 0xd4, 0x62, 0x31, 0x5e, 0x29, 0x5e, 0x86, 0x57, + 0xc4, 0xa7, 0x90, 0xe6, 0x47, 0x53, 0xab, 0x4b, 0xcf, 0x6c, 0x29, 0x0f, 0xb1, 0x89, 0x6b, 0xc8, + 0x02, 0xe3, 0x99, 0x03, 0xf5, 0x14, 0x76, 0x16, 0x1b, 0xcc, 0x03, 0xef, 0x50, 0x6b, 0x30, 0x77, + 0x34, 0x3c, 0xf7, 0x37, 0x90, 0xf0, 0x9e, 0x04, 0x52, 0x11, 0x00, 0xfb, 0x8a, 0xb2, 0xf8, 0xa4, + 0x7a, 0x68, 0xc4, 0xd5, 0xc1, 0x00, 0x75, 0xe9, 0xd4, 0x42, 0x41, 0x27, 0xf8, 0x98, 0x57, 0xf3, + 0xd9, 0xcf, 0x02, 0xc4, 0xf8, 0x9d, 0xf0, 0x14, 0x94, 0xe3, 0x76, 0xad, 0xdd, 0xec, 0x9c, 0x1c, + 0x69, 0x47, 0x5a, 0x5b, 0xab, 0x7d, 0xab, 0x9d, 0x36, 0x9f, 0x75, 0x4e, 0x8e, 0x8e, 0x5b, 0xcd, + 0x86, 0x76, 0xa8, 0x35, 0x9f, 0xe5, 0x22, 0x85, 0x07, 0x17, 0x97, 0xa5, 0xec, 0x52, 0x80, 0x24, + 0x03, 0xf0, 0x3c, 0x77, 0x30, 0x27, 0x14, 0x92, 0x17, 0x97, 0x25, 0xd1, 0xb5, 0xa5, 0x22, 0x64, + 0xb9, 0xa7, 0xad, 0x7f, 0xff, 0xa2, 0xd5, 0x3c, 0xca, 0x45, 0x0b, 0xe9, 0x8b, 0xcb, 0x52, 0xc2, + 0x83, 0x8b, 0x4c, 0xe6, 0xdc, 0xe2, 0x99, 0xae, 0x5d, 0x10, 0x5f, 0xfd, 0x56, 0x8c, 0xd4, 0x5b, + 0xaf, 0xaf, 0x8a, 0xc2, 0x9b, 0xab, 0xa2, 0xf0, 0xf7, 0x55, 0x51, 0xf8, 0xf1, 0xba, 0x18, 0x79, + 0x73, 0x5d, 0x8c, 0xfc, 0x79, 0x5d, 0x8c, 0x9c, 0x3e, 0xfd, 0xdf, 0xd3, 0xe5, 0x65, 0xd5, 0x7d, + 0xe4, 0x7f, 0xf1, 0x64, 0x2f, 0xf4, 0x4f, 0x82, 0x9d, 0x38, 0xbd, 0x38, 0xbb, 0x2b, 0x9f, 0xfc, + 0x1b, 0x00, 0x00, 0xff, 0xff, 0xff, 0x74, 0xb1, 0x7f, 0x68, 0x0c, 0x00, 0x00, } func (m *MsgConnectionOpenInit) Marshal() (dAtA []byte, err error) { @@ -856,31 +895,38 @@ func (m *MsgConnectionOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.Signer) i = encodeVarintConnection(dAtA, i, uint64(len(m.Signer))) i-- - dAtA[i] = 0x4a + dAtA[i] = 0x5a } if m.ConsensusHeight != 0 { i = encodeVarintConnection(dAtA, i, uint64(m.ConsensusHeight)) i-- - dAtA[i] = 0x40 + dAtA[i] = 0x50 } if len(m.ProofConsensus) > 0 { i -= len(m.ProofConsensus) copy(dAtA[i:], m.ProofConsensus) i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofConsensus))) i-- - dAtA[i] = 0x3a + dAtA[i] = 0x4a } - if m.ProofHeight != 0 { - i = encodeVarintConnection(dAtA, i, uint64(m.ProofHeight)) + if len(m.ProofClient) > 0 { + i -= len(m.ProofClient) + copy(dAtA[i:], m.ProofClient) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofClient))) i-- - dAtA[i] = 0x30 + dAtA[i] = 0x42 } if len(m.ProofInit) > 0 { i -= len(m.ProofInit) copy(dAtA[i:], m.ProofInit) i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofInit))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x3a + } + if m.ProofHeight != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.ProofHeight)) + i-- + dAtA[i] = 0x30 } if len(m.CounterpartyVersions) > 0 { for iNdEx := len(m.CounterpartyVersions) - 1; iNdEx >= 0; iNdEx-- { @@ -888,7 +934,7 @@ func (m *MsgConnectionOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.CounterpartyVersions[iNdEx]) i = encodeVarintConnection(dAtA, i, uint64(len(m.CounterpartyVersions[iNdEx]))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } } { @@ -900,7 +946,19 @@ func (m *MsgConnectionOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintConnection(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } if len(m.ConnectionId) > 0 { i -= len(m.ConnectionId) copy(dAtA[i:], m.ConnectionId) @@ -943,30 +1001,49 @@ func (m *MsgConnectionOpenAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.Signer) i = encodeVarintConnection(dAtA, i, uint64(len(m.Signer))) i-- - dAtA[i] = 0x3a + dAtA[i] = 0x4a } if m.ConsensusHeight != 0 { i = encodeVarintConnection(dAtA, i, uint64(m.ConsensusHeight)) i-- - dAtA[i] = 0x30 + dAtA[i] = 0x40 } if len(m.ProofConsensus) > 0 { i -= len(m.ProofConsensus) copy(dAtA[i:], m.ProofConsensus) i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofConsensus))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x3a } - if m.ProofHeight != 0 { - i = encodeVarintConnection(dAtA, i, uint64(m.ProofHeight)) + if len(m.ProofClient) > 0 { + i -= len(m.ProofClient) + copy(dAtA[i:], m.ProofClient) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofClient))) i-- - dAtA[i] = 0x20 + dAtA[i] = 0x32 } if len(m.ProofTry) > 0 { i -= len(m.ProofTry) copy(dAtA[i:], m.ProofTry) i = encodeVarintConnection(dAtA, i, uint64(len(m.ProofTry))) i-- + dAtA[i] = 0x2a + } + if m.ProofHeight != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.ProofHeight)) + i-- + dAtA[i] = 0x20 + } + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a } if len(m.Version) > 0 { @@ -1355,6 +1432,10 @@ func (m *MsgConnectionOpenTry) Size() (n int) { if l > 0 { n += 1 + l + sovConnection(uint64(l)) } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovConnection(uint64(l)) + } l = m.Counterparty.Size() n += 1 + l + sovConnection(uint64(l)) if len(m.CounterpartyVersions) > 0 { @@ -1363,12 +1444,16 @@ func (m *MsgConnectionOpenTry) Size() (n int) { n += 1 + l + sovConnection(uint64(l)) } } + if m.ProofHeight != 0 { + n += 1 + sovConnection(uint64(m.ProofHeight)) + } l = len(m.ProofInit) if l > 0 { n += 1 + l + sovConnection(uint64(l)) } - if m.ProofHeight != 0 { - n += 1 + sovConnection(uint64(m.ProofHeight)) + l = len(m.ProofClient) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) } l = len(m.ProofConsensus) if l > 0 { @@ -1398,13 +1483,21 @@ func (m *MsgConnectionOpenAck) Size() (n int) { if l > 0 { n += 1 + l + sovConnection(uint64(l)) } - l = len(m.ProofTry) - if l > 0 { + if m.ClientState != nil { + l = m.ClientState.Size() n += 1 + l + sovConnection(uint64(l)) } if m.ProofHeight != 0 { n += 1 + sovConnection(uint64(m.ProofHeight)) } + l = len(m.ProofTry) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + l = len(m.ProofClient) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } l = len(m.ProofConsensus) if l > 0 { n += 1 + l + sovConnection(uint64(l)) @@ -1851,6 +1944,42 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { m.ConnectionId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) } @@ -1883,7 +2012,7 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyVersions", wireType) } @@ -1915,7 +2044,26 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { } m.CounterpartyVersions = append(m.CounterpartyVersions, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + } + m.ProofHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProofHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProofInit", wireType) } @@ -1949,11 +2097,11 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { m.ProofInit = []byte{} } iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofClient", wireType) } - m.ProofHeight = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowConnection @@ -1963,12 +2111,27 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ProofHeight |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 7: + if byteLen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofClient = append(m.ProofClient[:0], dAtA[iNdEx:postIndex]...) + if m.ProofClient == nil { + m.ProofClient = []byte{} + } + iNdEx = postIndex + case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProofConsensus", wireType) } @@ -2002,7 +2165,7 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { m.ProofConsensus = []byte{} } iNdEx = postIndex - case 8: + case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConsensusHeight", wireType) } @@ -2021,7 +2184,7 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { break } } - case 9: + case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } @@ -2173,6 +2336,61 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + } + m.ProofHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProofHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProofTry", wireType) } @@ -2206,11 +2424,11 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { m.ProofTry = []byte{} } iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofClient", wireType) } - m.ProofHeight = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowConnection @@ -2220,12 +2438,27 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ProofHeight |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 5: + if byteLen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofClient = append(m.ProofClient[:0], dAtA[iNdEx:postIndex]...) + if m.ProofClient == nil { + m.ProofClient = []byte{} + } + iNdEx = postIndex + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProofConsensus", wireType) } @@ -2259,7 +2492,7 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { m.ProofConsensus = []byte{} } iNdEx = postIndex - case 6: + case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConsensusHeight", wireType) } @@ -2278,7 +2511,7 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { break } } - case 7: + case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } diff --git a/x/ibc/03-connection/types/connection_test.go b/x/ibc/03-connection/types/connection_test.go index b05eaba7a8bc..8f0f2fd52aeb 100644 --- a/x/ibc/03-connection/types/connection_test.go +++ b/x/ibc/03-connection/types/connection_test.go @@ -11,10 +11,12 @@ import ( ) var ( + chainID = "gaiamainnet" connectionID = "connectionidone" clientID = "clientidone" connectionID2 = "connectionidtwo" clientID2 = "clientidtwo" + clientHeight = uint64(6) ) func TestConnectionValidateBasic(t *testing.T) { diff --git a/x/ibc/03-connection/types/expected_keepers.go b/x/ibc/03-connection/types/expected_keepers.go index 122596808de0..50ce9716f7ef 100644 --- a/x/ibc/03-connection/types/expected_keepers.go +++ b/x/ibc/03-connection/types/expected_keepers.go @@ -10,6 +10,7 @@ type ClientKeeper interface { GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) GetClientConsensusState(ctx sdk.Context, clientID string, height uint64) (clientexported.ConsensusState, bool) GetSelfConsensusState(ctx sdk.Context, height uint64) (clientexported.ConsensusState, bool) + ValidateSelfClient(ctx sdk.Context, clientState clientexported.ClientState) error IterateClients(ctx sdk.Context, cb func(string, clientexported.ClientState) bool) ClientStore(ctx sdk.Context, clientID string) sdk.KVStore } diff --git a/x/ibc/03-connection/types/msgs.go b/x/ibc/03-connection/types/msgs.go index a1f63b6afd49..8daf3a271193 100644 --- a/x/ibc/03-connection/types/msgs.go +++ b/x/ibc/03-connection/types/msgs.go @@ -3,6 +3,8 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) @@ -63,17 +65,21 @@ var _ sdk.Msg = &MsgConnectionOpenTry{} // NewMsgConnectionOpenTry creates a new MsgConnectionOpenTry instance func NewMsgConnectionOpenTry( connectionID, clientID, counterpartyConnectionID, - counterpartyClientID string, counterpartyPrefix commitmenttypes.MerklePrefix, - counterpartyVersions []string, proofInit, proofConsensus []byte, + counterpartyClientID string, counterpartyClient clientexported.ClientState, + counterpartyPrefix commitmenttypes.MerklePrefix, counterpartyVersions []string, + proofInit, proofClient, proofConsensus []byte, proofHeight, consensusHeight uint64, signer sdk.AccAddress, ) *MsgConnectionOpenTry { counterparty := NewCounterparty(counterpartyClientID, counterpartyConnectionID, counterpartyPrefix) + csAny, _ := clienttypes.PackClientState(counterpartyClient) return &MsgConnectionOpenTry{ ConnectionId: connectionID, ClientId: clientID, + ClientState: csAny, Counterparty: counterparty, CounterpartyVersions: counterpartyVersions, ProofInit: proofInit, + ProofClient: proofClient, ProofConsensus: proofConsensus, ProofHeight: proofHeight, ConsensusHeight: consensusHeight, @@ -99,6 +105,16 @@ func (msg MsgConnectionOpenTry) ValidateBasic() error { if err := host.ClientIdentifierValidator(msg.ClientId); err != nil { return sdkerrors.Wrap(err, "invalid client ID") } + if msg.ClientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + } + clientState, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + } + if err := clientState.Validate(); err != nil { + return sdkerrors.Wrap(err, "counterparty client is invalid") + } if len(msg.CounterpartyVersions) == 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "empty counterparty versions") } @@ -110,6 +126,9 @@ func (msg MsgConnectionOpenTry) ValidateBasic() error { if len(msg.ProofInit) == 0 { return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") } + if len(msg.ProofClient) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + } if len(msg.ProofConsensus) == 0 { return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") } @@ -139,13 +158,17 @@ var _ sdk.Msg = &MsgConnectionOpenAck{} // NewMsgConnectionOpenAck creates a new MsgConnectionOpenAck instance func NewMsgConnectionOpenAck( - connectionID string, proofTry, proofConsensus []byte, + connectionID string, counterpartyClient clientexported.ClientState, + proofTry, proofClient, proofConsensus []byte, proofHeight, consensusHeight uint64, version string, signer sdk.AccAddress, ) *MsgConnectionOpenAck { + csAny, _ := clienttypes.PackClientState(counterpartyClient) return &MsgConnectionOpenAck{ ConnectionId: connectionID, + ClientState: csAny, ProofTry: proofTry, + ProofClient: proofClient, ProofConsensus: proofConsensus, ProofHeight: proofHeight, ConsensusHeight: consensusHeight, @@ -172,9 +195,22 @@ func (msg MsgConnectionOpenAck) ValidateBasic() error { if err := ValidateVersion(msg.Version); err != nil { return err } + if msg.ClientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + } + clientState, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + } + if err := clientState.Validate(); err != nil { + return sdkerrors.Wrap(err, "counterparty client is invalid") + } if len(msg.ProofTry) == 0 { return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") } + if len(msg.ProofClient) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + } if len(msg.ProofConsensus) == 0 { return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") } diff --git a/x/ibc/03-connection/types/msgs_test.go b/x/ibc/03-connection/types/msgs_test.go index 6f272960b3b9..42d36e7bdc52 100644 --- a/x/ibc/03-connection/types/msgs_test.go +++ b/x/ibc/03-connection/types/msgs_test.go @@ -3,6 +3,7 @@ package types_test import ( "fmt" "testing" + "time" "github.com/stretchr/testify/suite" @@ -14,7 +15,9 @@ import ( "github.com/cosmos/cosmos-sdk/store/rootmulti" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) @@ -103,20 +106,40 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { prefix := commitmenttypes.NewMerklePrefix([]byte("storePrefixKey")) signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + clientState := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), + ) + + // Pack consensus state into any to test unpacking error + consState := ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), clientHeight, []byte("nextValsHash"), + ) + invalidAny := clienttypes.MustPackConsensusState(consState) + counterparty := types.NewCounterparty("connectiontotest", "clienttotest", prefix) + + // invalidClientState fails validateBasic + invalidClient := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, 0, commitmenttypes.GetSDKSpecs(), + ) + testMsgs := []*types.MsgConnectionOpenTry{ - types.NewMsgConnectionOpenTry("test/conn1", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "test/iris", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "ibc/test", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "test/conn1", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", emptyPrefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, emptyProof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, emptyProof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 0, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 0, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, nil), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, 10, 10, signer), - types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", prefix, []string{"(invalid version)"}, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("test/conn1", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "test/iris", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "ibc/test", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "test/conn1", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", nil, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + &types.MsgConnectionOpenTry{"ibcconntest", "clienttotesta", invalidAny, counterparty, []string{ibctesting.ConnectionVersion}, 10, suite.proof, suite.proof, suite.proof, 10, signer}, + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, emptyPrefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, emptyProof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, emptyProof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, emptyProof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 0, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 0, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, nil), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{ibctesting.ConnectionVersion}, suite.proof, suite.proof, suite.proof, 10, 10, signer), + types.NewMsgConnectionOpenTry("ibcconntest", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []string{"(invalid version)"}, suite.proof, suite.proof, suite.proof, 10, 10, signer), } var testCases = []struct { @@ -128,15 +151,19 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { {testMsgs[1], false, "invalid client ID"}, {testMsgs[2], false, "invalid counterparty connection ID"}, {testMsgs[3], false, "invalid counterparty client ID"}, - {testMsgs[4], false, "empty counterparty prefix"}, - {testMsgs[5], false, "empty counterpartyVersions"}, - {testMsgs[6], false, "empty proofInit"}, - {testMsgs[7], false, "empty proofConsensus"}, - {testMsgs[8], false, "invalid proofHeight"}, - {testMsgs[9], false, "invalid consensusHeight"}, - {testMsgs[10], false, "empty singer"}, - {testMsgs[11], true, "success"}, - {testMsgs[12], false, "invalid version"}, + {testMsgs[4], false, "invalid nil counterparty client"}, + {testMsgs[5], false, "invalid client unpacking"}, + {testMsgs[6], false, "counterparty failed Validate"}, + {testMsgs[7], false, "empty counterparty prefix"}, + {testMsgs[8], false, "empty counterpartyVersions"}, + {testMsgs[9], false, "empty proofInit"}, + {testMsgs[10], false, "empty proofClient"}, + {testMsgs[11], false, "empty proofConsensus"}, + {testMsgs[12], false, "invalid proofHeight"}, + {testMsgs[13], false, "invalid consensusHeight"}, + {testMsgs[14], false, "empty singer"}, + {testMsgs[15], true, "success"}, + {testMsgs[16], false, "invalid version"}, } for i, tc := range testCases { @@ -151,16 +178,34 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + clientState := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), + ) + + // Pack consensus state into any to test unpacking error + consState := ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), clientHeight, []byte("nextValsHash"), + ) + invalidAny := clienttypes.MustPackConsensusState(consState) + + // invalidClientState fails validateBasic + invalidClient := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, 0, commitmenttypes.GetSDKSpecs(), + ) testMsgs := []*types.MsgConnectionOpenAck{ - types.NewMsgConnectionOpenAck("test/conn1", suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), - types.NewMsgConnectionOpenAck("ibcconntest", emptyProof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, emptyProof, 10, 10, ibctesting.ConnectionVersion, signer), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, suite.proof, 0, 10, ibctesting.ConnectionVersion, signer), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, suite.proof, 10, 0, ibctesting.ConnectionVersion, signer), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, suite.proof, 10, 10, "", signer), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, nil), - types.NewMsgConnectionOpenAck("ibcconntest", suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("test/conn1", clientState, suite.proof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", nil, suite.proof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + &types.MsgConnectionOpenAck{"ibcconntest", ibctesting.ConnectionVersion, invalidAny, 10, suite.proof, suite.proof, suite.proof, 10, signer}, + types.NewMsgConnectionOpenAck("ibcconntest", invalidClient, suite.proof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, emptyProof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, emptyProof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, emptyProof, 10, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, suite.proof, 0, 10, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, suite.proof, 10, 0, ibctesting.ConnectionVersion, signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, suite.proof, 10, 10, "", signer), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, nil), + types.NewMsgConnectionOpenAck("ibcconntest", clientState, suite.proof, suite.proof, suite.proof, 10, 10, ibctesting.ConnectionVersion, signer), } var testCases = []struct { msg *types.MsgConnectionOpenAck @@ -168,13 +213,17 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { errMsg string }{ {testMsgs[0], false, "invalid connection ID"}, - {testMsgs[1], false, "empty proofTry"}, - {testMsgs[2], false, "empty proofConsensus"}, - {testMsgs[3], false, "invalid proofHeight"}, - {testMsgs[4], false, "invalid consensusHeight"}, - {testMsgs[5], false, "invalid version"}, - {testMsgs[6], false, "empty signer"}, - {testMsgs[7], true, "success"}, + {testMsgs[1], false, "invalid nil counterparty client"}, + {testMsgs[2], false, "invalid unpacking counterparty client"}, + {testMsgs[3], false, "counterparty client failed Validate"}, + {testMsgs[4], false, "empty proofTry"}, + {testMsgs[5], false, "empty proofClient"}, + {testMsgs[6], false, "empty proofConsensus"}, + {testMsgs[7], false, "invalid proofHeight"}, + {testMsgs[8], false, "invalid consensusHeight"}, + {testMsgs[9], false, "invalid version"}, + {testMsgs[10], false, "empty signer"}, + {testMsgs[11], true, "success"}, } for i, tc := range testCases { diff --git a/x/ibc/03-connection/types/query.pb.gw.go b/x/ibc/03-connection/types/query.pb.gw.go index 7c17c2dd53ff..6fa7dc87e01d 100644 --- a/x/ibc/03-connection/types/query.pb.gw.go +++ b/x/ibc/03-connection/types/query.pb.gw.go @@ -304,7 +304,6 @@ func local_request_Query_ConnectionConsensusState_0(ctx context.Context, marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Connection_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/ibc/04-channel/types/query.pb.gw.go b/x/ibc/04-channel/types/query.pb.gw.go index d3ee502d1656..520d86c54948 100644 --- a/x/ibc/04-channel/types/query.pb.gw.go +++ b/x/ibc/04-channel/types/query.pb.gw.go @@ -878,7 +878,6 @@ func local_request_Query_NextSequenceReceive_0(ctx context.Context, marshaler ru // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Channel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/ibc/07-tendermint/types/client_state.go b/x/ibc/07-tendermint/types/client_state.go index 822b0083d803..22e1a8845062 100644 --- a/x/ibc/07-tendermint/types/client_state.go +++ b/x/ibc/07-tendermint/types/client_state.go @@ -112,6 +112,46 @@ func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { return cs.ProofSpecs } +// VerifyClientState verifies a proof of the client state of the running chain +// stored on the target machine +func (cs ClientState) VerifyClientState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + provingRoot commitmentexported.Root, + height uint64, + prefix commitmentexported.Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState clientexported.ClientState, +) error { + merkleProof, _, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := "clients/" + counterpartyClientIdentifier + "/" + host.ClientStatePath() + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + if clientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state cannot be empty") + } + + _, ok := clientState.(*ClientState) + if !ok { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "invalid client type %T, expected %T", clientState, &ClientState{}) + } + + bz, err := codec.MarshalAny(cdc, clientState) + if err != nil { + return err + } + + return merkleProof.VerifyMembership(cs.ProofSpecs, provingRoot, path, bz) +} + // VerifyClientConsensusState verifies a proof of the consensus state of the // Tendermint client stored on the target machine. func (cs ClientState) VerifyClientConsensusState( diff --git a/x/ibc/07-tendermint/types/client_state_test.go b/x/ibc/07-tendermint/types/client_state_test.go index 3af2e206c482..fad950bc1edc 100644 --- a/x/ibc/07-tendermint/types/client_state_test.go +++ b/x/ibc/07-tendermint/types/client_state_test.go @@ -6,7 +6,6 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" - ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" @@ -32,52 +31,52 @@ func (suite *TendermintTestSuite) TestValidate() { }{ { name: "valid client", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: true, }, { name: "invalid chainID", - clientState: ibctmtypes.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "invalid trust level", - clientState: ibctmtypes.NewClientState(chainID, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "invalid trusting period", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "invalid unbonding period", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "invalid max clock drift", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "invalid height", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, 0, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, 0, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "trusting period not less than unbonding period", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), expPass: false, }, { name: "proof specs is nil", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil), expPass: false, }, { name: "proof specs contains nil", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}), + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}), expPass: false, }, } @@ -95,8 +94,8 @@ func (suite *TendermintTestSuite) TestValidate() { func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { testCases := []struct { name string - clientState *ibctmtypes.ClientState - consensusState ibctmtypes.ConsensusState + clientState *types.ClientState + consensusState types.ConsensusState prefix commitmenttypes.MerklePrefix proof []byte expPass bool @@ -104,8 +103,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { // FIXME: uncomment // { // name: "successful verification", - // clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - // consensusState: ibctmtypes.ConsensusState{ + // clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + // consensusState: types.ConsensusState{ // Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), // }, // prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), @@ -113,8 +112,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { // }, { name: "ApplyPrefix failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + consensusState: types.ConsensusState{ Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), }, prefix: commitmenttypes.MerklePrefix{}, @@ -122,8 +121,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { }, { name: "latest client height < height", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + consensusState: types.ConsensusState{ Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), @@ -131,8 +130,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { }, { name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - consensusState: ibctmtypes.ConsensusState{ + clientState: &types.ClientState{LatestHeight: height, FrozenHeight: height - 1}, + consensusState: types.ConsensusState{ Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), @@ -140,8 +139,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { }, { name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + consensusState: types.ConsensusState{ Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), NextValidatorsHash: suite.valsHash, }, diff --git a/x/ibc/09-localhost/types/client_state.go b/x/ibc/09-localhost/types/client_state.go index e05311e4a574..94057a00faed 100644 --- a/x/ibc/09-localhost/types/client_state.go +++ b/x/ibc/09-localhost/types/client_state.go @@ -3,6 +3,7 @@ package types import ( "bytes" "encoding/binary" + "reflect" "strings" ics23 "github.com/confio/ics23/go" @@ -17,7 +18,6 @@ import ( channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" - commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) @@ -91,13 +91,36 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( return nil, sdkerrors.Wrap(clienttypes.ErrInvalidEvidence, "cannot submit misbehaviour to localhost client") } -// VerifyClientConsensusState returns an error since a local host client does not store consensus +// VerifyClientState verifies that the localhost client state is stored locally +func (cs ClientState) VerifyClientState( + store sdk.KVStore, cdc codec.BinaryMarshaler, _ commitmentexported.Root, + _ uint64, _ commitmentexported.Prefix, _ string, _ []byte, clientState clientexported.ClientState, +) error { + path := host.KeyClientState() + bz := store.Get(path) + if bz == nil { + return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, + "not found for path: %s", path) + } + + selfClient := clienttypes.MustUnmarshalClientState(cdc, bz) + + if !reflect.DeepEqual(selfClient, clientState) { + return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, + "stored clientState != provided clientState: \n%v\n≠\n%v", + selfClient, clientState, + ) + } + return nil +} + +// VerifyClientConsensusState returns nil since a local host client does not store consensus // states. func (cs ClientState) VerifyClientConsensusState( sdk.KVStore, codec.BinaryMarshaler, commitmentexported.Root, uint64, string, uint64, commitmentexported.Prefix, []byte, clientexported.ConsensusState, ) error { - return ErrConsensusStatesNotStored + return nil } // VerifyConnectionState verifies a proof of the connection state of the @@ -106,28 +129,24 @@ func (cs ClientState) VerifyConnectionState( store sdk.KVStore, cdc codec.BinaryMarshaler, _ uint64, - prefix commitmentexported.Prefix, + _ commitmentexported.Prefix, _ []byte, connectionID string, connectionEnd connectionexported.ConnectionI, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.ConnectionPath(connectionID)) - if err != nil { - return err - } - - bz := store.Get([]byte(path.String())) + path := host.KeyConnection(connectionID) + bz := store.Get(path) if bz == nil { return sdkerrors.Wrapf(clienttypes.ErrFailedConnectionStateVerification, "not found for path %s", path) } var prevConnection connectiontypes.ConnectionEnd - err = cdc.UnmarshalBinaryBare(bz, &prevConnection) + err := cdc.UnmarshalBinaryBare(bz, &prevConnection) if err != nil { return err } - if connectionEnd != &prevConnection { + if !reflect.DeepEqual(&prevConnection, connectionEnd) { return sdkerrors.Wrapf( clienttypes.ErrFailedConnectionStateVerification, "connection end ≠ previous stored connection: \n%v\n≠\n%v", connectionEnd, prevConnection, @@ -149,23 +168,19 @@ func (cs ClientState) VerifyChannelState( channelID string, channel channelexported.ChannelI, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.ChannelPath(portID, channelID)) - if err != nil { - return err - } - - bz := store.Get([]byte(path.String())) + path := host.KeyChannel(portID, channelID) + bz := store.Get(path) if bz == nil { return sdkerrors.Wrapf(clienttypes.ErrFailedChannelStateVerification, "not found for path %s", path) } var prevChannel channeltypes.Channel - err = cdc.UnmarshalBinaryBare(bz, &prevChannel) + err := cdc.UnmarshalBinaryBare(bz, &prevChannel) if err != nil { return err } - if channel != &prevChannel { + if !reflect.DeepEqual(&prevChannel, channel) { return sdkerrors.Wrapf( clienttypes.ErrFailedChannelStateVerification, "channel end ≠ previous stored channel: \n%v\n≠\n%v", channel, prevChannel, @@ -181,19 +196,16 @@ func (cs ClientState) VerifyPacketCommitment( store sdk.KVStore, _ codec.BinaryMarshaler, _ uint64, - prefix commitmentexported.Prefix, + _ commitmentexported.Prefix, _ []byte, portID, channelID string, sequence uint64, commitmentBytes []byte, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketCommitmentPath(portID, channelID, sequence)) - if err != nil { - return err - } + path := host.KeyPacketCommitment(portID, channelID, sequence) - data := store.Get([]byte(path.String())) + data := store.Get(path) if len(data) == 0 { return sdkerrors.Wrapf(clienttypes.ErrFailedPacketCommitmentVerification, "not found for path %s", path) } @@ -214,19 +226,16 @@ func (cs ClientState) VerifyPacketAcknowledgement( store sdk.KVStore, _ codec.BinaryMarshaler, _ uint64, - prefix commitmentexported.Prefix, + _ commitmentexported.Prefix, _ []byte, portID, channelID string, sequence uint64, acknowledgement []byte, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) - if err != nil { - return err - } + path := host.KeyPacketAcknowledgement(portID, channelID, sequence) - data := store.Get([]byte(path.String())) + data := store.Get(path) if len(data) == 0 { return sdkerrors.Wrapf(clienttypes.ErrFailedPacketAckVerification, "not found for path %s", path) } @@ -248,18 +257,15 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( store sdk.KVStore, _ codec.BinaryMarshaler, _ uint64, - prefix commitmentexported.Prefix, + _ commitmentexported.Prefix, _ []byte, portID, channelID string, sequence uint64, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) - if err != nil { - return err - } + path := host.KeyPacketAcknowledgement(portID, channelID, sequence) - data := store.Get([]byte(path.String())) + data := store.Get(path) if data != nil { return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckAbsenceVerification, "expected no ack absence") } @@ -273,18 +279,15 @@ func (cs ClientState) VerifyNextSequenceRecv( store sdk.KVStore, _ codec.BinaryMarshaler, _ uint64, - prefix commitmentexported.Prefix, + _ commitmentexported.Prefix, _ []byte, portID, channelID string, nextSequenceRecv uint64, ) error { - path, err := commitmenttypes.ApplyPrefix(prefix, host.NextSequenceRecvPath(portID, channelID)) - if err != nil { - return err - } + path := host.KeyNextSequenceRecv(portID, channelID) - data := store.Get([]byte(path.String())) + data := store.Get(path) if len(data) == 0 { return sdkerrors.Wrapf(clienttypes.ErrFailedNextSeqRecvVerification, "not found for path %s", path) } diff --git a/x/ibc/09-localhost/types/client_state_test.go b/x/ibc/09-localhost/types/client_state_test.go index 62cd5dfe444f..f5edebc4abc1 100644 --- a/x/ibc/09-localhost/types/client_state_test.go +++ b/x/ibc/09-localhost/types/client_state_test.go @@ -1,10 +1,13 @@ package types_test import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) const ( @@ -47,106 +50,218 @@ func (suite *LocalhostTestSuite) TestValidate() { } } +func (suite *LocalhostTestSuite) TestVerifyClientState() { + clientState := types.NewClientState("chainID", 10) + invalidClient := types.NewClientState("chainID", 12) + + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + counterparty *types.ClientState + expPass bool + }{ + { + name: "proof verification success", + clientState: clientState, + malleate: func() { + bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) + suite.store.Set(host.KeyClientState(), bz) + }, + counterparty: clientState, + expPass: true, + }, + { + name: "proof verification failed: invalid client", + clientState: clientState, + malleate: func() { + bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) + suite.store.Set(host.KeyClientState(), bz) + }, + counterparty: invalidClient, + expPass: false, + }, + { + name: "proof verification failed: client not stored", + clientState: clientState, + malleate: func() {}, + counterparty: clientState, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyClientState( + suite.store, suite.cdc, nil, 10, nil, "", []byte{}, tc.counterparty, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} + func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() { clientState := types.NewClientState("chainID", 10) err := clientState.VerifyClientConsensusState( nil, nil, nil, 0, "", 0, nil, nil, nil, ) - suite.Require().Error(err) + suite.Require().NoError(err) } func (suite *LocalhostTestSuite) TestVerifyConnectionState() { counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []string{"1.0.0"}) + conn1 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []string{"1.0.0"}) + conn2 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []string{"2.0.0"}) testCases := []struct { name string clientState *types.ClientState + malleate func() connection connectiontypes.ConnectionEnd - prefix commitmenttypes.MerklePrefix - proof []byte expPass bool }{ { - name: "ApplyPrefix failed", + name: "proof verification success", clientState: types.NewClientState("chainID", 10), - connection: conn, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&conn1) + suite.Require().NoError(err) + suite.store.Set(host.KeyConnection(testConnectionID), bz) + }, + connection: conn1, + expPass: true, }, { - name: "proof verification failed", + name: "proof verification failed: connection not stored", clientState: types.NewClientState("chainID", 10), - connection: conn, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, + malleate: func() {}, + connection: conn1, expPass: false, }, + { + name: "proof verification failed: unmarshal error", + clientState: types.NewClientState("chainID", 10), + malleate: func() { + suite.store.Set(host.KeyConnection(testConnectionID), []byte("connection")) + }, + connection: conn1, + expPass: false, + }, + { + name: "proof verification failed: different connection stored", + clientState: types.NewClientState("chainID", 10), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&conn2) + suite.Require().NoError(err) + suite.store.Set(host.KeyConnection(testConnectionID), bz) + }, + connection: conn1, + expPass: false, + }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyConnectionState( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testConnectionID, &tc.connection, - ) + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + err := tc.clientState.VerifyConnectionState( + suite.store, suite.cdc, height, nil, []byte{}, testConnectionID, &tc.connection, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } func (suite *LocalhostTestSuite) TestVerifyChannelState() { counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + ch1 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + ch2 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "2.0.0") testCases := []struct { name string clientState *types.ClientState + malleate func() channel channeltypes.Channel - prefix commitmenttypes.MerklePrefix - proof []byte expPass bool }{ { - name: "ApplyPrefix failed", + name: "proof verification success", clientState: types.NewClientState("chainID", 10), - channel: ch, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&ch1) + suite.Require().NoError(err) + suite.store.Set(host.KeyChannel(testPortID, testChannelID), bz) + }, + channel: ch1, + expPass: true, }, { - name: "latest client height < height", + name: "proof verification failed: channel not stored", clientState: types.NewClientState("chainID", 10), - channel: ch, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + malleate: func() {}, + channel: ch1, expPass: false, }, { - name: "proof verification failed", + name: "proof verification failed: unmarshal failed", clientState: types.NewClientState("chainID", 10), - channel: ch, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + malleate: func() { + suite.store.Set(host.KeyChannel(testPortID, testChannelID), []byte("channel")) + + }, + channel: ch1, + expPass: false, + }, + { + name: "proof verification failed: different channel stored", + clientState: types.NewClientState("chainID", 10), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&ch2) + suite.Require().NoError(err) + suite.store.Set(host.KeyChannel(testPortID, testChannelID), bz) + + }, + channel: ch1, + expPass: false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyChannelState( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, &tc.channel, - ) + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + err := tc.clientState.VerifyChannelState( + suite.store, suite.cdc, height, nil, []byte{}, testPortID, testChannelID, &tc.channel, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } @@ -154,54 +269,58 @@ func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() { testCases := []struct { name string clientState *types.ClientState + malleate func() commitment []byte - prefix commitmenttypes.MerklePrefix - proof []byte expPass bool }{ { - name: "ApplyPrefix failed", + name: "proof verification success", clientState: types.NewClientState("chainID", 10), - commitment: []byte{}, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - { - name: "latest client height < height", - clientState: types.NewClientState("chainID", 10), - commitment: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + malleate: func() { + suite.store.Set( + host.KeyPacketCommitment(testPortID, testChannelID, testSequence), []byte("commitment"), + ) + }, + commitment: []byte("commitment"), + expPass: true, }, { - name: "client is frozen", + name: "proof verification failed: different commitment stored", clientState: types.NewClientState("chainID", 10), - commitment: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + malleate: func() { + suite.store.Set( + host.KeyPacketCommitment(testPortID, testChannelID, testSequence), []byte("different"), + ) + }, + commitment: []byte("commitment"), + expPass: false, }, { - name: "proof verification failed", + name: "proof verification failed: no commitment stored", clientState: types.NewClientState("chainID", 10), + malleate: func() {}, commitment: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, expPass: false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyPacketCommitment( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment, - ) + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + err := tc.clientState.VerifyPacketCommitment( + suite.store, suite.cdc, height, nil, []byte{}, testPortID, testChannelID, testSequence, tc.commitment, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } @@ -209,134 +328,137 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() { testCases := []struct { name string clientState *types.ClientState + malleate func() ack []byte - prefix commitmenttypes.MerklePrefix - proof []byte expPass bool }{ { - name: "ApplyPrefix failed", + name: "proof verification success", clientState: types.NewClientState("chainID", 10), - ack: []byte{}, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - { - name: "latest client height < height", - clientState: types.NewClientState("chainID", 10), - ack: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + malleate: func() { + suite.store.Set( + host.KeyPacketAcknowledgement(testPortID, testChannelID, testSequence), []byte("acknowledgement"), + ) + }, + ack: []byte("acknowledgement"), + expPass: true, }, { - name: "client is frozen", + name: "proof verification failed: different ack stored", clientState: types.NewClientState("chainID", 10), - ack: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + malleate: func() { + suite.store.Set( + host.KeyPacketAcknowledgement(testPortID, testChannelID, testSequence), []byte("different"), + ) + }, + ack: []byte("acknowledgment"), + expPass: false, }, { - name: "proof verification failed", + name: "proof verification failed: no commitment stored", clientState: types.NewClientState("chainID", 10), + malleate: func() {}, ack: []byte{}, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, expPass: false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyPacketAcknowledgement( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack, - ) + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + err := tc.clientState.VerifyPacketAcknowledgement( + suite.store, suite.cdc, height, nil, []byte{}, testPortID, testChannelID, testSequence, tc.ack, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgementAbsence() { - testCases := []struct { - name string - clientState *types.ClientState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool - }{ - { - name: "ApplyPrefix failed", - clientState: types.NewClientState("chainID", 10), - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - } + clientState := types.NewClientState("chainID", 10) - for i, tc := range testCases { - tc := tc + err := clientState.VerifyPacketAcknowledgementAbsence( + suite.store, suite.cdc, height, nil, nil, testPortID, testChannelID, testSequence, + ) - err := tc.clientState.VerifyPacketAcknowledgementAbsence( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, - ) + suite.Require().NoError(err, "ack absence failed") - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } + suite.store.Set(host.KeyPacketAcknowledgement(testPortID, testChannelID, testSequence), []byte("ack")) + + err = clientState.VerifyPacketAcknowledgementAbsence( + suite.store, suite.cdc, height, nil, nil, testPortID, testChannelID, testSequence, + ) + suite.Require().Error(err, "ack exists in store") } func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() { + nextSeqRecv := uint64(5) + testCases := []struct { name string clientState *types.ClientState - prefix commitmenttypes.MerklePrefix - proof []byte + malleate func() + nextSeqRecv uint64 expPass bool }{ { - name: "ApplyPrefix failed", + name: "proof verification success", clientState: types.NewClientState("chainID", 10), - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - { - name: "latest client height < height", - clientState: types.NewClientState("chainID", 10), - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + malleate: func() { + suite.store.Set( + host.KeyNextSequenceRecv(testPortID, testChannelID), + sdk.Uint64ToBigEndian(nextSeqRecv), + ) + }, + nextSeqRecv: nextSeqRecv, + expPass: true, }, { - name: "client is frozen", + name: "proof verification failed: different nextSeqRecv stored", clientState: types.NewClientState("chainID", 10), - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + malleate: func() { + suite.store.Set( + host.KeyNextSequenceRecv(testPortID, testChannelID), + sdk.Uint64ToBigEndian(3), + ) + }, + nextSeqRecv: nextSeqRecv, expPass: false, }, { - name: "proof verification failed", + name: "proof verification failed: no nextSeqRecv stored", clientState: types.NewClientState("chainID", 10), - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, + malleate: func() {}, + nextSeqRecv: nextSeqRecv, expPass: false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyNextSequenceRecv( - suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, - ) + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + err := tc.clientState.VerifyNextSequenceRecv( + suite.store, suite.cdc, height, nil, []byte{}, testPortID, testChannelID, nextSeqRecv, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } diff --git a/x/ibc/spec/04_messages.md b/x/ibc/spec/04_messages.md index 945488a0272e..d77ca02642ea 100644 --- a/x/ibc/spec/04_messages.md +++ b/x/ibc/spec/04_messages.md @@ -91,10 +91,12 @@ using the `MsgConnectionOpenTry`. type MsgConnectionOpenTry struct { ClientId string ConnectionId string + ClientState *types.Any // proto-packed counterparty client Counterparty Counterparty CounterpartyVersions []string - ProofInit []byte ProofHeight uint64 + ProofInit []byte + ProofClient []byte ProofConsensus []byte ConsensusHeight uint64 Signer sdk.AccAddress @@ -104,16 +106,19 @@ type MsgConnectionOpenTry struct { This message is expected to fail if: - `ClientId` is invalid (see naming requirements) - `ConnectionId` is invalid (see naming requirements) +- `ClientState` is not a valid client of the executing chain - `Counterparty` is empty - `CounterpartyVersions` is empty -- `ProofInit` is empty - `ProofHeight` is zero +- `ProofInit` is empty +- `ProofClient` is empty - `ProofConsensus` is empty - `ConsensusHeight` is zero - `Signer` is empty - A Client hasn't been created for the given ID - A Connection for the given ID already exists - `ProofInit` does not prove that the counterparty connection is in state INIT +- `ProofClient` does not prove that the counterparty has stored the `ClientState` provided in message - `ProofConsensus` does not prove that the counterparty has the correct consensus state for this chain The message creates a connection for the given ID with an TRYOPEN State. @@ -127,8 +132,10 @@ using the `MsgConnectionOpenAck`. type MsgConnectionOpenAck struct { ConnectionId string Version string - ProofTry []byte + ClientState *types.Any // proto-packed counterparty client ProofHeight uint64 + ProofTry []byte + ProofClient []byte ProofConsensus []byte ConsensusHeight uint64 Signer sdk.AccAddress @@ -138,12 +145,15 @@ type MsgConnectionOpenAck struct { This message is expected to fail if: - `ConnectionId` is invalid (see naming requirements) - `Version` is empty -- `ProofTry` is empty +- `ClientState` is not a valid client of the executing chain - `ProofHeight` is zero +- `ProofTry` is empty +- `ProofClient` is empty - `ProofConsensus` is empty - `ConsensusHeight` is zero - `Signer` is empty - `ProofTry` does not prove that the counterparty connection is in state TRYOPEN +- `ProofClient` does not prove that the counterparty has stored the `ClientState` provided by message - `ProofConsensus` does not prove that the counterparty has the correct consensus state for this chain The message sets the connection state for the given ID to OPEN. diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go index 24bed50c9ceb..4f5e8df1a305 100644 --- a/x/ibc/testing/chain.go +++ b/x/ibc/testing/chain.go @@ -170,6 +170,19 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, uint64) { return proof, uint64(res.Height) + 1 } +// QueryClientStateProof performs and abci query for a client state +// stored with a given clientID and returns the ClientState along with the proof +func (chain *TestChain) QueryClientStateProof(clientID string) (clientexported.ClientState, []byte) { + // retrieve client state to provide proof for + clientState, found := chain.App.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID) + require.True(chain.t, found) + + clientKey := host.FullKeyClientPath(clientID, host.KeyClientState()) + proofClient, _ := chain.QueryProof(clientKey) + + return clientState, proofClient +} + // QueryConsensusStateProof performs an abci query for a consensus state // stored on the given clientID. The proof and consensusHeight are returned. func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, uint64) { @@ -194,6 +207,7 @@ func (chain *TestChain) NextBlock() { // increment the current header chain.CurrentHeader = tmproto.Header{ + ChainID: chain.ChainID, Height: chain.App.LastBlockHeight() + 1, AppHash: chain.App.LastCommitID().Hash, // NOTE: the time is increased by the coordinator to maintain time synchrony amongst @@ -216,6 +230,7 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) error { chain.App.BaseApp, chain.GetContext().BlockHeader(), msgs, + chain.ChainID, []uint64{chain.SenderAccount.GetAccountNumber()}, []uint64{chain.SenderAccount.GetSequence()}, true, true, chain.senderPrivKey, @@ -467,6 +482,8 @@ func (chain *TestChain) ConnectionOpenTry( counterparty *TestChain, connection, counterpartyConnection *TestConnection, ) error { + counterpartyClient, proofClient := counterparty.QueryClientStateProof(counterpartyConnection.ClientID) + connectionKey := host.KeyConnection(counterpartyConnection.ID) proofInit, proofHeight := counterparty.QueryProof(connectionKey) @@ -475,8 +492,8 @@ func (chain *TestChain) ConnectionOpenTry( msg := connectiontypes.NewMsgConnectionOpenTry( connection.ID, connection.ClientID, counterpartyConnection.ID, counterpartyConnection.ClientID, - counterparty.GetPrefix(), []string{ConnectionVersion}, - proofInit, proofConsensus, + counterpartyClient, counterparty.GetPrefix(), []string{ConnectionVersion}, + proofInit, proofClient, proofConsensus, proofHeight, consensusHeight, chain.SenderAccount.GetAddress(), ) @@ -488,14 +505,16 @@ func (chain *TestChain) ConnectionOpenAck( counterparty *TestChain, connection, counterpartyConnection *TestConnection, ) error { + counterpartyClient, proofClient := counterparty.QueryClientStateProof(counterpartyConnection.ClientID) + connectionKey := host.KeyConnection(counterpartyConnection.ID) proofTry, proofHeight := counterparty.QueryProof(connectionKey) proofConsensus, consensusHeight := counterparty.QueryConsensusStateProof(counterpartyConnection.ClientID) msg := connectiontypes.NewMsgConnectionOpenAck( - connection.ID, - proofTry, proofConsensus, + connection.ID, counterpartyClient, + proofTry, proofClient, proofConsensus, proofHeight, consensusHeight, ConnectionVersion, chain.SenderAccount.GetAddress(), diff --git a/x/mint/types/query.pb.gw.go b/x/mint/types/query.pb.gw.go index c70c3e60b077..644c2782e2cd 100644 --- a/x/mint/types/query.pb.gw.go +++ b/x/mint/types/query.pb.gw.go @@ -88,7 +88,6 @@ func local_request_Query_AnnualProvisions_0(ctx context.Context, marshaler runti // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/params/types/proposal/query.pb.gw.go b/x/params/types/proposal/query.pb.gw.go index c7fab13fca65..05f80279a985 100644 --- a/x/params/types/proposal/query.pb.gw.go +++ b/x/params/types/proposal/query.pb.gw.go @@ -70,7 +70,6 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 2191ede320f0..9310d912e0e6 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -69,7 +69,7 @@ func TestSlashingMsgs(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) require.NoError(t, err) simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) @@ -86,7 +86,7 @@ func TestSlashingMsgs(t *testing.T) { // unjail should fail with unknown validator header = tmproto.Header{Height: app.LastBlockHeight() + 1} - _, res, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{unjailMsg}, []uint64{0}, []uint64{1}, false, false, priv1) + _, res, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) require.Error(t, err) require.Nil(t, res) require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) diff --git a/x/slashing/types/query.pb.gw.go b/x/slashing/types/query.pb.gw.go index 5ac3296025c2..77c4688d9e4f 100644 --- a/x/slashing/types/query.pb.gw.go +++ b/x/slashing/types/query.pb.gw.go @@ -142,7 +142,6 @@ func local_request_Query_SigningInfos_0(ctx context.Context, marshaler runtime.M // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/staking/app_test.go b/x/staking/app_test.go index 2de07c62a957..28cbfdce60fa 100644 --- a/x/staking/app_test.go +++ b/x/staking/app_test.go @@ -71,7 +71,7 @@ func TestStakingMsgs(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeEncodingConfig().TxConfig - _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) require.NoError(t, err) simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) @@ -91,7 +91,7 @@ func TestStakingMsgs(t *testing.T) { editValidatorMsg := types.NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil) header = tmproto.Header{Height: app.LastBlockHeight() + 1} - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{1}, true, true, priv1) + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{editValidatorMsg}, "", []uint64{0}, []uint64{1}, true, true, priv1) require.NoError(t, err) validator = checkValidator(t, app, sdk.ValAddress(addr1), true) @@ -102,7 +102,7 @@ func TestStakingMsgs(t *testing.T) { delegateMsg := types.NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) header = tmproto.Header{Height: app.LastBlockHeight() + 1} - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{delegateMsg}, []uint64{1}, []uint64{0}, true, true, priv2) + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{delegateMsg}, "", []uint64{1}, []uint64{0}, true, true, priv2) require.NoError(t, err) simapp.CheckBalance(t, app, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) @@ -111,7 +111,7 @@ func TestStakingMsgs(t *testing.T) { // begin unbonding beginUnbondingMsg := types.NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondCoin) header = tmproto.Header{Height: app.LastBlockHeight() + 1} - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, []uint64{1}, []uint64{1}, true, true, priv2) + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, "", []uint64{1}, []uint64{1}, true, true, priv2) require.NoError(t, err) // delegation should exist anymore diff --git a/x/staking/types/query.pb.gw.go b/x/staking/types/query.pb.gw.go index 0c433330203b..8995f7645485 100644 --- a/x/staking/types/query.pb.gw.go +++ b/x/staking/types/query.pb.gw.go @@ -874,7 +874,6 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Validators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { diff --git a/x/upgrade/types/query.pb.gw.go b/x/upgrade/types/query.pb.gw.go index 6d311b577828..004a27419cb7 100644 --- a/x/upgrade/types/query.pb.gw.go +++ b/x/upgrade/types/query.pb.gw.go @@ -106,7 +106,6 @@ func local_request_Query_AppliedPlan_0(ctx context.Context, marshaler runtime.Ma // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_CurrentPlan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {