From a32d4b94ee30946eb3608b9b677f2b936c442f75 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 6 Jan 2020 13:59:49 -0300 Subject: [PATCH 01/35] implement ICS07 --- x/ibc/02-client/exported/exported.go | 66 +++++++++++++++++++ x/ibc/02-client/keeper/client.go | 8 +-- x/ibc/02-client/keeper/keeper.go | 26 +++++--- x/ibc/02-client/types/codec.go | 8 +-- x/ibc/02-client/types/state.go | 19 ------ x/ibc/07-tendermint/client_state.go | 51 ++++++++++++++ .../tendermint => 07-tendermint}/codec.go | 1 + .../tendermint => 07-tendermint}/committer.go | 12 ++-- .../consensus_state.go | 0 .../consensus_state_test.go | 0 .../types/tendermint => 07-tendermint}/doc.go | 0 .../tendermint => 07-tendermint}/evidence.go | 0 .../evidence_test.go | 0 .../tendermint => 07-tendermint}/header.go | 0 .../misbehaviour.go | 0 .../misbehaviour_test.go | 0 .../tendermint_test.go | 0 .../test_utils.go | 0 18 files changed, 147 insertions(+), 44 deletions(-) delete mode 100644 x/ibc/02-client/types/state.go create mode 100644 x/ibc/07-tendermint/client_state.go rename x/ibc/{02-client/types/tendermint => 07-tendermint}/codec.go (89%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/committer.go (57%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/consensus_state.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/consensus_state_test.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/doc.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/evidence.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/evidence_test.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/header.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/misbehaviour.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/misbehaviour_test.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/tendermint_test.go (100%) rename x/ibc/{02-client/types/tendermint => 07-tendermint}/test_utils.go (100%) diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 50632bf71ca8..f32ae4767a2d 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -8,6 +8,72 @@ import ( commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +// ClientState defines the required common functions for light clients. +type ClientState interface { + GetID() string + ClientType() ClientType + IsFrozen() bool + + // State verification functions + + VerifyClientConsensusState( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + clientID string, + consensusState ConsensusState, + ) error + VerifyConnectionState( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + connectionID string, + // connectionEnd connection, + ) error + VerifyChannelState( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID string, + channelID string, + // channelEnd channel, + ) error + VerifyPacketCommitment( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID string, + channelID string, + sequence uint64, + commitmentBytes []byte, + ) + VerifyPacketAcknowledgement( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID string, + channelID string, + sequence uint64, + acknowledgement []byte, + ) + VerifyPacketAcknowledgementAbsence( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID string, + channelID string, + sequence uint64, + ) + VerifyNextSequenceRecv( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID string, + channelID string, + nextSequenceRecv uint64, + ) +} + // ConsensusState is the state of the consensus process type ConsensusState interface { ClientType() ClientType // Consensus kind diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 91cadfe4a016..5a29e758ca45 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) // CreateClient creates a new client state and populates it with a given consensus @@ -17,7 +17,7 @@ import ( func (k Keeper) CreateClient( ctx sdk.Context, clientID string, clientType exported.ClientType, consensusState exported.ConsensusState, -) (types.State, error) { +) (exported.ClientState, error) { _, found := k.GetClientState(ctx, clientID) if found { return types.State{}, sdkerrors.Wrapf(errors.ErrClientExists, "cannot create client with ID %s", clientID) @@ -28,7 +28,7 @@ func (k Keeper) CreateClient( panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) } - clientState := k.initialize(ctx, clientID, consensusState) + clientState := k.initialize(ctx, clientID, clientType, consensusState) k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) @@ -54,7 +54,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H return sdkerrors.Wrapf(errors.ErrClientNotFound, "cannot update client with ID %s", clientID) } - if clientState.Frozen { + if clientState.IsFrozen() { return sdkerrors.Wrapf(errors.ErrClientFrozen, "cannot update client with ID %s", clientID) } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 5732913247b3..6115d5ce6d91 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -36,23 +36,23 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { } // GetClientState gets a particular client from the store -func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.State, bool) { +func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.KeyClientState(clientID)) if bz == nil { return types.State{}, false } - var clientState types.State + var clientState exported.ClientState k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &clientState) return clientState, true } // SetClientState sets a particular Client to the store -func (k Keeper) SetClientState(ctx sdk.Context, clientState types.State) { +func (k Keeper) SetClientState(ctx sdk.Context, clientState exported.ClientState) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) - store.Set(types.KeyClientState(clientState.ID), bz) + store.Set(types.KeyClientState(clientState.GetID()), bz) } // GetClientType gets the consensus type for a specific client @@ -170,8 +170,18 @@ func (k Keeper) SetCommitter(ctx sdk.Context, clientID string, height uint64, co // State returns a new client state with a given id as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation -func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.State { - clientState := types.NewClientState(clientID) +func (k Keeper) initialize( + ctx sdk.Context, clientID string, clientType exported.ClientType, + consensusState exported.ConsensusState, +) exported.ClientState { + var clientState exported.ClientState + switch clientType { + case exported.Tendermint: + clientState = types.NewClientState(clientID) + default: + panic("invalid client type") + } + k.SetConsensusState(ctx, clientID, consensusState) return clientState } @@ -200,7 +210,7 @@ func (k Keeper) VerifyMembership( return false } - if clientState.Frozen { + if clientState.IsFrozen() { return false } @@ -225,7 +235,7 @@ func (k Keeper) VerifyNonMembership( return false } - if clientState.Frozen { + if clientState.IsFrozen() { return false } diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index 6cd376b6e8ea..91729390d33a 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -3,7 +3,6 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" ) // SubModuleCdc defines the IBC client codec. @@ -11,6 +10,7 @@ var SubModuleCdc *codec.Codec // RegisterCodec registers the IBC client interfaces and types func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.ClientState)(nil), nil) cdc.RegisterInterface((*exported.ConsensusState)(nil), nil) cdc.RegisterInterface((*exported.Committer)(nil), nil) cdc.RegisterInterface((*exported.Header)(nil), nil) @@ -18,12 +18,6 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgCreateClient{}, "ibc/client/MsgCreateClient", nil) cdc.RegisterConcrete(MsgUpdateClient{}, "ibc/client/MsgUpdateClient", nil) - cdc.RegisterConcrete(State{}, "ibc/client/State", nil) - - cdc.RegisterConcrete(tendermint.ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) - cdc.RegisterConcrete(tendermint.Committer{}, "ibc/client/tendermint/Committer", nil) - cdc.RegisterConcrete(tendermint.Header{}, "ibc/client/tendermint/Header", nil) - cdc.RegisterConcrete(tendermint.Evidence{}, "ibc/client/tendermint/Evidence", nil) SetSubModuleCodec(cdc) } diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go deleted file mode 100644 index 6ae3d0552693..000000000000 --- a/x/ibc/02-client/types/state.go +++ /dev/null @@ -1,19 +0,0 @@ -package types - -// State is a type that represents the state of a client. -// Any actor holding the Stage can access on and modify that client information. -type State struct { - // Client ID - ID string `json:"id" yaml:"id"` - // Boolean that states if the client is frozen when a misbehaviour proof is - // submitted in the event of an equivocation. - Frozen bool `json:"frozen" yaml:"frozen"` -} - -// NewClientState creates a new ClientState instance -func NewClientState(id string) State { - return State{ - ID: id, - Frozen: false, - } -} diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go new file mode 100644 index 000000000000..698f527f23f6 --- /dev/null +++ b/x/ibc/07-tendermint/client_state.go @@ -0,0 +1,51 @@ +package tendermint + +import ( + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +var _ clientexported.ClientState = ClientState{} + +// ClientState from Tendermint tracks the current validator set, latest height, +// and a possible frozen height. +type ClientState struct { + // Client ID + ID string `json:"id" yaml:"id"` + // Latests block height + LastestHeight uint64 `json:"latest_height" yaml:"latest_height"` + // Block height when the client was frozen due to a misbehaviour + FrozenHeight uint64 `json:"frozen_height" yaml:"frozen_height"` +} + +// NewClientState creates a new ClientState instance +func NewClientState(id string) ClientState { + return ClientState{ + ID: id, + LastestHeight: 0, + FrozenHeight: 0, + } +} + +// GetID returns the tendermint client state identifier. +func (cs ClientState) GetID() string { + return cs.ID +} + +// ClientType is tendermint. +func (cs ClientState) ClientType() clientexported.ClientType { + return clientexported.Tendermint +} + +// IsFrozen returns true if the frozen height has been set. +func (cs ClientState) IsFrozen() bool { + return cs.FrozenHeight != 0 +} + +func (cs ClientState) VerifyClientConsensusState( + height uint64, prefix commitment.PrefixI, proof commitment.ProofI, + clientID string, consensusState clientexported.ConsensusState, +) error { + + return nil +} diff --git a/x/ibc/02-client/types/tendermint/codec.go b/x/ibc/07-tendermint/codec.go similarity index 89% rename from x/ibc/02-client/types/tendermint/codec.go rename to x/ibc/07-tendermint/codec.go index a546ed313cef..ed36b8166612 100644 --- a/x/ibc/02-client/types/tendermint/codec.go +++ b/x/ibc/07-tendermint/codec.go @@ -9,6 +9,7 @@ var SubModuleCdc = codec.New() // RegisterCodec registers the Tendermint types func RegisterCodec(cdc *codec.Codec) { codec.RegisterCrypto(cdc) + cdc.RegisterConcrete(ClientState{}, "ibc/client/tendermint/ClientState", nil) cdc.RegisterConcrete(Committer{}, "ibc/client/tendermint/Committer", nil) cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) diff --git a/x/ibc/02-client/types/tendermint/committer.go b/x/ibc/07-tendermint/committer.go similarity index 57% rename from x/ibc/02-client/types/tendermint/committer.go rename to x/ibc/07-tendermint/committer.go index ccf798feb570..317c422259a0 100644 --- a/x/ibc/02-client/types/tendermint/committer.go +++ b/x/ibc/07-tendermint/committer.go @@ -3,10 +3,10 @@ package tendermint import ( tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) -var _ exported.Committer = Committer{} +var _ clientexported.Committer = Committer{} // Committer definites a Tendermint Committer type Committer struct { @@ -15,12 +15,12 @@ type Committer struct { NextValSetHash []byte `json:"next_valset_hash" yaml:"next_valset_hash"` } -// Implement exported.Committer interface -func (c Committer) ClientType() exported.ClientType { - return exported.Tendermint +// ClientType implements exported.Committer interface +func (c Committer) ClientType() clientexported.ClientType { + return clientexported.Tendermint } -// Implement exported.Committer interface +// GetHeight implements exported.Committer interface func (c Committer) GetHeight() uint64 { return c.Height } diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go similarity index 100% rename from x/ibc/02-client/types/tendermint/consensus_state.go rename to x/ibc/07-tendermint/consensus_state.go diff --git a/x/ibc/02-client/types/tendermint/consensus_state_test.go b/x/ibc/07-tendermint/consensus_state_test.go similarity index 100% rename from x/ibc/02-client/types/tendermint/consensus_state_test.go rename to x/ibc/07-tendermint/consensus_state_test.go diff --git a/x/ibc/02-client/types/tendermint/doc.go b/x/ibc/07-tendermint/doc.go similarity index 100% rename from x/ibc/02-client/types/tendermint/doc.go rename to x/ibc/07-tendermint/doc.go diff --git a/x/ibc/02-client/types/tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go similarity index 100% rename from x/ibc/02-client/types/tendermint/evidence.go rename to x/ibc/07-tendermint/evidence.go diff --git a/x/ibc/02-client/types/tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go similarity index 100% rename from x/ibc/02-client/types/tendermint/evidence_test.go rename to x/ibc/07-tendermint/evidence_test.go diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/07-tendermint/header.go similarity index 100% rename from x/ibc/02-client/types/tendermint/header.go rename to x/ibc/07-tendermint/header.go diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go similarity index 100% rename from x/ibc/02-client/types/tendermint/misbehaviour.go rename to x/ibc/07-tendermint/misbehaviour.go diff --git a/x/ibc/02-client/types/tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go similarity index 100% rename from x/ibc/02-client/types/tendermint/misbehaviour_test.go rename to x/ibc/07-tendermint/misbehaviour_test.go diff --git a/x/ibc/02-client/types/tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go similarity index 100% rename from x/ibc/02-client/types/tendermint/tendermint_test.go rename to x/ibc/07-tendermint/tendermint_test.go diff --git a/x/ibc/02-client/types/tendermint/test_utils.go b/x/ibc/07-tendermint/test_utils.go similarity index 100% rename from x/ibc/02-client/types/tendermint/test_utils.go rename to x/ibc/07-tendermint/test_utils.go From c2e66b8c7f60984e99e13f45a8f19ed304177c25 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 6 Jan 2020 15:38:56 -0300 Subject: [PATCH 02/35] build --- x/ibc/02-client/alias.go | 2 - x/ibc/02-client/client/rest/swagger.go | 2 +- x/ibc/02-client/client/utils/utils.go | 13 ++-- x/ibc/02-client/exported/exported.go | 18 +++--- x/ibc/02-client/handler.go | 2 +- x/ibc/02-client/keeper/client.go | 2 +- x/ibc/02-client/keeper/keeper.go | 21 ++++--- x/ibc/02-client/keeper/querier.go | 3 +- x/ibc/02-client/types/msgs_test.go | 2 +- x/ibc/02-client/types/querier.go | 32 +++++----- x/ibc/03-connection/types/expected_keepers.go | 3 +- x/ibc/07-tendermint/client_state.go | 63 +++++++++++++++++++ 12 files changed, 113 insertions(+), 50 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index d818bbb99572..5c87e992bfb5 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -55,7 +55,6 @@ var ( NewMsgSubmitMibehaviour = types.NewMsgSubmitMisbehaviour NewQueryClientStateParams = types.NewQueryClientStateParams NewQueryCommitmentRootParams = types.NewQueryCommitmentRootParams - NewClientState = types.NewClientState // variable aliases SubModuleCdc = types.SubModuleCdc @@ -71,5 +70,4 @@ type ( MsgSubmitMisbehaviour = types.MsgSubmitMisbehaviour QueryClientStateParams = types.QueryClientStateParams QueryCommitmentRootParams = types.QueryCommitmentRootParams - State = types.State ) diff --git a/x/ibc/02-client/client/rest/swagger.go b/x/ibc/02-client/client/rest/swagger.go index 2ff01ff34fb8..a9709ae841de 100644 --- a/x/ibc/02-client/client/rest/swagger.go +++ b/x/ibc/02-client/client/rest/swagger.go @@ -3,7 +3,7 @@ package rest import ( auth "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go index 02f1163b3d32..ddad3361675c 100644 --- a/x/ibc/02-client/client/utils/utils.go +++ b/x/ibc/02-client/client/utils/utils.go @@ -7,14 +7,15 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // QueryAllClientStates returns all the light client states. It _does not_ return // any merkle proof. -func QueryAllClientStates(cliCtx context.CLIContext, page, limit int) ([]types.State, int64, error) { +func QueryAllClientStates(cliCtx context.CLIContext, page, limit int) ([]exported.ClientState, int64, error) { params := types.NewQueryAllClientsParams(page, limit) bz, err := cliCtx.Codec.MarshalJSON(params) if err != nil { @@ -27,7 +28,7 @@ func QueryAllClientStates(cliCtx context.CLIContext, page, limit int) ([]types.S return nil, 0, err } - var clients []types.State + var clients []exported.ClientState err = cliCtx.Codec.UnmarshalJSON(res, &clients) if err != nil { return nil, 0, fmt.Errorf("failed to unmarshal light clients: %w", err) @@ -51,7 +52,7 @@ func QueryClientState( return types.StateResponse{}, err } - var clientState types.State + var clientState exported.ClientState if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &clientState); err != nil { return types.StateResponse{}, err } @@ -78,7 +79,7 @@ func QueryConsensusState( return conStateRes, err } - var cs tendermint.ConsensusState + var cs exported.ConsensusState if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &cs); err != nil { return conStateRes, err } @@ -127,7 +128,7 @@ func QueryCommitter( return types.CommitterResponse{}, err } - var committer tendermint.Committer + var committer exported.Committer if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &committer); err != nil { return types.CommitterResponse{}, err } diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index f32ae4767a2d..a3a49645ecb6 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -34,7 +34,7 @@ type ClientState interface { height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID string, + portID, channelID string, // channelEnd channel, ) error @@ -42,36 +42,36 @@ type ClientState interface { height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID string, + portID, channelID string, sequence uint64, commitmentBytes []byte, - ) + ) error VerifyPacketAcknowledgement( height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID string, + portID, channelID string, sequence uint64, acknowledgement []byte, - ) + ) error VerifyPacketAcknowledgementAbsence( height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID string, + portID, channelID string, sequence uint64, - ) + ) error VerifyNextSequenceRecv( height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID string, + portID, channelID string, nextSequenceRecv uint64, - ) + ) error } // ConsensusState is the state of the consensus process diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 0525e115a014..474e00cbc0ce 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) // HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 5a29e758ca45..844aecb2862a 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -20,7 +20,7 @@ func (k Keeper) CreateClient( ) (exported.ClientState, error) { _, found := k.GetClientState(ctx, clientID) if found { - return types.State{}, sdkerrors.Wrapf(errors.ErrClientExists, "cannot create client with ID %s", clientID) + return nil, sdkerrors.Wrapf(errors.ErrClientExists, "cannot create client with ID %s", clientID) } _, found = k.GetClientType(ctx, clientID) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 6115d5ce6d91..7717df8514e1 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -40,7 +41,7 @@ func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.Clien store := ctx.KVStore(k.storeKey) bz := store.Get(types.KeyClientState(clientID)) if bz == nil { - return types.State{}, false + return nil, false } var clientState exported.ClientState @@ -118,13 +119,13 @@ func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, // 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. -func (k Keeper) IterateClients(ctx sdk.Context, cb func(types.State) bool) { +func (k Keeper) IterateClients(ctx sdk.Context, cb func(exported.ClientState) bool) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.GetClientKeysPrefix(ibctypes.KeyClientPrefix)) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - var clientState types.State + var clientState exported.ClientState k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &clientState) if cb(clientState) { @@ -134,8 +135,8 @@ func (k Keeper) IterateClients(ctx sdk.Context, cb func(types.State) bool) { } // GetAllClients returns all stored light client State objects. -func (k Keeper) GetAllClients(ctx sdk.Context) (states []types.State) { - k.IterateClients(ctx, func(state types.State) bool { +func (k Keeper) GetAllClients(ctx sdk.Context) (states []exported.ClientState) { + k.IterateClients(ctx, func(state exported.ClientState) bool { states = append(states, state) return false }) @@ -177,7 +178,7 @@ func (k Keeper) initialize( var clientState exported.ClientState switch clientType { case exported.Tendermint: - clientState = types.NewClientState(clientID) + clientState = tendermint.NewClientState(clientID) default: panic("invalid client type") } @@ -187,12 +188,12 @@ func (k Keeper) initialize( } // freeze updates the state of the client in the event of a misbehaviour -func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { - if clientState.Frozen { - return types.State{}, sdkerrors.Wrap(errors.ErrClientFrozen, clientState.ID) +func (k Keeper) freeze(ctx sdk.Context, clientState exported.ClientState) (exported.ClientState, error) { + if clientState.IsFrozen() { + return nil, sdkerrors.Wrap(errors.ErrClientFrozen, clientState.GetID()) } - clientState.Frozen = true + // clientState.Frozen = true // FIXME: set height return clientState, nil } diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 87da8c1eea5f..d71f16a90701 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) @@ -23,7 +24,7 @@ func QuerierClients(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e start, end := client.Paginate(len(clients), params.Page, params.Limit, 100) if start < 0 || end < 0 { - clients = []types.State{} + clients = []exported.ClientState{} } else { clients = clients[start:end] } diff --git a/x/ibc/02-client/types/msgs_test.go b/x/ibc/02-client/types/msgs_test.go index 844276b3ae6b..26b09fb34d9f 100644 --- a/x/ibc/02-client/types/msgs_test.go +++ b/x/ibc/02-client/types/msgs_test.go @@ -12,7 +12,7 @@ import ( evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) var _ evidenceexported.Evidence = mockEvidence{} diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index d2862307b09b..ac39c41643bf 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -5,7 +5,7 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" - tmtypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -79,15 +79,15 @@ func NewQueryCommitterParams(id string, height uint64) QueryCommitterParams { // StateResponse defines the client response for a client state query. // It includes the commitment proof and the height of the proof. type StateResponse struct { - ClientState State `json:"client_state" yaml:"client_state"` - Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` - ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` - ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` + ClientState exported.ClientState `json:"client_state" yaml:"client_state"` + Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` + ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` + ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` } // NewClientStateResponse creates a new StateResponse instance. func NewClientStateResponse( - clientID string, clientState State, proof *merkle.Proof, height int64, + clientID string, clientState exported.ClientState, proof *merkle.Proof, height int64, ) StateResponse { return StateResponse{ ClientState: clientState, @@ -100,15 +100,15 @@ func NewClientStateResponse( // ConsensusStateResponse defines the client response for a Consensus state query. // It includes the commitment proof and the height of the proof. type ConsensusStateResponse struct { - ConsensusState tmtypes.ConsensusState `json:"consensus_state" yaml:"consensus_state"` - Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` - ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` - ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` + ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_state"` + Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` + ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` + ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` } // NewConsensusStateResponse creates a new ConsensusStateResponse instance. func NewConsensusStateResponse( - clientID string, cs tmtypes.ConsensusState, proof *merkle.Proof, height int64, + clientID string, cs exported.ConsensusState, proof *merkle.Proof, height int64, ) ConsensusStateResponse { return ConsensusStateResponse{ ConsensusState: cs, @@ -142,15 +142,15 @@ func NewRootResponse( // CommitterResponse defines the client response for a committer query // It includes the commitment proof and the height of the proof type CommitterResponse struct { - Committer tmtypes.Committer `json:"committer" yaml:"committer"` - Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` - ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` - ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` + Committer exported.Committer `json:"committer" yaml:"committer"` + Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` + ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` + ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` } // NewCommitterResponse creates a new CommitterResponse instance. func NewCommitterResponse( - clientID string, height uint64, committer tmtypes.Committer, proof *merkle.Proof, proofHeight int64, + clientID string, height uint64, committer exported.Committer, proof *merkle.Proof, proofHeight int64, ) CommitterResponse { return CommitterResponse{ Committer: committer, diff --git a/x/ibc/03-connection/types/expected_keepers.go b/x/ibc/03-connection/types/expected_keepers.go index 1a114f6f8374..3ea564899f5a 100644 --- a/x/ibc/03-connection/types/expected_keepers.go +++ b/x/ibc/03-connection/types/expected_keepers.go @@ -2,7 +2,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -10,7 +9,7 @@ import ( // ClientKeeper expected account IBC client keeper type ClientKeeper interface { GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) - GetClientState(ctx sdk.Context, clientID string) (client.State, bool) + GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) VerifyMembership( ctx sdk.Context, clientID string, height uint64, proof commitment.ProofI, path commitment.PathI, value []byte, diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 698f527f23f6..cb1a5d861937 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -49,3 +49,66 @@ func (cs ClientState) VerifyClientConsensusState( return nil } + +func (cs ClientState) VerifyConnectionState( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + connectionID string, + // connectionEnd connection, +) error { + return nil +} + +func (cs ClientState) VerifyChannelState( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + // channelEnd channel, +) error { + return nil +} + +func (cs ClientState) VerifyPacketCommitment( + height uint64, prefix commitment.PrefixI, proof commitment.ProofI, + portID, channelID string, sequence uint64, + commitmentBytes []byte, +) error { + return nil +} + +func (cs ClientState) VerifyPacketAcknowledgement( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, +) error { + return nil +} + +func (cs ClientState) VerifyPacketAcknowledgementAbsence( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, +) error { + return nil +} + +func (cs ClientState) VerifyNextSequenceRecv( + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + nextSequenceRecv uint64, +) error { + return nil +} From 7705818c108fd309cbf46bbec14f536f7b6a6b36 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 6 Jan 2020 19:16:07 -0300 Subject: [PATCH 03/35] update tests and cleanup --- x/ibc/02-client/alias.go | 19 +++--- x/ibc/02-client/exported/exported.go | 3 +- x/ibc/02-client/handler.go | 15 +++-- x/ibc/02-client/keeper/client.go | 43 ++++++------- x/ibc/02-client/keeper/client_test.go | 8 +-- x/ibc/02-client/keeper/keeper.go | 7 +- x/ibc/02-client/keeper/keeper_test.go | 13 ++-- x/ibc/02-client/types/events.go | 3 +- x/ibc/03-connection/keeper/handshake_test.go | 2 +- x/ibc/04-channel/keeper/handshake_test.go | 6 +- x/ibc/07-tendermint/codec.go | 2 - x/ibc/07-tendermint/evidence.go | 28 ++++++-- x/ibc/07-tendermint/misbehaviour.go | 67 +++++++++----------- x/ibc/07-tendermint/misbehaviour_test.go | 33 ++++------ x/ibc/20-transfer/handler_test.go | 6 +- x/ibc/20-transfer/keeper/relay_test.go | 6 +- x/ibc/module.go | 2 + 17 files changed, 133 insertions(+), 130 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index 5c87e992bfb5..f6148a83e050 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -13,15 +13,16 @@ import ( ) const ( - AttributeKeyClientID = types.AttributeKeyClientID - SubModuleName = types.SubModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - QueryAllClients = types.QueryAllClients - QueryClientState = types.QueryClientState - QueryConsensusState = types.QueryConsensusState - QueryVerifiedRoot = types.QueryVerifiedRoot + AttributeKeyClientID = types.AttributeKeyClientID + AttrbuteKeyClientType = types.AttributeKeyClientType + SubModuleName = types.SubModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryAllClients = types.QueryAllClients + QueryClientState = types.QueryClientState + QueryConsensusState = types.QueryConsensusState + QueryVerifiedRoot = types.QueryVerifiedRoot ) var ( diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index a3a49645ecb6..dc0bdc12e3aa 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -93,8 +93,9 @@ type ConsensusState interface { // Misbehaviour defines a specific consensus kind and an evidence type Misbehaviour interface { + evidenceexported.Evidence ClientType() ClientType - GetEvidence() evidenceexported.Evidence + GetClientID() string } // Header is the consensus state update information diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 474e00cbc0ce..97d91a0363eb 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient @@ -25,6 +25,7 @@ func HandleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) (*sdk sdk.NewEvent( EventTypeCreateClient, sdk.NewAttribute(AttributeKeyClientID, msg.ClientID), + sdk.NewAttribute(AttrbuteKeyClientType, msg.ClientType), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -49,6 +50,7 @@ func HandleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) (*sdk sdk.NewEvent( EventTypeUpdateClient, sdk.NewAttribute(AttributeKeyClientID, msg.ClientID), + sdk.NewAttribute(AttrbuteKeyClientType, msg.Header.ClientType().String()), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -66,12 +68,11 @@ func HandleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) (*sdk // light client misbehaviour. func HandlerClientMisbehaviour(k Keeper) evidence.Handler { return func(ctx sdk.Context, evidence evidenceexported.Evidence) error { - switch e := evidence.(type) { - case tendermint.Misbehaviour: - return k.CheckMisbehaviourAndUpdateState(ctx, evidence) - - default: - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC client evidence type: %T", e) + misbehaviour, ok := evidence.(exported.Misbehaviour) + if !ok { + return errors.ErrInvalidEvidence } + + return k.CheckMisbehaviourAndUpdateState(ctx, misbehaviour) } } diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 844aecb2862a..3b0f6b3bf8d7 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -5,7 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" @@ -28,7 +27,11 @@ func (k Keeper) CreateClient( panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) } - clientState := k.initialize(ctx, clientID, clientType, consensusState) + clientState, err := k.initialize(ctx, clientID, clientType, consensusState) + if err != nil { + return nil, sdkerrors.Wrapf(err, "cannot create client with ID %s", clientID) + } + k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) @@ -77,47 +80,41 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H // CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the // client if so. -// -// NOTE: In the first implementation, only Tendermint misbehaviour evidence is -// supported. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, evidence evidenceexported.Evidence) error { - misbehaviour, ok := evidence.(tendermint.Misbehaviour) - if !ok { - return sdkerrors.Wrap(errors.ErrInvalidClientType, "consensus type is not Tendermint") - } - - clientState, found := k.GetClientState(ctx, misbehaviour.ClientID) +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error { + clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID()) if !found { - return sdkerrors.Wrap(errors.ErrClientNotFound, misbehaviour.ClientID) + return sdkerrors.Wrap(errors.ErrClientNotFound, misbehaviour.GetClientID()) } - committer, found := k.GetCommitter(ctx, misbehaviour.ClientID, uint64(misbehaviour.GetHeight())) + committer, found := k.GetCommitter(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight())) if !found { return errors.ErrCommitterNotFound } - tmCommitter, ok := committer.(tendermint.Committer) - if !ok { - return sdkerrors.Wrap(errors.ErrInvalidCommitter, "committer type is not Tendermint") - } - if err := tendermint.CheckMisbehaviour(tmCommitter, misbehaviour); err != nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + var err error + switch e := misbehaviour.(type) { + case tendermint.Evidence: + clientState, err = tendermint.CheckMisbehaviourAndUpdateState(clientState, committer, misbehaviour) + + default: + err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC client evidence type: %T", e) } - clientState, err := k.freeze(ctx, clientState) if err != nil { return err } k.SetClientState(ctx, clientState) - k.Logger(ctx).Info(fmt.Sprintf("client %s frozen due to misbehaviour", misbehaviour.ClientID)) + k.Logger(ctx).Info(fmt.Sprintf("client %s frozen due to misbehaviour", misbehaviour.GetClientID())) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSubmitMisbehaviour, - sdk.NewAttribute(types.AttributeKeyClientID, misbehaviour.ClientID), + sdk.NewAttribute(types.AttributeKeyClientID, misbehaviour.GetClientID()), + sdk.NewAttribute(types.AttributeKeyClientType, misbehaviour.ClientType().String()), ), ) return nil + } diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index c99757d3eacb..8d3c9e39f371 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -8,8 +8,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -23,10 +22,7 @@ func (suite *KeeperTestSuite) TestCreateClient() { suite.NoError(err, "CreateClient failed") // Test ClientState stored correctly - expectedState := types.State{ - ID: testClientID, - Frozen: false, - } + expectedState := tendermint.NewClientState(testClientID) require.Equal(suite.T(), expectedState, state, "Incorrect ClientState returned") // Test ClientType and VerifiedRoot stored correctly diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 7717df8514e1..4e64d850df5d 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -174,17 +174,18 @@ func (k Keeper) SetCommitter(ctx sdk.Context, clientID string, height uint64, co func (k Keeper) initialize( ctx sdk.Context, clientID string, clientType exported.ClientType, consensusState exported.ConsensusState, -) exported.ClientState { +) (exported.ClientState, error) { var clientState exported.ClientState + switch clientType { case exported.Tendermint: clientState = tendermint.NewClientState(clientID) default: - panic("invalid client type") + return nil, errors.ErrInvalidClientType } k.SetConsensusState(ctx, clientID, consensusState) - return clientState + return clientState, nil } // freeze updates the state of the client in the event of a misbehaviour diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 2c74a5a22f7c..1feaa75a4215 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -12,8 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "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" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/stretchr/testify/require" @@ -67,7 +66,7 @@ func TestKeeperTestSuite(t *testing.T) { } func (suite *KeeperTestSuite) TestSetClientState() { - clientState := types.NewClientState(testClientID) + clientState := tendermint.NewClientState(testClientID) suite.keeper.SetClientState(suite.ctx, clientState) retrievedState, ok := suite.keeper.GetClientState(suite.ctx, testClientID) @@ -152,10 +151,10 @@ func (suite KeeperTestSuite) TestSetCommitter() { } func (suite KeeperTestSuite) TestGetAllClients() { - expClients := []types.State{ - types.NewClientState(testClientID2), - types.NewClientState(testClientID3), - types.NewClientState(testClientID), + expClients := []exported.ClientState{ + tendermint.NewClientState(testClientID2), + tendermint.NewClientState(testClientID3), + tendermint.NewClientState(testClientID), } for i := range expClients { diff --git a/x/ibc/02-client/types/events.go b/x/ibc/02-client/types/events.go index 6b02bfa5eaf8..ea0dcc7d6606 100644 --- a/x/ibc/02-client/types/events.go +++ b/x/ibc/02-client/types/events.go @@ -8,7 +8,8 @@ import ( // IBC client events const ( - AttributeKeyClientID = "client_id" + AttributeKeyClientID = "client_id" + AttributeKeyClientType = "client_type" ) // IBC client events vars diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index 52c53df884d5..a31c1e115aea 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -6,7 +6,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 4e39b906eb44..2e47a3826a72 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -6,9 +6,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - clienttypestm "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -20,7 +20,7 @@ func (suite *KeeperTestSuite) createClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - consensusState := clienttypestm.ConsensusState{ + consensusState := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), @@ -42,7 +42,7 @@ func (suite *KeeperTestSuite) updateClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - state := clienttypestm.ConsensusState{ + state := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), diff --git a/x/ibc/07-tendermint/codec.go b/x/ibc/07-tendermint/codec.go index ed36b8166612..e3b4ddcc8844 100644 --- a/x/ibc/07-tendermint/codec.go +++ b/x/ibc/07-tendermint/codec.go @@ -8,12 +8,10 @@ var SubModuleCdc = codec.New() // RegisterCodec registers the Tendermint types func RegisterCodec(cdc *codec.Codec) { - codec.RegisterCrypto(cdc) cdc.RegisterConcrete(ClientState{}, "ibc/client/tendermint/ClientState", nil) cdc.RegisterConcrete(Committer{}, "ibc/client/tendermint/Committer", nil) cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) - cdc.RegisterConcrete(Misbehaviour{}, "ibc/client/tendermint/Misbehaviour", nil) cdc.RegisterConcrete(Evidence{}, "ibc/client/tendermint/Evidence", nil) } diff --git a/x/ibc/07-tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go index bc60b413b4e6..ee89c711be7f 100644 --- a/x/ibc/07-tendermint/evidence.go +++ b/x/ibc/07-tendermint/evidence.go @@ -9,17 +9,33 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) -var _ evidenceexported.Evidence = Evidence{} +var ( + _ evidenceexported.Evidence = Evidence{} + _ clientexported.Misbehaviour = Evidence{} +) // Evidence is a wrapper over tendermint's DuplicateVoteEvidence // that implements Evidence interface expected by ICS-02 type Evidence struct { - Header1 Header `json:"header1" yaml:"header1"` - Header2 Header `json:"header2" yaml:"header2"` - ChainID string `json:"chain_id" yaml:"chain_id"` + ClientID string `json:"client_id" yaml:"client_id"` + Header1 Header `json:"header1" yaml:"header1"` + Header2 Header `json:"header2" yaml:"header2"` + ChainID string `json:"chain_id" yaml:"chain_id"` +} + +// ClientType is Tendermint light client +func (ev Evidence) ClientType() clientexported.ClientType { + return clientexported.Tendermint +} + +// GetClientID returns the ID of the client that committed a misbehaviour. +func (ev Evidence) GetClientID() string { + return ev.ClientID } // Route implements Evidence interface @@ -53,6 +69,10 @@ func (ev Evidence) GetHeight() int64 { // ValidateBasic implements Evidence interface func (ev Evidence) ValidateBasic() error { + if err := host.DefaultClientIdentifierValidator(ev.ClientID); err != nil { + return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + } + // ValidateBasic on both validators if err := ev.Header1.ValidateBasic(ev.ChainID); err != nil { return sdkerrors.Wrap( diff --git a/x/ibc/07-tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go index 36c8e14ce8ab..7ce947ef556d 100644 --- a/x/ibc/07-tendermint/misbehaviour.go +++ b/x/ibc/07-tendermint/misbehaviour.go @@ -2,50 +2,43 @@ package tendermint import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "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/types/errors" - host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) -var _ exported.Misbehaviour = Misbehaviour{} -var _ evidenceexported.Evidence = Misbehaviour{} +// CheckMisbehaviourAndUpdateState +func CheckMisbehaviourAndUpdateState( + clientState clientexported.ClientState, committer clientexported.Committer, + misbehaviour clientexported.Misbehaviour, +) (clientexported.ClientState, error) { -// Misbehaviour contains evidence that a light client submitted a different header from -// a full node at the same height. -type Misbehaviour struct { - *Evidence - ClientID string `json:"client_id" yaml:"client_id"` -} - -// ClientType is Tendermint light client -func (m Misbehaviour) ClientType() exported.ClientType { - return exported.Tendermint -} - -// GetEvidence returns the evidence to handle a light client misbehaviour -func (m Misbehaviour) GetEvidence() evidenceexported.Evidence { - return m.Evidence -} + tmClientState, ok := clientState.(ClientState) + if !ok { + return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "client state type is not Tendermint") + } -// ValidateBasic performs the basic validity checks for the evidence and the -// client ID. -func (m Misbehaviour) ValidateBasic() error { - if m.Evidence == nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, "evidence is empty") + tmCommitter, ok := committer.(Committer) + if !ok { + return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "committer type is not Tendermint") } - if err := m.Evidence.ValidateBasic(); err != nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + + tmEvidence, ok := misbehaviour.(Evidence) + if !ok { + return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "committer type is not Tendermint") } - if err := host.DefaultClientIdentifierValidator(m.ClientID); err != nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + + if err := CheckMisbehaviour(tmCommitter, tmEvidence); err != nil { + return nil, sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) } - return nil + + tmClientState.FrozenHeight = uint64(tmEvidence.GetHeight()) + + return clientState, nil } // CheckMisbehaviour checks if the evidence provided is a valid light client misbehaviour -func CheckMisbehaviour(trustedCommitter Committer, m Misbehaviour) error { - if err := m.ValidateBasic(); err != nil { +func CheckMisbehaviour(trustedCommitter Committer, evidence Evidence) error { + if err := evidence.ValidateBasic(); err != nil { return err } @@ -55,14 +48,14 @@ func CheckMisbehaviour(trustedCommitter Committer, m Misbehaviour) error { // check that the validator sets on both headers are valid given the last trusted validatorset // less than or equal to evidence height if err := trustedValSet.VerifyFutureCommit( - m.Evidence.Header1.ValidatorSet, m.Evidence.ChainID, - m.Evidence.Header1.Commit.BlockID, m.Evidence.Header1.Height, m.Evidence.Header1.Commit, + evidence.Header1.ValidatorSet, evidence.ChainID, + evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit, ); err != nil { return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 1 has too much change from last known committer: %v", err) } if err := trustedValSet.VerifyFutureCommit( - m.Evidence.Header2.ValidatorSet, m.Evidence.ChainID, - m.Evidence.Header2.Commit.BlockID, m.Evidence.Header2.Height, m.Evidence.Header2.Commit, + evidence.Header2.ValidatorSet, evidence.ChainID, + evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit, ); err != nil { return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 2 has too much change from last known committer: %v", err) } diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index f69a6d12884e..dbe26cdc0dbb 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -17,43 +17,41 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string evidence *Evidence - clientID string expErr bool }{ { "valid misbehavior", &Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", false, }, { "nil evidence", nil, - "gaiamainnet", true, }, { "invalid evidence", &Evidence{ - Header1: suite.header, - Header2: suite.header, - ChainID: "gaia", + Header1: suite.header, + Header2: suite.header, + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", true, }, { "invalid ClientID", &Evidence{ - Header1: MakeHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), - Header2: MakeHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), - ChainID: "gaia123?", + Header1: MakeHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), + Header2: MakeHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), + ChainID: "gaia123?", + ClientID: "gaia123?", }, - "gaia123?", true, }, } @@ -61,12 +59,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { for _, tc := range testCases { tc := tc // pin for scopelint suite.Run(tc.name, func() { - misbehaviour := Misbehaviour{ - Evidence: tc.evidence, - ClientID: tc.clientID, - } - - err := misbehaviour.ValidateBasic() + err := tc.evidence.ValidateBasic() if tc.expErr { suite.Error(err, "Invalid Misbehaviour passed ValidateBasic") diff --git a/x/ibc/20-transfer/handler_test.go b/x/ibc/20-transfer/handler_test.go index d3db1ed739fc..36f9b7c21e8d 100644 --- a/x/ibc/20-transfer/handler_test.go +++ b/x/ibc/20-transfer/handler_test.go @@ -12,9 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienttypestm "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -81,7 +81,7 @@ func (suite *HandlerTestSuite) createClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - consensusState := clienttypestm.ConsensusState{ + consensusState := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), @@ -101,7 +101,7 @@ func (suite *HandlerTestSuite) updateClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - state := clienttypestm.ConsensusState{ + state := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), diff --git a/x/ibc/20-transfer/keeper/relay_test.go b/x/ibc/20-transfer/keeper/relay_test.go index 79b84c13716a..2371ade338f5 100644 --- a/x/ibc/20-transfer/keeper/relay_test.go +++ b/x/ibc/20-transfer/keeper/relay_test.go @@ -6,7 +6,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - clienttypestm "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" @@ -22,7 +22,7 @@ func (suite *KeeperTestSuite) createClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - consensusState := clienttypestm.ConsensusState{ + consensusState := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), @@ -42,7 +42,7 @@ func (suite *KeeperTestSuite) updateClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - state := clienttypestm.ConsensusState{ + state := tendermint.ConsensusState{ ChainID: testChainID, Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), diff --git a/x/ibc/module.go b/x/ibc/module.go index 42da7343d6d6..c3acdb5f6b02 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -15,6 +15,7 @@ import ( client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" @@ -43,6 +44,7 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { client.RegisterCodec(cdc) connection.RegisterCodec(cdc) channel.RegisterCodec(cdc) + tendermint.RegisterCodec(cdc) transfer.RegisterCodec(cdc) commitment.RegisterCodec(cdc) } From 6404caa3fa94d1ec450e33aaea2086ae25a88f6d Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 7 Jan 2020 11:23:40 -0300 Subject: [PATCH 04/35] x/ibc/02-client/types: remove misbehaviour in favor of evidence --- x/ibc/02-client/types/msgs.go | 51 ---------- x/ibc/02-client/types/msgs_test.go | 93 ++++-------------- x/ibc/07-tendermint/client_state.go | 10 +- x/ibc/07-tendermint/evidence_test.go | 60 +++++++----- x/ibc/07-tendermint/misbehaviour_test.go | 115 +++++------------------ x/ibc/07-tendermint/update.go | 65 +++++++++++++ 6 files changed, 147 insertions(+), 247 deletions(-) create mode 100644 x/ibc/07-tendermint/update.go diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index 128c76e4144b..ba9b8972619e 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -3,7 +3,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" @@ -119,53 +118,3 @@ func (msg MsgUpdateClient) GetSignBytes() []byte { func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } - -// MsgSubmitMisbehaviour defines a message to update an IBC client -type MsgSubmitMisbehaviour struct { - ClientID string `json:"id" yaml:"id"` - Evidence evidenceexported.Evidence `json:"evidence" yaml:"evidence"` - Signer sdk.AccAddress `json:"address" yaml:"address"` -} - -// NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance -func NewMsgSubmitMisbehaviour(id string, evidence evidenceexported.Evidence, signer sdk.AccAddress) MsgSubmitMisbehaviour { - return MsgSubmitMisbehaviour{ - ClientID: id, - Evidence: evidence, - Signer: signer, - } -} - -// Route implements sdk.Msg -func (msg MsgSubmitMisbehaviour) Route() string { - return ibctypes.RouterKey -} - -// Type implements sdk.Msg -func (msg MsgSubmitMisbehaviour) Type() string { - return "submit_misbehaviour" -} - -// ValidateBasic implements sdk.Msg -func (msg MsgSubmitMisbehaviour) ValidateBasic() error { - if msg.Evidence == nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, "evidence cannot be nil") - } - if err := msg.Evidence.ValidateBasic(); err != nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) - } - if msg.Signer.Empty() { - return sdkerrors.ErrInvalidAddress - } - return host.DefaultClientIdentifierValidator(msg.ClientID) -} - -// GetSignBytes implements sdk.Msg -func (msg MsgSubmitMisbehaviour) GetSignBytes() []byte { - return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(msg)) -} - -// GetSigners implements sdk.Msg -func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Signer} -} diff --git a/x/ibc/02-client/types/msgs_test.go b/x/ibc/02-client/types/msgs_test.go index 26b09fb34d9f..09117a5f68e0 100644 --- a/x/ibc/02-client/types/msgs_test.go +++ b/x/ibc/02-client/types/msgs_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "testing" @@ -6,55 +6,27 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - cmn "github.com/tendermint/tendermint/libs/common" sdk "github.com/cosmos/cosmos-sdk/types" - evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) -var _ evidenceexported.Evidence = mockEvidence{} -var _ evidenceexported.Evidence = mockBadEvidence{} - -const mockStr = "mock" - -// mock GoodEvidence -type mockEvidence struct{} - -// Implement Evidence interface -func (me mockEvidence) Route() string { return mockStr } -func (me mockEvidence) Type() string { return mockStr } -func (me mockEvidence) String() string { return mockStr } -func (me mockEvidence) Hash() cmn.HexBytes { return cmn.HexBytes([]byte(mockStr)) } -func (me mockEvidence) ValidateBasic() error { return nil } -func (me mockEvidence) GetHeight() int64 { return 3 } - -// mock bad evidence -type mockBadEvidence struct { - mockEvidence -} - -// Override ValidateBasic -func (mbe mockBadEvidence) ValidateBasic() error { - return errors.ErrInvalidEvidence -} - func TestMsgCreateClientValidateBasic(t *testing.T) { cs := tendermint.ConsensusState{} privKey := secp256k1.GenPrivKey() signer := sdk.AccAddress(privKey.PubKey().Address()) - testMsgs := []MsgCreateClient{ - NewMsgCreateClient(exported.ClientTypeTendermint, exported.ClientTypeTendermint, cs, signer), // valid msg - NewMsgCreateClient("badClient", exported.ClientTypeTendermint, cs, signer), // invalid client id - NewMsgCreateClient("goodChain", "bad_type", cs, signer), // invalid client type - NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, nil, signer), // nil Consensus State - NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, cs, sdk.AccAddress{}), // empty signer + testMsgs := []types.MsgCreateClient{ + types.NewMsgCreateClient(exported.ClientTypeTendermint, exported.ClientTypeTendermint, cs, signer), // valid msg + types.NewMsgCreateClient("badClient", exported.ClientTypeTendermint, cs, signer), // invalid client id + types.NewMsgCreateClient("goodChain", "bad_type", cs, signer), // invalid client type + types.NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, nil, signer), // nil Consensus State + types.NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, cs, sdk.AccAddress{}), // empty signer } cases := []struct { - msg MsgCreateClient + msg types.MsgCreateClient expPass bool errMsg string }{ @@ -78,15 +50,15 @@ func TestMsgCreateClientValidateBasic(t *testing.T) { func TestMsgUpdateClient(t *testing.T) { privKey := secp256k1.GenPrivKey() signer := sdk.AccAddress(privKey.PubKey().Address()) - testMsgs := []MsgUpdateClient{ - NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, signer), // valid msg - NewMsgUpdateClient("badClient", tendermint.Header{}, signer), // bad client id - NewMsgUpdateClient(exported.ClientTypeTendermint, nil, signer), // nil Header - NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, sdk.AccAddress{}), // empty address + testMsgs := []types.MsgUpdateClient{ + types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, signer), // valid msg + types.NewMsgUpdateClient("badClient", tendermint.Header{}, signer), // bad client id + types.NewMsgUpdateClient(exported.ClientTypeTendermint, nil, signer), // nil Header + types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, sdk.AccAddress{}), // empty address } cases := []struct { - msg MsgUpdateClient + msg types.MsgUpdateClient expPass bool errMsg string }{ @@ -105,36 +77,3 @@ func TestMsgUpdateClient(t *testing.T) { } } } - -func TestMsgSubmitMisbehaviour(t *testing.T) { - privKey := secp256k1.GenPrivKey() - signer := sdk.AccAddress(privKey.PubKey().Address()) - testMsgs := []MsgSubmitMisbehaviour{ - NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockEvidence{}, signer), // valid msg - NewMsgSubmitMisbehaviour("badClient", mockEvidence{}, signer), // bad client id - NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, nil, signer), // nil evidence - NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockBadEvidence{}, signer), // invalid evidence - NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockEvidence{}, sdk.AccAddress{}), // empty signer - } - - cases := []struct { - msg MsgSubmitMisbehaviour - expPass bool - errMsg string - }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "invalid client id passed"}, - {testMsgs[2], false, "Nil Evidence passed"}, - {testMsgs[3], false, "Invalid Evidence passed"}, - {testMsgs[4], false, "Empty address passed"}, - } - - for i, tc := range cases { - err := tc.msg.ValidateBasic() - if tc.expPass { - require.Nil(t, err, "Msg %d failed: %v", i, err) - } else { - require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index cb1a5d861937..5a061b18a6b4 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -12,8 +12,8 @@ var _ clientexported.ClientState = ClientState{} type ClientState struct { // Client ID ID string `json:"id" yaml:"id"` - // Latests block height - LastestHeight uint64 `json:"latest_height" yaml:"latest_height"` + // Latest block height + LatestHeight uint64 `json:"latest_height" yaml:"latest_height"` // Block height when the client was frozen due to a misbehaviour FrozenHeight uint64 `json:"frozen_height" yaml:"frozen_height"` } @@ -21,9 +21,9 @@ type ClientState struct { // NewClientState creates a new ClientState instance func NewClientState(id string) ClientState { return ClientState{ - ID: id, - LastestHeight: 0, - FrozenHeight: 0, + ID: id, + LatestHeight: 0, + FrozenHeight: 0, } } diff --git a/x/ibc/07-tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go index 09f40499b458..d785de21bd88 100644 --- a/x/ibc/07-tendermint/evidence_test.go +++ b/x/ibc/07-tendermint/evidence_test.go @@ -36,9 +36,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "valid evidence", Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) {}, false, @@ -46,9 +47,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "wrong chainID on header1", Evidence{ - Header1: suite.header, - Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), - ChainID: "ethermint", + Header1: suite.header, + Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), + ChainID: "ethermint", + ClientID: "gaiamainnet", }, func(ev *Evidence) {}, true, @@ -56,9 +58,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "wrong chainID on header2", Evidence{ - Header1: suite.header, - Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) {}, true, @@ -66,9 +69,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "mismatched heights", Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 6, suite.valSet, bothValSet, signers), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("gaia", 6, suite.valSet, bothValSet, signers), + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) {}, true, @@ -76,9 +80,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "same block id", Evidence{ - Header1: suite.header, - Header2: suite.header, - ChainID: "gaia", + Header1: suite.header, + Header2: suite.header, + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) {}, true, @@ -86,9 +91,10 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "header doesn't have 2/3 majority", Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) { // voteSet contains only altVal which is less than 2/3 of total power (4/14) @@ -104,15 +110,27 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "validators sign off on wrong commit", Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + Header1: suite.header, + Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, func(ev *Evidence) { ev.Header2.Commit.BlockID = makeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) }, true, }, + { + "invalid ClientID", + Evidence{ + Header1: MakeHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), + Header2: MakeHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), + ChainID: "gaia123?", + ClientID: "gaia123?", + }, + func(ev *Evidence) {}, + true, + }, } for _, tc := range testCases { diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index dbe26cdc0dbb..b0295b0a67c5 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -6,70 +6,6 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) -func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { - altPrivVal := tmtypes.NewMockPV() - altVal := tmtypes.NewValidator(altPrivVal.GetPubKey(), 4) - - // Create bothValSet with both suite validator and altVal - bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - - signers := []tmtypes.PrivValidator{suite.privVal} - testCases := []struct { - name string - evidence *Evidence - expErr bool - }{ - { - "valid misbehavior", - &Evidence{ - Header1: suite.header, - Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", - ClientID: "gaiamainnet", - }, - false, - }, - { - "nil evidence", - nil, - true, - }, - { - "invalid evidence", - &Evidence{ - Header1: suite.header, - Header2: suite.header, - ChainID: "gaia", - ClientID: "gaiamainnet", - }, - true, - }, - { - "invalid ClientID", - &Evidence{ - Header1: MakeHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), - Header2: MakeHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), - ChainID: "gaia123?", - ClientID: "gaia123?", - }, - true, - }, - } - - for _, tc := range testCases { - tc := tc // pin for scopelint - suite.Run(tc.name, func() { - err := tc.evidence.ValidateBasic() - - if tc.expErr { - suite.Error(err, "Invalid Misbehaviour passed ValidateBasic") - } else { - suite.NoError(err, "Valid Misbehaviour failed ValidateBasic: %v", err) - } - }) - } -} - func (suite *TendermintTestSuite) TestCheckMisbehaviour() { altPrivVal := tmtypes.NewMockPV() altVal := tmtypes.NewValidator(altPrivVal.GetPubKey(), 4) @@ -97,48 +33,47 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { testCases := []struct { name string - evidence *Evidence - clientID string + evidence Evidence expErr bool }{ { "trusting period misbehavior should pass", - &Evidence{ - Header1: MakeHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), - Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + Evidence{ + Header1: MakeHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), + Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", false, }, { "first valset has too much change", - &Evidence{ - Header1: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + Evidence{ + Header1: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), + Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", true, }, { "second valset has too much change", - &Evidence{ - Header1: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", + Evidence{ + Header1: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", true, }, { "both valsets have too much change", - &Evidence{ - Header1: MakeHeader("gaia", 5, altValSet, altValSet, altSigners), - Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", + Evidence{ + Header1: MakeHeader("gaia", 5, altValSet, altValSet, altSigners), + Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), + ChainID: "gaia", + ClientID: "gaiamainnet", }, - "gaiamainnet", true, }, } @@ -146,13 +81,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { for _, tc := range testCases { tc := tc // pin for scopelint suite.Run(tc.name, func() { - misbehaviour := Misbehaviour{ - Evidence: tc.evidence, - ClientID: tc.clientID, - } - - err := CheckMisbehaviour(committer, misbehaviour) - + err := CheckMisbehaviour(committer, tc.evidence) if tc.expErr { suite.Error(err, "CheckMisbehaviour passed unexpectedly") } else { diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go new file mode 100644 index 000000000000..dca755feeccc --- /dev/null +++ b/x/ibc/07-tendermint/update.go @@ -0,0 +1,65 @@ +package tendermint + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// CheckValidityAndUpdateState checks if the provided header is valid and updates +// the consensus state if appropriate +func CheckValidityAndUpdateState( + clientState clientexported.ClientState, header clientexported.Header, +) (clientexported.ClientState, clientexported.ConsensusState, error) { + tmClientState, ok := clientState.(ClientState) + if !ok { + return nil, nil, sdkerrors.Wrap( + clienterrors.ErrInvalidClientType, "light client is not from Tendermint", + ) + } + + tmHeader, ok := header.(Header) + if !ok { + return nil, nil, sdkerrors.Wrap( + clienterrors.ErrInvalidHeader, "header is not from Tendermint", + ) + } + + if err := checkValidity(tmClientState, tmHeader); err != nil { + return nil, nil, err + } + + tmClientState, consensusState := update(tmClientState, tmHeader) + return tmClientState, consensusState, nil +} + +// checkValidity checks if the Tendermint header is valid +// +// CONTRACT: assumes header.Height > consensusState.Height +func checkValidity(clientState ClientState, header Header) error { + if header.GetHeight() < clientState.LatestHeight { + return sdkerrors.Wrapf( + clienterrors.ErrInvalidHeader, + "header height < latest client state height (%d < %d)", header.GetHeight(), clientState.LatestHeight, + ) + } + + // basic consistency check + if err := header.ValidateBasic(); err != nil { + return err + } + + return header.ValidatorSet.VerifyCommit(header.ChainID, header.Commit.BlockID, header.Height, header.Commit) +} + +// update the consensus state from a new header +func update(clientState ClientState, header Header) (ClientState, ConsensusState) { + clientState.LatestHeight = header.GetHeight() + consensusState := ConsensusState{ + Root: commitment.NewRoot(header.AppHash), + ValidatorSetHash: header.ValidatorSet.Hash(), + } + + return clientState, consensusState +} From 062b748f2e5e591ecbb1f551c50c87e681a3de86 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 8 Jan 2020 10:18:29 -0300 Subject: [PATCH 05/35] remove root query, update queriers, implement verification funcs --- x/ibc/02-client/alias.go | 62 +++---- x/ibc/02-client/client/cli/cli.go | 2 +- x/ibc/02-client/client/cli/query.go | 50 +---- x/ibc/02-client/client/cli/tx.go | 19 +- x/ibc/02-client/client/rest/query.go | 41 +---- x/ibc/02-client/client/rest/swagger.go | 14 +- x/ibc/02-client/client/rest/tx.go | 14 +- x/ibc/02-client/client/utils/utils.go | 35 +--- x/ibc/02-client/exported/exported.go | 9 +- x/ibc/02-client/keeper/client.go | 30 +-- x/ibc/02-client/keeper/keeper.go | 99 +--------- x/ibc/02-client/keeper/querier.go | 65 +------ x/ibc/02-client/types/errors/errors.go | 31 ++-- x/ibc/02-client/types/keys.go | 12 +- x/ibc/02-client/types/querier.go | 53 +----- x/ibc/07-tendermint/client_state.go | 193 +++++++++++++++++--- x/ibc/07-tendermint/consensus_state.go | 94 +--------- x/ibc/07-tendermint/consensus_state_test.go | 54 +----- x/ibc/07-tendermint/tendermint_test.go | 26 +-- x/ibc/07-tendermint/update.go | 8 +- x/ibc/07-tendermint/update_test.go | 47 +++++ 21 files changed, 348 insertions(+), 610 deletions(-) create mode 100644 x/ibc/07-tendermint/update_test.go diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index f6148a83e050..b23defe586eb 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -22,40 +22,33 @@ const ( QueryAllClients = types.QueryAllClients QueryClientState = types.QueryClientState QueryConsensusState = types.QueryConsensusState - QueryVerifiedRoot = types.QueryVerifiedRoot ) var ( // functions aliases - NewKeeper = keeper.NewKeeper - QuerierClients = keeper.QuerierClients - QuerierClientState = keeper.QuerierClientState - QuerierConsensusState = keeper.QuerierConsensusState - QuerierVerifiedRoot = keeper.QuerierVerifiedRoot - RegisterCodec = types.RegisterCodec - ErrClientExists = errors.ErrClientExists - ErrClientNotFound = errors.ErrClientNotFound - ErrClientFrozen = errors.ErrClientFrozen - ErrConsensusStateNotFound = errors.ErrConsensusStateNotFound - ErrInvalidConsensus = errors.ErrInvalidConsensus - ErrClientTypeNotFound = errors.ErrClientTypeNotFound - ErrInvalidClientType = errors.ErrInvalidClientType - ErrRootNotFound = errors.ErrRootNotFound - ErrInvalidHeader = errors.ErrInvalidHeader - ErrInvalidEvidence = errors.ErrInvalidEvidence - ClientStatePath = types.ClientStatePath - ClientTypePath = types.ClientTypePath - ConsensusStatePath = types.ConsensusStatePath - RootPath = types.RootPath - KeyClientState = types.KeyClientState - KeyClientType = types.KeyClientType - KeyConsensusState = types.KeyConsensusState - KeyRoot = types.KeyRoot - NewMsgCreateClient = types.NewMsgCreateClient - NewMsgUpdateClient = types.NewMsgUpdateClient - NewMsgSubmitMibehaviour = types.NewMsgSubmitMisbehaviour - NewQueryClientStateParams = types.NewQueryClientStateParams - NewQueryCommitmentRootParams = types.NewQueryCommitmentRootParams + NewKeeper = keeper.NewKeeper + QuerierClients = keeper.QuerierClients + RegisterCodec = types.RegisterCodec + ErrClientExists = errors.ErrClientExists + ErrClientNotFound = errors.ErrClientNotFound + ErrClientFrozen = errors.ErrClientFrozen + ErrConsensusStateNotFound = errors.ErrConsensusStateNotFound + ErrInvalidConsensus = errors.ErrInvalidConsensus + ErrClientTypeNotFound = errors.ErrClientTypeNotFound + ErrInvalidClientType = errors.ErrInvalidClientType + ErrRootNotFound = errors.ErrRootNotFound + ErrInvalidHeader = errors.ErrInvalidHeader + ErrInvalidEvidence = errors.ErrInvalidEvidence + ClientStatePath = types.ClientStatePath + ClientTypePath = types.ClientTypePath + ConsensusStatePath = types.ConsensusStatePath + RootPath = types.RootPath + KeyClientState = types.KeyClientState + KeyClientType = types.KeyClientType + KeyConsensusState = types.KeyConsensusState + KeyRoot = types.KeyRoot + NewMsgCreateClient = types.NewMsgCreateClient + NewMsgUpdateClient = types.NewMsgUpdateClient // variable aliases SubModuleCdc = types.SubModuleCdc @@ -65,10 +58,7 @@ var ( ) type ( - Keeper = keeper.Keeper - MsgCreateClient = types.MsgCreateClient - MsgUpdateClient = types.MsgUpdateClient - MsgSubmitMisbehaviour = types.MsgSubmitMisbehaviour - QueryClientStateParams = types.QueryClientStateParams - QueryCommitmentRootParams = types.QueryCommitmentRootParams + Keeper = keeper.Keeper + MsgCreateClient = types.MsgCreateClient + MsgUpdateClient = types.MsgUpdateClient ) diff --git a/x/ibc/02-client/client/cli/cli.go b/x/ibc/02-client/client/cli/cli.go index 4a36894faf35..757b8894f719 100644 --- a/x/ibc/02-client/client/cli/cli.go +++ b/x/ibc/02-client/client/cli/cli.go @@ -20,7 +20,6 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { GetCmdQueryClientStates(queryRoute, cdc), GetCmdQueryClientState(queryRoute, cdc), GetCmdQueryConsensusState(queryRoute, cdc), - GetCmdQueryRoot(queryRoute, cdc), GetCmdQueryHeader(cdc), GetCmdNodeConsensusState(queryRoute, cdc), GetCmdQueryPath(queryRoute, cdc), @@ -40,6 +39,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ics02ClientTxCmd.AddCommand(flags.PostCommands( GetCmdCreateClient(cdc), GetCmdUpdateClient(cdc), + GetCmdSubmitMisbehaviour(cdc), )...) return ics02ClientTxCmd diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 682758783292..1c59284baad2 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -88,45 +88,11 @@ $ %s query ibc client state [client-id] // the chain as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query func GetCmdQueryConsensusState(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "consensus-state [client-id]", - Short: "Query the latest consensus state of the client", - Long: "Query the consensus state for a particular light client", - Example: fmt.Sprintf("%s query ibc client consensus-state [client-id]", version.ClientName), - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - clientID := args[0] - if strings.TrimSpace(clientID) == "" { - return errors.New("client ID can't be blank") - } - - prove := viper.GetBool(flags.FlagProve) - - csRes, err := utils.QueryConsensusState(cliCtx, clientID, prove) - if err != nil { - return err - } - - return cliCtx.PrintOutput(csRes) - }, - } - cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") - return cmd -} - -// GetCmdQueryRoot defines the command to query a verified commitment root -func GetCmdQueryRoot(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "root [client-id] [height]", - Short: "Query a verified commitment root", - Long: strings.TrimSpace( - fmt.Sprintf(`Query an already verified commitment root at a specific height for a particular client - -Example: -$ %s query ibc client root [client-id] [height] -`, version.ClientName), - ), - Args: cobra.ExactArgs(2), + Use: "consensus-state [client-id] [height]", + Short: "Query the consensus state of a client at a given height", + Long: "Query the consensus state for a particular light client at a given height", + Example: fmt.Sprintf("%s query ibc client consensus-state [client-id] [height]", version.ClientName), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) clientID := args[0] @@ -136,17 +102,17 @@ $ %s query ibc client root [client-id] [height] height, err := strconv.ParseUint(args[1], 10, 64) if err != nil { - return fmt.Errorf("expected integer height, got: %v", args[1]) + return fmt.Errorf("expected integer height, got: %s", args[1]) } prove := viper.GetBool(flags.FlagProve) - rootRes, err := utils.QueryCommitmentRoot(cliCtx, clientID, height, prove) + csRes, err := utils.QueryConsensusState(cliCtx, clientID, height, prove) if err != nil { return err } - return cliCtx.PrintOutput(rootRes) + return cliCtx.PrintOutput(csRes) }, } cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 6081ddcdddfe..1e86de879a91 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/evidence" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" @@ -118,36 +119,34 @@ $ %s tx ibc client update [client-id] [path/to/header.json] --from node0 --home // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour func GetCmdSubmitMisbehaviour(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "misbehaviour [client-it] [path/to/evidence.json]", + Use: "misbehaviour [path/to/evidence.json]", Short: "submit a client misbehaviour", Long: strings.TrimSpace(fmt.Sprintf(`submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates: Example: -$ %s tx ibc client misbehaviour [client-id] [path/to/evidence.json] --from node0 --home ../node0/cli --chain-id $CID +$ %s tx ibc client misbehaviour [path/to/evidence.json] --from node0 --home ../node0/cli --chain-id $CID `, version.ClientName), ), - Args: cobra.ExactArgs(2), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { inBuf := bufio.NewReader(cmd.InOrStdin()) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - clientID := args[0] - - var evidence evidenceexported.Evidence - if err := cdc.UnmarshalJSON([]byte(args[1]), &evidence); err != nil { + var ev evidenceexported.Evidence + if err := cdc.UnmarshalJSON([]byte(args[0]), &ev); err != nil { fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...") // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[1]) + contents, err := ioutil.ReadFile(args[0]) if err != nil { return errors.New("neither JSON input nor path to .json file were provided") } - if err := cdc.UnmarshalJSON(contents, &evidence); err != nil { + if err := cdc.UnmarshalJSON(contents, &ev); err != nil { return errors.Wrap(err, "error unmarshalling evidence file") } } - msg := types.NewMsgSubmitMisbehaviour(clientID, evidence, cliCtx.GetFromAddress()) + msg := evidence.NewMsgSubmitEvidence(ev, cliCtx.GetFromAddress()) if err := msg.ValidateBasic(); err != nil { return err } diff --git a/x/ibc/02-client/client/rest/query.go b/x/ibc/02-client/client/rest/query.go index 3b59aeb49f85..c4d901a1206b 100644 --- a/x/ibc/02-client/client/rest/query.go +++ b/x/ibc/02-client/client/rest/query.go @@ -17,7 +17,6 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/ibc/clients", queryAllClientStatesFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/client-state", RestClientID), queryClientStateHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/roots/{%s}", RestClientID, RestRootHeight), queryRootHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/committers/{%s}", RestClientID, RestRootHeight), queryCommitterHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/header", queryHeaderHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/node-state", queryNodeConsensusStateHandlerFn(cliCtx)).Methods("GET") @@ -97,45 +96,13 @@ func queryClientStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // @Tags IBC // @Produce json // @Param client-id path string true "Client ID" +// @Param height path number true "Height" // @Param prove query boolean false "Proof of result" // @Success 200 {object} QueryConsensusState "OK" // @Failure 400 {object} rest.ErrorResponse "Invalid client id" // @Failure 500 {object} rest.ErrorResponse "Internal Server Error" // @Router /ibc/clients/{client-id}/consensus-state [get] func queryConsensusStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - clientID := vars[RestClientID] - prove := rest.ParseQueryProve(r) - - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) - if !ok { - return - } - - csRes, err := utils.QueryConsensusState(cliCtx, clientID, prove) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - cliCtx = cliCtx.WithHeight(int64(csRes.ProofHeight)) - rest.PostProcessResponse(w, cliCtx, csRes) - } -} - -// queryRootHandlerFn implements a root querying route -// -// @Summary Query client root -// @Tags IBC -// @Produce json -// @Param client-id path string true "Client ID" -// @Param height path number true "Root height" -// @Success 200 {object} QueryRoot "OK" -// @Failure 400 {object} rest.ErrorResponse "Invalid client id or height" -// @Failure 500 {object} rest.ErrorResponse "Internal Server Error" -// @Router /ibc/clients/{client-id}/roots/{height} [get] -func queryRootHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clientID := vars[RestClientID] @@ -152,14 +119,14 @@ func queryRootHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - rootRes, err := utils.QueryCommitmentRoot(cliCtx, clientID, height, prove) + csRes, err := utils.QueryConsensusState(cliCtx, clientID, height, prove) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - cliCtx = cliCtx.WithHeight(int64(rootRes.ProofHeight)) - rest.PostProcessResponse(w, cliCtx, rootRes) + cliCtx = cliCtx.WithHeight(int64(csRes.ProofHeight)) + rest.PostProcessResponse(w, cliCtx, csRes) } } diff --git a/x/ibc/02-client/client/rest/swagger.go b/x/ibc/02-client/client/rest/swagger.go index a9709ae841de..42809a112192 100644 --- a/x/ibc/02-client/client/rest/swagger.go +++ b/x/ibc/02-client/client/rest/swagger.go @@ -2,6 +2,7 @@ package rest import ( auth "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -24,11 +25,6 @@ type ( Result types.StateResponse `json:"result"` } - QueryRoot struct { - Height int64 `json:"height"` - Result types.RootResponse `json:"result"` - } - QueryNodeConsensusState struct { Height int64 `json:"height"` Result tendermint.ConsensusState `json:"result"` @@ -54,9 +50,9 @@ type ( } PostSubmitMisbehaviour struct { - Msgs []types.MsgSubmitMisbehaviour `json:"msg" yaml:"msg"` - Fee auth.StdFee `json:"fee" yaml:"fee"` - Signatures []auth.StdSignature `json:"signatures" yaml:"signatures"` - Memo string `json:"memo" yaml:"memo"` + Msgs []evidence.MsgSubmitEvidence `json:"msg" yaml:"msg"` + Fee auth.StdFee `json:"fee" yaml:"fee"` + Signatures []auth.StdSignature `json:"signatures" yaml:"signatures"` + Memo string `json:"memo" yaml:"memo"` } ) diff --git a/x/ibc/02-client/client/rest/tx.go b/x/ibc/02-client/client/rest/tx.go index 50f9a38b022f..a0cb4840879d 100644 --- a/x/ibc/02-client/client/rest/tx.go +++ b/x/ibc/02-client/client/rest/tx.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) @@ -17,7 +18,7 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/ibc/clients", createClientHandlerFn(cliCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/update", RestClientID), updateClientHandlerFn(cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/misbehaviour", RestClientID), submitMisbehaviourHandlerFn(cliCtx)).Methods("POST") + r.HandleFunc("/ibc/clients/{%s}/misbehaviour", submitMisbehaviourHandlerFn(cliCtx)).Methods("POST") } // createClientHandlerFn implements a create client handler @@ -120,7 +121,6 @@ func updateClientHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // @Tags IBC // @Accept json // @Produce json -// @Param client-id path string true "Client ID" // @Param body body rest.SubmitMisbehaviourReq true "Submit misbehaviour request body" // @Success 200 {object} PostSubmitMisbehaviour "OK" // @Failure 400 {object} rest.ErrorResponse "Invalid client id" @@ -128,9 +128,6 @@ func updateClientHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // @Router /ibc/clients/{client-id}/misbehaviour [post] func submitMisbehaviourHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - clientID := vars[RestClientID] - var req SubmitMisbehaviourReq if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { return @@ -148,12 +145,7 @@ func submitMisbehaviourHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } // create the message - msg := types.NewMsgSubmitMisbehaviour( - clientID, - req.Evidence, - fromAddr, - ) - + msg := evidence.NewMsgSubmitEvidence(req.Evidence, fromAddr) if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go index ddad3361675c..6ab42e746377 100644 --- a/x/ibc/02-client/client/utils/utils.go +++ b/x/ibc/02-client/client/utils/utils.go @@ -65,12 +65,13 @@ func QueryClientState( // QueryConsensusState queries the store to get the consensus state and a merkle // proof. func QueryConsensusState( - cliCtx context.CLIContext, clientID string, prove bool) (types.ConsensusStateResponse, error) { + cliCtx context.CLIContext, clientID string, height uint64, prove bool, +) (types.ConsensusStateResponse, error) { var conStateRes types.ConsensusStateResponse req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyConsensusState(clientID), + Data: types.KeyConsensusState(clientID, height), Prove: prove, } @@ -87,32 +88,6 @@ func QueryConsensusState( return types.NewConsensusStateResponse(clientID, cs, res.Proof, res.Height), nil } -// QueryCommitmentRoot queries the store to get the commitment root and a merkle -// proof. -func QueryCommitmentRoot( - cliCtx context.CLIContext, clientID string, height uint64, prove bool, -) (types.RootResponse, error) { - req := abci.RequestQuery{ - Path: "store/ibc/key", - Data: types.KeyRoot(clientID, height), - Prove: prove, - } - - res, err := cliCtx.QueryABCI(req) - if err != nil { - return types.RootResponse{}, err - } - - var root commitment.Root - if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &root); err != nil { - return types.RootResponse{}, err - } - - rootRes := types.NewRootResponse(clientID, height, root, res.Proof, res.Height) - - return rootRes, nil -} - // QueryCommitter queries the store to get the committer and a merkle proof func QueryCommitter( cliCtx context.CLIContext, clientID string, height uint64, prove bool, @@ -205,10 +180,8 @@ func QueryNodeConsensusState(cliCtx context.CLIContext) (tendermint.ConsensusSta } state := tendermint.ConsensusState{ - ChainID: commit.ChainID, - Height: uint64(commit.Height), Root: commitment.NewRoot(commit.AppHash), - NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + ValidatorSetHash: tmtypes.NewValidatorSet(validators.Validators).Hash(), } return state, height, nil diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index dc0bdc12e3aa..fde0856d0ebe 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -12,6 +12,7 @@ import ( type ClientState interface { GetID() string ClientType() ClientType + GetSequence() uint64 IsFrozen() bool // State verification functions @@ -77,18 +78,12 @@ type ClientState interface { // ConsensusState is the state of the consensus process type ConsensusState interface { ClientType() ClientType // Consensus kind - GetHeight() uint64 // GetRoot returns the commitment root of the consensus state, // which is used for key-value pair verification. GetRoot() commitment.RootI - // GetCommitter returns the committer that committed the consensus state - GetCommitter() Committer - - // CheckValidityAndUpdateState returns the updated consensus state - // only if the header is a descendent of this consensus state. - CheckValidityAndUpdateState(Header) (ConsensusState, error) + ValidateBasic() error } // Misbehaviour defines a specific consensus kind and an evidence diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 3b0f6b3bf8d7..88af78aa7ff2 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -32,11 +32,11 @@ func (k Keeper) CreateClient( return nil, sdkerrors.Wrapf(err, "cannot create client with ID %s", clientID) } - k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) - k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + // k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) + // k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) k.SetClientType(ctx, clientID, clientType) - k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, consensusState.GetHeight())) + k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, clientState.GetSequence())) return clientState, nil } @@ -57,24 +57,32 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H return sdkerrors.Wrapf(errors.ErrClientNotFound, "cannot update client with ID %s", clientID) } + // addittion to spec: prevent update if the client is frozen if clientState.IsFrozen() { return sdkerrors.Wrapf(errors.ErrClientFrozen, "cannot update client with ID %s", clientID) } - consensusState, found := k.GetConsensusState(ctx, clientID) - if !found { - return sdkerrors.Wrapf(errors.ErrConsensusStateNotFound, "cannot update client with ID %s", clientID) + var ( + consensusState exported.ConsensusState + err error + ) + + switch clientType { + case exported.Tendermint: + clientState, consensusState, err = tendermint.CheckValidityAndUpdateState(clientState, header, ctx.ChainID()) + default: + return sdkerrors.Wrapf(errors.ErrInvalidClientType, "cannot update client with ID %s", clientID) } - consensusState, err := consensusState.CheckValidityAndUpdateState(header) if err != nil { return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID) } - k.SetConsensusState(ctx, clientID, consensusState) - k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) - k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) - k.Logger(ctx).Info(fmt.Sprintf("client %s updated to height %d", clientID, consensusState.GetHeight())) + k.SetClientState(ctx, clientState) + k.SetConsensusState(ctx, clientID, header.GetHeight(), consensusState) + // k.SetCommitter(ctx, clientID, header.GetHeight(), consensusState.GetCommitter()) + // k.SetVerifiedRoot(ctx, clientID, header.GetHeight(), consensusState.GetRoot()) + k.Logger(ctx).Info(fmt.Sprintf("client %s updated to height %d", clientID, header.GetHeight())) return nil } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 4e64d850df5d..55889f750893 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -7,12 +7,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" 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" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -74,9 +72,9 @@ func (k Keeper) SetClientType(ctx sdk.Context, clientID string, clientType expor } // GetConsensusState creates a new client state and populates it with a given consensus state -func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) { +func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string, height uint64) (exported.ConsensusState, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyConsensusState(clientID)) + bz := store.Get(types.KeyConsensusState(clientID, height)) if bz == nil { return nil, false } @@ -87,33 +85,10 @@ func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.Co } // SetConsensusState sets a ConsensusState to a particular client -func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) { +func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, height uint64, consensusState exported.ConsensusState) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState) - store.Set(types.KeyConsensusState(clientID), bz) -} - -// GetVerifiedRoot gets a verified commitment Root from a particular height to -// a client -func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitment.RootI, bool) { - store := ctx.KVStore(k.storeKey) - - bz := store.Get(types.KeyRoot(clientID, height)) - if bz == nil { - return nil, false - } - - var root commitment.RootI - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &root) - return root, true -} - -// SetVerifiedRoot sets a verified commitment Root from a particular height to -// a client -func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, root commitment.RootI) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(root) - store.Set(types.KeyRoot(clientID, height), bz) + store.Set(types.KeyConsensusState(clientID, height), bz) } // IterateClients provides an iterator over all stored light client State @@ -176,75 +151,15 @@ func (k Keeper) initialize( consensusState exported.ConsensusState, ) (exported.ClientState, error) { var clientState exported.ClientState + height := uint64(ctx.BlockHeight()) switch clientType { case exported.Tendermint: - clientState = tendermint.NewClientState(clientID) + clientState = tendermint.NewClientState(clientID, height) default: return nil, errors.ErrInvalidClientType } - k.SetConsensusState(ctx, clientID, consensusState) + k.SetConsensusState(ctx, clientID, height, consensusState) return clientState, nil } - -// freeze updates the state of the client in the event of a misbehaviour -func (k Keeper) freeze(ctx sdk.Context, clientState exported.ClientState) (exported.ClientState, error) { - if clientState.IsFrozen() { - return nil, sdkerrors.Wrap(errors.ErrClientFrozen, clientState.GetID()) - } - - // clientState.Frozen = true // FIXME: set height - return clientState, nil -} - -// VerifyMembership state membership verification function defined by the client type -func (k Keeper) VerifyMembership( - ctx sdk.Context, - clientID string, - height uint64, // sequence - proof commitment.ProofI, - path commitment.PathI, - value []byte, -) bool { - clientState, found := k.GetClientState(ctx, clientID) - if !found { - return false - } - - if clientState.IsFrozen() { - return false - } - - root, found := k.GetVerifiedRoot(ctx, clientID, height) - if !found { - return false - } - - return proof.VerifyMembership(root, path, value) -} - -// VerifyNonMembership state non-membership function defined by the client type -func (k Keeper) VerifyNonMembership( - ctx sdk.Context, - clientID string, - height uint64, // sequence - proof commitment.ProofI, - path commitment.PathI, -) bool { - clientState, found := k.GetClientState(ctx, clientID) - if !found { - return false - } - - if clientState.IsFrozen() { - return false - } - - root, found := k.GetVerifiedRoot(ctx, clientID, height) - if !found { - return false - } - - return proof.VerifyNonMembership(root, path) -} diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index d71f16a90701..a0cca752b3a5 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -7,8 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) @@ -37,69 +37,6 @@ func QuerierClients(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e return res, nil } -// QuerierClientState defines the sdk.Querier to query the IBC client state -func QuerierClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryClientStateParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - clientState, found := k.GetClientState(ctx, params.ClientID) - if !found { - return nil, sdkerrors.Wrap(errors.ErrClientTypeNotFound, params.ClientID) - } - - bz, err := codec.MarshalJSONIndent(k.cdc, clientState) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} - -// QuerierConsensusState defines the sdk.Querier to query a consensus state -func QuerierConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryClientStateParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - consensusState, found := k.GetConsensusState(ctx, params.ClientID) - if !found { - return nil, errors.ErrConsensusStateNotFound - } - - bz, err := codec.MarshalJSONIndent(k.cdc, consensusState) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} - -// QuerierVerifiedRoot defines the sdk.Querier to query a verified commitment root -func QuerierVerifiedRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryCommitmentRootParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - root, found := k.GetVerifiedRoot(ctx, params.ClientID, params.Height) - if !found { - return nil, errors.ErrRootNotFound - } - - bz, err := codec.MarshalJSONIndent(k.cdc, root) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} - // QuerierCommitter defines the sdk.Querier to query a committer func QuerierCommitter(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { var params types.QueryCommitterParams diff --git a/x/ibc/02-client/types/errors/errors.go b/x/ibc/02-client/types/errors/errors.go index b6786a6113f6..d73790a18e0b 100644 --- a/x/ibc/02-client/types/errors/errors.go +++ b/x/ibc/02-client/types/errors/errors.go @@ -9,16 +9,23 @@ const SubModuleName string = "ibc/client" // IBC client sentinel errors var ( - ErrClientExists = sdkerrors.Register(SubModuleName, 1, "light client already exists") - ErrClientNotFound = sdkerrors.Register(SubModuleName, 2, "light client not found") - ErrClientFrozen = sdkerrors.Register(SubModuleName, 3, "light client is frozen due to misbehaviour") - ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 4, "consensus state not found") - ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 5, "invalid consensus state") - ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 6, "client type not found") - ErrInvalidClientType = sdkerrors.Register(SubModuleName, 7, "invalid client type") - ErrRootNotFound = sdkerrors.Register(SubModuleName, 8, "commitment root not found") - ErrInvalidHeader = sdkerrors.Register(SubModuleName, 9, "invalid block header") - ErrInvalidEvidence = sdkerrors.Register(SubModuleName, 10, "invalid light client misbehaviour evidence") - ErrCommitterNotFound = sdkerrors.Register(SubModuleName, 11, "commiter not found") - ErrInvalidCommitter = sdkerrors.Register(SubModuleName, 12, "invalid commiter") + ErrClientExists = sdkerrors.Register(SubModuleName, 1, "light client already exists") + ErrClientNotFound = sdkerrors.Register(SubModuleName, 2, "light client not found") + ErrClientFrozen = sdkerrors.Register(SubModuleName, 3, "light client is frozen due to misbehaviour") + ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 4, "consensus state not found") + ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 5, "invalid consensus state") + ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 6, "client type not found") + ErrInvalidClientType = sdkerrors.Register(SubModuleName, 7, "invalid client type") + ErrRootNotFound = sdkerrors.Register(SubModuleName, 8, "commitment root not found") + ErrInvalidHeader = sdkerrors.Register(SubModuleName, 9, "invalid block header") + ErrInvalidEvidence = sdkerrors.Register(SubModuleName, 10, "invalid light client misbehaviour evidence") + ErrCommitterNotFound = sdkerrors.Register(SubModuleName, 11, "commiter not found") + ErrInvalidCommitter = sdkerrors.Register(SubModuleName, 12, "invalid commiter") + ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 13, "client consensus state verification failed") + ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 14, "connection state verification failed") + ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 15, "channel state verification failed") + ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 16, "packet commitment verification failed") + ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 17, "packet acknowledgement verification failed") + ErrFailedPacketAckAbsenceVerification = sdkerrors.Register(SubModuleName, 18, "packet acknowledgement absence verification failed") + ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 18, "next sequence receive verification failed") ) diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index eeb7e8b0d8b9..3c3a83305d81 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -36,8 +36,8 @@ func ClientTypePath(clientID string) string { // ConsensusStatePath takes an Identifier and returns a Path under which to // store the consensus state of a client. -func ConsensusStatePath(clientID string) string { - return string(KeyConsensusState(clientID)) +func ConsensusStatePath(clientID string, height uint64) string { + return string(KeyConsensusState(clientID, height)) } // RootPath takes an Identifier and returns a Path under which to @@ -70,10 +70,10 @@ func KeyClientType(clientID string) []byte { // KeyConsensusState returns the store key for the consensus state of a particular // client -func KeyConsensusState(clientID string) []byte { +func KeyConsensusState(clientID string, height uint64) []byte { return append( ibctypes.KeyPrefixBytes(ibctypes.KeyConsensusStatePrefix), - []byte(consensusStatePath(clientID))..., + []byte(consensusStatePath(clientID, height))..., ) } @@ -108,8 +108,8 @@ func clientTypePath(clientID string) string { return fmt.Sprintf("clients/%s/type", clientID) } -func consensusStatePath(clientID string) string { - return fmt.Sprintf("clients/%s/consensusState", clientID) +func consensusStatePath(clientID string, height uint64) string { + return fmt.Sprintf("consensusState/%s/%d", clientID, height) } func rootPath(clientID string, height uint64) string { diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index ac39c41643bf..844c9c6db9ea 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -14,23 +14,8 @@ const ( QueryAllClients = "client_states" QueryClientState = "client_state" QueryConsensusState = "consensus_state" - QueryVerifiedRoot = "roots" ) -// QueryClientStateParams defines the params for the following queries: -// - 'custom/ibc/clients//client_state' -// - 'custom/ibc/clients//consensus_state' -type QueryClientStateParams struct { - ClientID string -} - -// NewQueryClientStateParams creates a new QueryClientStateParams instance -func NewQueryClientStateParams(id string) QueryClientStateParams { - return QueryClientStateParams{ - ClientID: id, - } -} - // QueryAllClientsParams defines the parameters necessary for querying for all // light client states. type QueryAllClientsParams struct { @@ -46,21 +31,6 @@ func NewQueryAllClientsParams(page, limit int) QueryAllClientsParams { } } -// QueryCommitmentRootParams defines the params for the following queries: -// - 'custom/ibc/clients//roots/' -type QueryCommitmentRootParams struct { - ClientID string - Height uint64 -} - -// NewQueryCommitmentRootParams creates a new QueryCommitmentRootParams instance -func NewQueryCommitmentRootParams(id string, height uint64) QueryCommitmentRootParams { - return QueryCommitmentRootParams{ - ClientID: id, - Height: height, - } -} - // QueryCommitterParams defines the params for the following queries: // - 'custom/ibc/clients//committers/' type QueryCommitterParams struct { @@ -113,32 +83,11 @@ func NewConsensusStateResponse( return ConsensusStateResponse{ ConsensusState: cs, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ConsensusStatePath(clientID), "/")), + ProofPath: commitment.NewPath(strings.Split(ConsensusStatePath(clientID, uint64(height)), "/")), ProofHeight: uint64(height), } } -// RootResponse defines the client response for a commitment root query. -// It includes the commitment proof and the height of the proof. -type RootResponse struct { - Root commitment.Root `json:"root" yaml:"root"` - Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` - ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` - ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` -} - -// NewRootResponse creates a new RootResponse instance. -func NewRootResponse( - clientID string, height uint64, root commitment.Root, proof *merkle.Proof, proofHeight int64, -) RootResponse { - return RootResponse{ - Root: root, - Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(RootPath(clientID, height), "/")), - ProofHeight: uint64(proofHeight), - } -} - // CommitterResponse defines the client response for a committer query // It includes the commitment proof and the height of the proof type CommitterResponse struct { diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 5a061b18a6b4..fc00e46f16f0 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,8 +1,12 @@ package tendermint import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) var _ clientexported.ClientState = ClientState{} @@ -19,10 +23,10 @@ type ClientState struct { } // NewClientState creates a new ClientState instance -func NewClientState(id string) ClientState { +func NewClientState(id string, latestHeight uint64) ClientState { return ClientState{ ID: id, - LatestHeight: 0, + LatestHeight: latestHeight, FrozenHeight: 0, } } @@ -37,68 +41,187 @@ func (cs ClientState) ClientType() clientexported.ClientType { return clientexported.Tendermint } +// GetSequence returns latest block height. +func (cs ClientState) GetSequence() uint64 { + return cs.LatestHeight +} + // IsFrozen returns true if the frozen height has been set. func (cs ClientState) IsFrozen() bool { return cs.FrozenHeight != 0 } func (cs ClientState) VerifyClientConsensusState( - height uint64, prefix commitment.PrefixI, proof commitment.ProofI, + cdc *codec.Codec, height uint64, + prefix commitment.PrefixI, proof commitment.ProofI, clientID string, consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + bz, err := cdc.MarshalBinaryBare(consensusState) + if err != nil { + return err + } + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + return clienterrors.ErrFailedClientConsensusStateVerification + } return nil } func (cs ClientState) VerifyConnectionState( - height uint64, - prefix commitment.PrefixI, - proof commitment.ProofI, + cdc *codec.Codec, height uint64, + prefix commitment.PrefixI, proof commitment.ProofI, connectionID string, // connectionEnd connection, + consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + bz, err := cdc.MarshalBinaryBare(consensusState) + if err != nil { + return err + } + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + return clienterrors.ErrFailedConnectionStateVerification + } + return nil } func (cs ClientState) VerifyChannelState( - height uint64, - prefix commitment.PrefixI, - proof commitment.ProofI, - portID, - channelID string, + cdc *codec.Codec, height uint64, + prefix commitment.PrefixI, proof commitment.ProofI, + portID, channelID string, // channelEnd channel, + consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + bz, err := cdc.MarshalBinaryBare(consensusState) + if err != nil { + return err + } + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + return clienterrors.ErrFailedChannelStateVerification + } + return nil } func (cs ClientState) VerifyPacketCommitment( - height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - portID, channelID string, sequence uint64, - commitmentBytes []byte, + height uint64, + prefix commitment.PrefixI, proof commitment.ProofI, + portID, channelID string, + sequence uint64, commitmentBytes []byte, + consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); !ok { + return clienterrors.ErrFailedPacketCommitmentVerification + } + return nil } func (cs ClientState) VerifyPacketAcknowledgement( height uint64, - prefix commitment.PrefixI, - proof commitment.ProofI, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, + prefix commitment.PrefixI, proof commitment.ProofI, + portID, channelID string, + sequence uint64, acknowledgement []byte, + consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); !ok { + return clienterrors.ErrFailedPacketAckVerification + } + return nil } func (cs ClientState) VerifyPacketAcknowledgementAbsence( height uint64, - prefix commitment.PrefixI, - proof commitment.ProofI, - portID, - channelID string, - sequence uint64, + prefix commitment.PrefixI, proof commitment.ProofI, + portID, channelID string, + sequence uint64, consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + if ok := proof.VerifyNonMembership(consensusState.GetRoot(), path); !ok { + return clienterrors.ErrFailedPacketAckAbsenceVerification + } + return nil } @@ -109,6 +232,26 @@ func (cs ClientState) VerifyNextSequenceRecv( portID, channelID string, nextSequenceRecv uint64, + consensusState clientexported.ConsensusState, ) error { + path, err := commitment.ApplyPrefix(prefix, "") + if err != nil { + return nil + } + + if cs.LatestHeight < height { + return ibctypes.ErrInvalidHeight + } + + if cs.IsFrozen() && cs.FrozenHeight <= height { + return clienterrors.ErrClientFrozen + } + + bz := sdk.Uint64ToBigEndian(nextSequenceRecv) + + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + return clienterrors.ErrFailedNextSeqRecvVerification + } + return nil } diff --git a/x/ibc/07-tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go index cb7e2e42e849..d9f1d818a4b2 100644 --- a/x/ibc/07-tendermint/consensus_state.go +++ b/x/ibc/07-tendermint/consensus_state.go @@ -1,27 +1,16 @@ package tendermint import ( - "bytes" - "fmt" - - lerr "github.com/tendermint/tendermint/lite/errors" - tmtypes "github.com/tendermint/tendermint/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) -var _ exported.ConsensusState = ConsensusState{} - // ConsensusState defines a Tendermint consensus state type ConsensusState struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec - Root commitment.RootI `json:"root" yaml:"root"` - ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey + Root commitment.RootI `json:"root" yaml:"root"` + ValidatorSetHash []byte `json:"validator_set_hash" yaml:"validator_set_hash"` } // ClientType returns Tendermint @@ -29,83 +18,18 @@ func (ConsensusState) ClientType() exported.ClientType { return exported.Tendermint } -// GetHeight returns the ConsensusState height -func (cs ConsensusState) GetHeight() uint64 { - return cs.Height -} - // GetRoot returns the commitment Root for the specific func (cs ConsensusState) GetRoot() commitment.RootI { return cs.Root } -// GetCommitter returns the commmitter that committed the ConsensusState -func (cs ConsensusState) GetCommitter() exported.Committer { - return Committer{ - ValidatorSet: cs.ValidatorSet, - Height: cs.Height, - NextValSetHash: cs.NextValidatorSet.Hash(), - } -} - -// CheckValidityAndUpdateState checks if the provided header is valid and updates -// the consensus state if appropriate -func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { - tmHeader, ok := header.(Header) - if !ok { - return nil, sdkerrors.Wrap( - clienterrors.ErrInvalidHeader, "header is not from Tendermint", - ) - } - - if err := cs.checkValidity(tmHeader); err != nil { - return nil, err - } - - return cs.update(tmHeader), nil -} - -// checkValidity checks if the Tendermint header is valid -// -// CONTRACT: assumes header.Height > consensusState.Height -func (cs ConsensusState) checkValidity(header Header) error { - if header.GetHeight() < cs.Height { - return sdkerrors.Wrap( - clienterrors.ErrInvalidHeader, - fmt.Sprintf("header height < consensus height (%d < %d)", header.GetHeight(), cs.Height), - ) +// ValidateBasic +func (cs ConsensusState) ValidateBasic() error { + if cs.Root == nil { + return sdkerrors.Wrap(clienterrors.ErrInvalidConsensus, "root cannot be nil") } - - // basic consistency check - if err := header.ValidateBasic(cs.ChainID); err != nil { - return err - } - - // check if the hash from the consensus set and header - // matches - nextHash := cs.NextValidatorSet.Hash() - if cs.Height == uint64(header.Height-1) && - !bytes.Equal(nextHash, header.ValidatorsHash) { - return lerr.ErrUnexpectedValidators(nextHash, header.ValidatorsHash) + if len(cs.ValidatorSetHash) == 0 { + return sdkerrors.Wrap(clienterrors.ErrInvalidConsensus, "validator set hash cannot be empty") } - - // validate the next validator set hash from the header - nextHash = header.NextValidatorSet.Hash() - if !bytes.Equal(header.NextValidatorsHash, nextHash) { - return lerr.ErrUnexpectedValidators(header.NextValidatorsHash, nextHash) - } - - // abortTransactionUnless(consensusState.publicKey.verify(header.signature)) - return header.ValidatorSet.VerifyFutureCommit( - cs.NextValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit, - ) -} - -// update the consensus state from a new header -func (cs ConsensusState) update(header Header) ConsensusState { - cs.Height = header.GetHeight() - cs.Root = commitment.NewRoot(header.AppHash) - cs.ValidatorSet = header.ValidatorSet - cs.NextValidatorSet = header.NextValidatorSet - return cs + return nil } diff --git a/x/ibc/07-tendermint/consensus_state_test.go b/x/ibc/07-tendermint/consensus_state_test.go index 778de45fa3a8..de8d07304e66 100644 --- a/x/ibc/07-tendermint/consensus_state_test.go +++ b/x/ibc/07-tendermint/consensus_state_test.go @@ -1,53 +1 @@ -package tendermint - -import ( - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/tmhash" - tmtypes "github.com/tendermint/tendermint/types" -) - -func (suite *TendermintTestSuite) TestCheckValidity() { - // valid header - err := suite.cs.checkValidity(suite.header) - suite.NoError(err, "validity failed") - - // switch out header ValidatorsHash - suite.header.ValidatorsHash = tmhash.Sum([]byte("hello")) - err = suite.cs.checkValidity(suite.header) - suite.Error(err, "validator hash is wrong") - - // reset suite and make header.NextValidatorSet different - // from NextValidatorSetHash - suite.SetupTest() - privVal := tmtypes.NewMockPV() - val := tmtypes.NewValidator(privVal.GetPubKey(), 5) - suite.header.NextValidatorSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - err = suite.cs.checkValidity(suite.header) - suite.Error(err, "header's next validator set is not consistent with hash") - - // reset and make header fail validatebasic - suite.SetupTest() - suite.header.ChainID = "not_gaia" - err = suite.cs.checkValidity(suite.header) - suite.Error(err, "invalid header should fail ValidateBasic") -} - -func (suite *TendermintTestSuite) TestCheckUpdate() { - // valid header should successfully update consensus state - cs, err := suite.cs.CheckValidityAndUpdateState(suite.header) - - require.Nil(suite.T(), err, "valid update failed") - require.Equal(suite.T(), suite.header.GetHeight(), cs.GetHeight(), "height not updated") - require.Equal(suite.T(), suite.header.AppHash.Bytes(), cs.GetRoot().GetHash(), "root not updated") - tmCS, _ := cs.(ConsensusState) - require.Equal(suite.T(), suite.header.ValidatorSet, tmCS.ValidatorSet, "validator set did not update") - require.Equal(suite.T(), suite.header.NextValidatorSet, tmCS.NextValidatorSet, "next validator set did not update") - - // make header invalid so update should be unsuccessful - suite.SetupTest() - suite.header.ChainID = "not_gaia" - - cs, err = suite.cs.CheckValidityAndUpdateState(suite.header) - suite.Error(err) - require.Nil(suite.T(), cs) -} +package tendermint \ No newline at end of file diff --git a/x/ibc/07-tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go index 7f8b8e0e863c..64ff8fb1c69a 100644 --- a/x/ibc/07-tendermint/tendermint_test.go +++ b/x/ibc/07-tendermint/tendermint_test.go @@ -5,10 +5,7 @@ import ( "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" - - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) type TendermintTestSuite struct { @@ -17,28 +14,13 @@ type TendermintTestSuite struct { privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet header Header - cs ConsensusState } func (suite *TendermintTestSuite) SetupTest() { - privVal := tmtypes.NewMockPV() - val := tmtypes.NewValidator(privVal.GetPubKey(), 10) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - suite.header = MakeHeader("gaia", 4, valSet, valSet, []tmtypes.PrivValidator{privVal}) - root := commitment.NewRoot(tmhash.Sum([]byte("my root"))) - - cs := ConsensusState{ - ChainID: "gaia", - Height: 3, - Root: root, - ValidatorSet: valSet, - NextValidatorSet: valSet, - } - - // set fields in suite - suite.privVal = privVal - suite.valSet = valSet - suite.cs = cs + suite.privVal = tmtypes.NewMockPV() + val := tmtypes.NewValidator(suite.privVal.GetPubKey(), 10) + suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + suite.header = MakeHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } func TestTendermintTestSuite(t *testing.T) { diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go index dca755feeccc..1874a86f6b1b 100644 --- a/x/ibc/07-tendermint/update.go +++ b/x/ibc/07-tendermint/update.go @@ -10,7 +10,7 @@ import ( // CheckValidityAndUpdateState checks if the provided header is valid and updates // the consensus state if appropriate func CheckValidityAndUpdateState( - clientState clientexported.ClientState, header clientexported.Header, + clientState clientexported.ClientState, header clientexported.Header, chainID string, ) (clientexported.ClientState, clientexported.ConsensusState, error) { tmClientState, ok := clientState.(ClientState) if !ok { @@ -26,7 +26,7 @@ func CheckValidityAndUpdateState( ) } - if err := checkValidity(tmClientState, tmHeader); err != nil { + if err := checkValidity(tmClientState, tmHeader, chainID); err != nil { return nil, nil, err } @@ -37,7 +37,7 @@ func CheckValidityAndUpdateState( // checkValidity checks if the Tendermint header is valid // // CONTRACT: assumes header.Height > consensusState.Height -func checkValidity(clientState ClientState, header Header) error { +func checkValidity(clientState ClientState, header Header, chainID string) error { if header.GetHeight() < clientState.LatestHeight { return sdkerrors.Wrapf( clienterrors.ErrInvalidHeader, @@ -46,7 +46,7 @@ func checkValidity(clientState ClientState, header Header) error { } // basic consistency check - if err := header.ValidateBasic(); err != nil { + if err := header.ValidateBasic(chainID); err != nil { return err } diff --git a/x/ibc/07-tendermint/update_test.go b/x/ibc/07-tendermint/update_test.go new file mode 100644 index 000000000000..23fbbfea15be --- /dev/null +++ b/x/ibc/07-tendermint/update_test.go @@ -0,0 +1,47 @@ +package tendermint + +func (suite *TendermintTestSuite) TestCheckValidity() { + // // valid header + // err := checkValidity(suite.header) + // suite.NoError(err, "validity failed") + + // // switch out header ValidatorsHash + // suite.header.ValidatorsHash = tmhash.Sum([]byte("hello")) + // err = checkValidity(suite.header) + // suite.Error(err, "validator hash is wrong") + + // // reset suite and make header.NextValidatorSet different + // // from NextValidatorSetHash + // suite.SetupTest() + // privVal := tmtypes.NewMockPV() + // val := tmtypes.NewValidator(privVal.GetPubKey(), 5) + // suite.header.NextValidatorSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + // err = checkValidity(suite.header) + // suite.Error(err, "header's next validator set is not consistent with hash") + + // // reset and make header fail validatebasic + // suite.SetupTest() + // suite.header.ChainID = "not_gaia" + // err = checkValidity(suite.header) + // suite.Error(err, "invalid header should fail ValidateBasic") + // } + + // func (suite *TendermintTestSuite) TestCheckUpdate() { + // // valid header should successfully update consensus state + // cs, err := CheckValidityAndUpdateState(suite.header) + + // require.Nil(suite.T(), err, "valid update failed") + // require.Equal(suite.T(), suite.header.GetHeight(), cs.GetHeight(), "height not updated") + // require.Equal(suite.T(), suite.header.AppHash.Bytes(), cs.GetRoot().GetHash(), "root not updated") + // tmCS, _ := cs.(ConsensusState) + // require.Equal(suite.T(), suite.header.ValidatorSet, tmCS.ValidatorSet, "validator set did not update") + // require.Equal(suite.T(), suite.header.NextValidatorSet, tmCS.NextValidatorSet, "next validator set did not update") + + // // make header invalid so update should be unsuccessful + // suite.SetupTest() + // suite.header.ChainID = "not_gaia" + + // cs, err = CheckValidityAndUpdateState(suite.header) + // suite.Error(err) + // require.Nil(suite.T(), cs) +} From 2126cc84fdc9bdb9c91c82089c25f7215303e126 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 8 Jan 2020 18:08:26 -0300 Subject: [PATCH 06/35] remove committer; cleanup --- x/ibc/02-client/client/cli/query.go | 40 -------------- x/ibc/02-client/client/rest/query.go | 40 -------------- x/ibc/02-client/client/utils/utils.go | 35 +----------- x/ibc/02-client/exported/exported.go | 20 +++---- x/ibc/02-client/keeper/client.go | 10 ++-- x/ibc/02-client/keeper/keeper.go | 26 --------- x/ibc/02-client/keeper/keeper_test.go | 46 ---------------- x/ibc/02-client/keeper/querier.go | 22 -------- x/ibc/02-client/types/codec.go | 1 - x/ibc/02-client/types/errors/errors.go | 2 - x/ibc/02-client/types/keys.go | 19 ------- x/ibc/02-client/types/msgs.go | 3 ++ x/ibc/02-client/types/querier.go | 36 ------------- x/ibc/03-connection/keeper/handshake_test.go | 11 ++-- x/ibc/04-channel/keeper/handshake_test.go | 2 - x/ibc/07-tendermint/codec.go | 1 - x/ibc/07-tendermint/committer.go | 26 --------- x/ibc/07-tendermint/evidence.go | 11 ++-- x/ibc/07-tendermint/header.go | 17 +----- x/ibc/07-tendermint/misbehaviour.go | 57 ++++++++++++++------ x/ibc/07-tendermint/misbehaviour_test.go | 29 ++++++---- x/ibc/07-tendermint/test_utils.go | 5 +- x/ibc/keeper/querier.go | 6 --- 23 files changed, 94 insertions(+), 371 deletions(-) delete mode 100644 x/ibc/07-tendermint/committer.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 1c59284baad2..34c5e58093de 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -119,46 +119,6 @@ func GetCmdQueryConsensusState(queryRoute string, cdc *codec.Codec) *cobra.Comma return cmd } -// GetCmdQueryCommitter defines the command to query the committer of the chain -// at a given height -func GetCmdQueryCommitter(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "committer [client-id] [height]", - Short: "Query a committer", - Long: strings.TrimSpace( - fmt.Sprintf(`Query a committer at a specific height for a particular client - -Example: -$ %s query ibc client committer [client-id] [height] -`, version.ClientName), - ), - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - clientID := args[0] - if strings.TrimSpace(clientID) == "" { - return errors.New("client ID can't be blank") - } - - height, err := strconv.ParseUint(args[1], 10, 64) - if err != nil { - return fmt.Errorf("expected integer height, got: %v", args[1]) - } - - prove := viper.GetBool(flags.FlagProve) - - committerRes, err := utils.QueryCommitter(cliCtx, clientID, height, prove) - if err != nil { - return err - } - - return cliCtx.PrintOutput(committerRes) - }, - } - cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") - return cmd -} - // GetCmdQueryHeader defines the command to query the latest header on the chain func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ diff --git a/x/ibc/02-client/client/rest/query.go b/x/ibc/02-client/client/rest/query.go index c4d901a1206b..97845c945532 100644 --- a/x/ibc/02-client/client/rest/query.go +++ b/x/ibc/02-client/client/rest/query.go @@ -17,7 +17,6 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/ibc/clients", queryAllClientStatesFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/client-state", RestClientID), queryClientStateHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/committers/{%s}", RestClientID, RestRootHeight), queryCommitterHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/header", queryHeaderHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/node-state", queryNodeConsensusStateHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/path", queryPathHandlerFn(cliCtx)).Methods("GET") @@ -130,45 +129,6 @@ func queryConsensusStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } -// queryCommitterHandlerFn implements a committer querying route -// -// @Summary Query committer -// @Tags IBC -// @Produce json -// @Param client-id path string true "Client ID" -// @Param height path number true "Committer height" -// @Success 200 {object} QueryCommitter "OK" -// @Failure 400 {object} rest.ErrorResponse "Invalid client id or height" -// @Failure 500 {object} rest.ErrorResponse "Internal Server Error" -// @Router /ibc/clients/{client-id}/committers/{height} [get] -func queryCommitterHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - clientID := vars[RestClientID] - height, err := strconv.ParseUint(vars[RestRootHeight], 10, 64) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - prove := rest.ParseQueryProve(r) - - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) - if !ok { - return - } - - committerRes, err := utils.QueryCommitter(cliCtx, clientID, height, prove) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - cliCtx = cliCtx.WithHeight(int64(committerRes.ProofHeight)) - rest.PostProcessResponse(w, cliCtx, committerRes) - } -} - // queryHeaderHandlerFn implements a header querying route // // @Summary Query header diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go index 6ab42e746377..bd7a181ec14d 100644 --- a/x/ibc/02-client/client/utils/utils.go +++ b/x/ibc/02-client/client/utils/utils.go @@ -88,31 +88,6 @@ func QueryConsensusState( return types.NewConsensusStateResponse(clientID, cs, res.Proof, res.Height), nil } -// QueryCommitter queries the store to get the committer and a merkle proof -func QueryCommitter( - cliCtx context.CLIContext, clientID string, height uint64, prove bool, -) (types.CommitterResponse, error) { - req := abci.RequestQuery{ - Path: "store/ibc/key", - Data: types.KeyCommitter(clientID, height), - Prove: prove, - } - - res, err := cliCtx.QueryABCI(req) - if err != nil { - return types.CommitterResponse{}, err - } - - var committer exported.Committer - if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &committer); err != nil { - return types.CommitterResponse{}, err - } - - committerRes := types.NewCommitterResponse(clientID, height, committer, res.Proof, res.Height) - - return committerRes, nil -} - // QueryTendermintHeader takes a client context and returns the appropriate // tendermint header func QueryTendermintHeader(cliCtx context.CLIContext) (tendermint.Header, int64, error) { @@ -139,15 +114,9 @@ func QueryTendermintHeader(cliCtx context.CLIContext) (tendermint.Header, int64, return tendermint.Header{}, 0, err } - nextvalidators, err := node.Validators(&height) - if err != nil { - return tendermint.Header{}, 0, err - } - header := tendermint.Header{ - SignedHeader: commit.SignedHeader, - ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), + SignedHeader: commit.SignedHeader, + ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), } return header, height, nil diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index fde0856d0ebe..0111ca194025 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/cosmos/cosmos-sdk/codec" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -18,6 +19,7 @@ type ClientState interface { // State verification functions VerifyClientConsensusState( + cdc *codec.Codec, height uint64, prefix commitment.PrefixI, proof commitment.ProofI, @@ -25,19 +27,23 @@ type ClientState interface { consensusState ConsensusState, ) error VerifyConnectionState( + cdc *codec.Codec, height uint64, prefix commitment.PrefixI, proof commitment.ProofI, connectionID string, // connectionEnd connection, + consensusState ConsensusState, ) error VerifyChannelState( + cdc *codec.Codec, height uint64, prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, // channelEnd channel, + consensusState ConsensusState, ) error VerifyPacketCommitment( height uint64, @@ -47,6 +53,7 @@ type ClientState interface { channelID string, sequence uint64, commitmentBytes []byte, + consensusState ConsensusState, ) error VerifyPacketAcknowledgement( height uint64, @@ -56,6 +63,7 @@ type ClientState interface { channelID string, sequence uint64, acknowledgement []byte, + consensusState ConsensusState, ) error VerifyPacketAcknowledgementAbsence( height uint64, @@ -64,6 +72,7 @@ type ClientState interface { portID, channelID string, sequence uint64, + consensusState ConsensusState, ) error VerifyNextSequenceRecv( height uint64, @@ -72,6 +81,7 @@ type ClientState interface { portID, channelID string, nextSequenceRecv uint64, + consensusState ConsensusState, ) error } @@ -95,16 +105,6 @@ type Misbehaviour interface { // Header is the consensus state update information type Header interface { - ClientType() ClientType - GetCommitter() Committer - GetHeight() uint64 -} - -// Committer defines the type that is responsible for -// updating the consensusState at a given height -// -// In Tendermint, this is the ValidatorSet at the given height -type Committer interface { ClientType() ClientType GetHeight() uint64 } diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 88af78aa7ff2..b115df35ede4 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -32,8 +32,6 @@ func (k Keeper) CreateClient( return nil, sdkerrors.Wrapf(err, "cannot create client with ID %s", clientID) } - // k.SetCommitter(ctx, clientID, consensusState.GetHeight(), consensusState.GetCommitter()) - // k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) k.SetClientType(ctx, clientID, clientType) k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, clientState.GetSequence())) @@ -80,8 +78,6 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H k.SetClientState(ctx, clientState) k.SetConsensusState(ctx, clientID, header.GetHeight(), consensusState) - // k.SetCommitter(ctx, clientID, header.GetHeight(), consensusState.GetCommitter()) - // k.SetVerifiedRoot(ctx, clientID, header.GetHeight(), consensusState.GetRoot()) k.Logger(ctx).Info(fmt.Sprintf("client %s updated to height %d", clientID, header.GetHeight())) return nil } @@ -94,15 +90,15 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex return sdkerrors.Wrap(errors.ErrClientNotFound, misbehaviour.GetClientID()) } - committer, found := k.GetCommitter(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight())) + consensusState, found := k.GetConsensusState(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight())) if !found { - return errors.ErrCommitterNotFound + return sdkerrors.Wrap(errors.ErrConsensusStateNotFound, misbehaviour.GetClientID()) } var err error switch e := misbehaviour.(type) { case tendermint.Evidence: - clientState, err = tendermint.CheckMisbehaviourAndUpdateState(clientState, committer, misbehaviour) + clientState, err = tendermint.CheckMisbehaviourAndUpdateState(clientState, consensusState, misbehaviour, uint64(misbehaviour.GetHeight())) default: err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC client evidence type: %T", e) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 55889f750893..b1c7461143b3 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -118,32 +118,6 @@ func (k Keeper) GetAllClients(ctx sdk.Context) (states []exported.ClientState) { return states } -// GetCommitter will get the Committer of a particular client at the oldest height -// that is less than or equal to the height passed in -func (k Keeper) GetCommitter(ctx sdk.Context, clientID string, height uint64) (exported.Committer, bool) { - store := ctx.KVStore(k.storeKey) - - var committer exported.Committer - - // TODO: Replace this for-loop with a ReverseIterator for efficiency - for i := height; i > 0; i-- { - bz := store.Get(types.KeyCommitter(clientID, i)) - if bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &committer) - return committer, true - } - } - return nil, false -} - -// SetCommitter sets a committer from a particular height to -// a particular client -func (k Keeper) SetCommitter(ctx sdk.Context, clientID string, height uint64, committer exported.Committer) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(committer) - store.Set(types.KeyCommitter(clientID, height), bz) -} - // State returns a new client state with a given id as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation func (k Keeper) initialize( diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 1feaa75a4215..33fe93ba1eba 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -4,7 +4,6 @@ import ( "testing" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -105,51 +104,6 @@ func (suite *KeeperTestSuite) TestSetVerifiedRoot() { require.Equal(suite.T(), root, retrievedRoot, "Root stored incorrectly") } -func (suite KeeperTestSuite) TestSetCommitter() { - committer := tendermint.Committer{ - ValidatorSet: suite.valSet, - Height: 3, - NextValSetHash: suite.valSet.Hash(), - } - nextCommitter := tendermint.Committer{ - ValidatorSet: suite.valSet, - Height: 6, - NextValSetHash: tmhash.Sum([]byte("next_hash")), - } - - suite.keeper.SetCommitter(suite.ctx, "gaia", 3, committer) - suite.keeper.SetCommitter(suite.ctx, "gaia", 6, nextCommitter) - - // fetch the commiter on each respective height - for i := 0; i < 3; i++ { - committer, ok := suite.keeper.GetCommitter(suite.ctx, "gaia", uint64(i)) - require.False(suite.T(), ok, "GetCommitter passed on nonexistent height: %d", i) - require.Nil(suite.T(), committer, "GetCommitter returned committer on nonexistent height: %d", i) - } - - for i := 3; i < 6; i++ { - recv, ok := suite.keeper.GetCommitter(suite.ctx, "gaia", uint64(i)) - tmRecv, _ := recv.(tendermint.Committer) - if tmRecv.ValidatorSet != nil { - // update validator set's power - tmRecv.ValidatorSet.TotalVotingPower() - } - require.True(suite.T(), ok, "GetCommitter failed on existing height: %d", i) - require.Equal(suite.T(), committer, recv, "GetCommitter returned committer on nonexistent height: %d", i) - } - - for i := 6; i < 9; i++ { - recv, ok := suite.keeper.GetCommitter(suite.ctx, "gaia", uint64(i)) - tmRecv, _ := recv.(tendermint.Committer) - if tmRecv.ValidatorSet != nil { - // update validator set's power - tmRecv.ValidatorSet.TotalVotingPower() - } - require.True(suite.T(), ok, "GetCommitter failed on existing height: %d", i) - require.Equal(suite.T(), nextCommitter, recv, "GetCommitter returned committer on nonexistent height: %d", i) - } -} - func (suite KeeperTestSuite) TestGetAllClients() { expClients := []exported.ClientState{ tendermint.NewClientState(testClientID2), diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index a0cca752b3a5..7cadac9fe426 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -9,7 +9,6 @@ import ( 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" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // QuerierClients defines the sdk.Querier to query all the light client states. @@ -36,24 +35,3 @@ func QuerierClients(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e return res, nil } - -// QuerierCommitter defines the sdk.Querier to query a committer -func QuerierCommitter(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryCommitterParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - committer, found := k.GetCommitter(ctx, params.ClientID, params.Height) - if !found { - return nil, errors.ErrCommitterNotFound - } - - bz, err := codec.MarshalJSONIndent(k.cdc, committer) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index 91729390d33a..3226fc02c2cf 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -12,7 +12,6 @@ var SubModuleCdc *codec.Codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.ClientState)(nil), nil) cdc.RegisterInterface((*exported.ConsensusState)(nil), nil) - cdc.RegisterInterface((*exported.Committer)(nil), nil) cdc.RegisterInterface((*exported.Header)(nil), nil) cdc.RegisterInterface((*exported.Misbehaviour)(nil), nil) diff --git a/x/ibc/02-client/types/errors/errors.go b/x/ibc/02-client/types/errors/errors.go index d73790a18e0b..af663e4d5b19 100644 --- a/x/ibc/02-client/types/errors/errors.go +++ b/x/ibc/02-client/types/errors/errors.go @@ -19,8 +19,6 @@ var ( ErrRootNotFound = sdkerrors.Register(SubModuleName, 8, "commitment root not found") ErrInvalidHeader = sdkerrors.Register(SubModuleName, 9, "invalid block header") ErrInvalidEvidence = sdkerrors.Register(SubModuleName, 10, "invalid light client misbehaviour evidence") - ErrCommitterNotFound = sdkerrors.Register(SubModuleName, 11, "commiter not found") - ErrInvalidCommitter = sdkerrors.Register(SubModuleName, 12, "invalid commiter") ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 13, "client consensus state verification failed") ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 14, "connection state verification failed") ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 15, "channel state verification failed") diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index 3c3a83305d81..9ec474343483 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -46,12 +46,6 @@ func RootPath(clientID string, height uint64) string { return string(KeyRoot(clientID, height)) } -// CommitterPath takes an Identifier and returns a Path under which -// to store the committer of a client at a particular height -func CommitterPath(clientID string, height uint64) string { - return string(KeyCommitter(clientID, height)) -} - // KeyClientState returns the store key for a particular client state func KeyClientState(clientID string) []byte { return append( @@ -86,15 +80,6 @@ func KeyRoot(clientID string, height uint64) []byte { ) } -// KeyCommitter returns the store key for a validator (aka commiter) of a particular -// client at a given height. -func KeyCommitter(clientID string, height uint64) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyCommiterPrefix), - []byte(committerPath(clientID, height))..., - ) -} - // GetClientKeysPrefix return the ICS02 prefix bytes func GetClientKeysPrefix(prefix int) []byte { return []byte(fmt.Sprintf("%d/clients", prefix)) @@ -115,7 +100,3 @@ func consensusStatePath(clientID string, height uint64) string { func rootPath(clientID string, height uint64) string { return fmt.Sprintf("clients/%s/roots/%d", clientID, height) } - -func committerPath(clientID string, height uint64) string { - return fmt.Sprintf("clients/%s/committer/%d", clientID, height) -} diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index ba9b8972619e..b47a8e24db83 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -54,6 +54,9 @@ func (msg MsgCreateClient) ValidateBasic() error { if msg.ConsensusState == nil { return errors.ErrInvalidConsensus } + if err := msg.ConsensusState.ValidateBasic(); err != nil { + return err + } if msg.Signer.Empty() { return sdkerrors.ErrInvalidAddress } diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 844c9c6db9ea..7c7827ceadc3 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -31,21 +31,6 @@ func NewQueryAllClientsParams(page, limit int) QueryAllClientsParams { } } -// QueryCommitterParams defines the params for the following queries: -// - 'custom/ibc/clients//committers/' -type QueryCommitterParams struct { - ClientID string - Height uint64 -} - -// NewQueryCommitterParams creates a new QueryCommitmentRootParams instance -func NewQueryCommitterParams(id string, height uint64) QueryCommitterParams { - return QueryCommitterParams{ - ClientID: id, - Height: height, - } -} - // StateResponse defines the client response for a client state query. // It includes the commitment proof and the height of the proof. type StateResponse struct { @@ -87,24 +72,3 @@ func NewConsensusStateResponse( ProofHeight: uint64(height), } } - -// CommitterResponse defines the client response for a committer query -// It includes the commitment proof and the height of the proof -type CommitterResponse struct { - Committer exported.Committer `json:"committer" yaml:"committer"` - Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` - ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` - ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` -} - -// NewCommitterResponse creates a new CommitterResponse instance. -func NewCommitterResponse( - clientID string, height uint64, committer exported.Committer, proof *merkle.Proof, proofHeight int64, -) CommitterResponse { - return CommitterResponse{ - Committer: committer, - Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(CommitterPath(clientID, height), "/")), - ProofHeight: uint64(proofHeight), - } -} diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index a31c1e115aea..86a21be54e16 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -6,8 +6,8 @@ import ( abci "github.com/tendermint/tendermint/abci/types" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -267,11 +267,10 @@ func (suite *KeeperTestSuite) createClient(clientID string) { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) consensusState := tendermint.ConsensusState{ - ChainID: chainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ChainID: chainID, + Height: uint64(commitID.Version), + Root: commitment.NewRoot(commitID.Hash), + ValidatorSet: suite.valSet, } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 2e47a3826a72..8272f8677f11 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -28,8 +28,6 @@ func (suite *KeeperTestSuite) createClient() { NextValidatorSet: suite.valSet, } - _ = consensusState.GetCommitter() - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) suite.NoError(err) } diff --git a/x/ibc/07-tendermint/codec.go b/x/ibc/07-tendermint/codec.go index e3b4ddcc8844..03a5b8bc31e4 100644 --- a/x/ibc/07-tendermint/codec.go +++ b/x/ibc/07-tendermint/codec.go @@ -9,7 +9,6 @@ var SubModuleCdc = codec.New() // RegisterCodec registers the Tendermint types func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ClientState{}, "ibc/client/tendermint/ClientState", nil) - cdc.RegisterConcrete(Committer{}, "ibc/client/tendermint/Committer", nil) cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) cdc.RegisterConcrete(Evidence{}, "ibc/client/tendermint/Evidence", nil) diff --git a/x/ibc/07-tendermint/committer.go b/x/ibc/07-tendermint/committer.go deleted file mode 100644 index 317c422259a0..000000000000 --- a/x/ibc/07-tendermint/committer.go +++ /dev/null @@ -1,26 +0,0 @@ -package tendermint - -import ( - tmtypes "github.com/tendermint/tendermint/types" - - clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" -) - -var _ clientexported.Committer = Committer{} - -// Committer definites a Tendermint Committer -type Committer struct { - *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` - Height uint64 `json:"height" yaml:"height"` - NextValSetHash []byte `json:"next_valset_hash" yaml:"next_valset_hash"` -} - -// ClientType implements exported.Committer interface -func (c Committer) ClientType() clientexported.ClientType { - return clientexported.Tendermint -} - -// GetHeight implements exported.Committer interface -func (c Committer) GetHeight() uint64 { - return c.Height -} diff --git a/x/ibc/07-tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go index ee89c711be7f..26cf7693b9a8 100644 --- a/x/ibc/07-tendermint/evidence.go +++ b/x/ibc/07-tendermint/evidence.go @@ -22,10 +22,11 @@ var ( // Evidence is a wrapper over tendermint's DuplicateVoteEvidence // that implements Evidence interface expected by ICS-02 type Evidence struct { - ClientID string `json:"client_id" yaml:"client_id"` - Header1 Header `json:"header1" yaml:"header1"` - Header2 Header `json:"header2" yaml:"header2"` - ChainID string `json:"chain_id" yaml:"chain_id"` + ClientID string `json:"client_id" yaml:"client_id"` + FromValidatorSet *tmtypes.ValidatorSet `json:"from_validator_set" yaml:"from_validator_set"` + Header1 Header `json:"header1" yaml:"header1"` + Header2 Header `json:"header2" yaml:"header2"` + ChainID string `json:"chain_id" yaml:"chain_id"` } // ClientType is Tendermint light client @@ -63,6 +64,8 @@ func (ev Evidence) Hash() cmn.HexBytes { } // GetHeight returns the height at which misbehaviour occurred +// +// NOTE: assumes that evidence headers have the same height func (ev Evidence) GetHeight() int64 { return ev.Header1.Height } diff --git a/x/ibc/07-tendermint/header.go b/x/ibc/07-tendermint/header.go index 9509d6370019..167fefc93a75 100644 --- a/x/ibc/07-tendermint/header.go +++ b/x/ibc/07-tendermint/header.go @@ -12,9 +12,8 @@ var _ exported.Header = Header{} // Header defines the Tendermint consensus Header type Header struct { - tmtypes.SignedHeader - ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` + tmtypes.SignedHeader // contains the commitment root + ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` } // ClientType defines that the Header is a Tendermint consensus algorithm @@ -22,15 +21,6 @@ func (h Header) ClientType() exported.ClientType { return exported.Tendermint } -// GetCommitter returns the ValidatorSet that committed header -func (h Header) GetCommitter() exported.Committer { - return Committer{ - ValidatorSet: h.ValidatorSet, - Height: uint64(h.Height), - NextValSetHash: h.NextValidatorsHash, - } -} - // GetHeight returns the current height // // NOTE: also referred as `sequence` @@ -47,8 +37,5 @@ func (h Header) ValidateBasic(chainID string) error { if h.ValidatorSet == nil { return sdkerrors.Wrap(clienterrors.ErrInvalidHeader, "validator set is nil") } - if h.NextValidatorSet == nil { - return sdkerrors.Wrap(clienterrors.ErrInvalidHeader, "next validator set is nil") - } return nil } diff --git a/x/ibc/07-tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go index 7ce947ef556d..31bdfaeffdb6 100644 --- a/x/ibc/07-tendermint/misbehaviour.go +++ b/x/ibc/07-tendermint/misbehaviour.go @@ -1,33 +1,43 @@ package tendermint import ( + "bytes" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) -// CheckMisbehaviourAndUpdateState +// CheckMisbehaviourAndUpdateState determines whether or not two conflicting +// headers at the same height would have convinced the light client. +// +// NOTE: assumes provided height is the height at which the consensusState is +// stored. func CheckMisbehaviourAndUpdateState( - clientState clientexported.ClientState, committer clientexported.Committer, + clientState clientexported.ClientState, + consensusState clientexported.ConsensusState, misbehaviour clientexported.Misbehaviour, + height uint64, // height at which the consensus state was loaded ) (clientexported.ClientState, error) { + // cast the interface to specific types before checking for misbehaviour tmClientState, ok := clientState.(ClientState) if !ok { return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "client state type is not Tendermint") } - tmCommitter, ok := committer.(Committer) + tmConsensusState, ok := consensusState.(ConsensusState) if !ok { - return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "committer type is not Tendermint") + return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "consensus state is not Tendermint") } tmEvidence, ok := misbehaviour.(Evidence) if !ok { - return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "committer type is not Tendermint") + return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "evidence type is not Tendermint") } - if err := CheckMisbehaviour(tmCommitter, tmEvidence); err != nil { + if err := checkMisbehaviour(tmClientState, tmConsensusState, tmEvidence, height); err != nil { return nil, sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) } @@ -36,28 +46,43 @@ func CheckMisbehaviourAndUpdateState( return clientState, nil } -// CheckMisbehaviour checks if the evidence provided is a valid light client misbehaviour -func CheckMisbehaviour(trustedCommitter Committer, evidence Evidence) error { - if err := evidence.ValidateBasic(); err != nil { - return err +// checkMisbehaviour checks if the evidence provided is a valid light client misbehaviour +func checkMisbehaviour( + clientState ClientState, consensusState ConsensusState, evidence Evidence, height uint64, +) error { + // NOTE: header height and commitment root assertions are checked with the + // evidence and msg ValidateBasic functions at the AnteHandler level. + + // check if provided height matches the headers' height + if height != uint64(evidence.GetHeight()) { + return sdkerrors.Wrapf( + ibctypes.ErrInvalidHeight, + "height ≠ evidence header height (%d ≠ %d)", height, evidence.GetHeight(), + ) } - trustedValSet := trustedCommitter.ValidatorSet + if !bytes.Equal(consensusState.ValidatorSetHash, evidence.FromValidatorSet.Hash()) { + return sdkerrors.Wrap( + errors.ErrInvalidEvidence, + "the consensus state's validator set hash doesn't match the evidence's one", + ) + } - // Evidence is within trusting period. ValidatorSet must have 2/3 similarity with trustedCommitter ValidatorSet + // Evidence is within the trusting period. ValidatorSet must have 2/3 similarity with trusted FromValidatorSet // check that the validator sets on both headers are valid given the last trusted validatorset // less than or equal to evidence height - if err := trustedValSet.VerifyFutureCommit( + if err := evidence.FromValidatorSet.VerifyFutureCommit( evidence.Header1.ValidatorSet, evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit, ); err != nil { - return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 1 has too much change from last known committer: %v", err) + return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err) } - if err := trustedValSet.VerifyFutureCommit( + + if err := evidence.FromValidatorSet.VerifyFutureCommit( evidence.Header2.ValidatorSet, evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit, ); err != nil { - return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 2 has too much change from last known committer: %v", err) + return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err) } return nil diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index b0295b0a67c5..fa5980065da1 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -25,55 +25,64 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { altSigners := []tmtypes.PrivValidator{altPrivVal} - committer := Committer{ - ValidatorSet: suite.valSet, - Height: 3, - NextValSetHash: suite.valSet.Hash(), - } - testCases := []struct { - name string - evidence Evidence - expErr bool + name string + clientState ClientState + consensusState ConsensusState + evidence Evidence + height uint64 + expErr bool }{ { "trusting period misbehavior should pass", + ClientState{}, + ConsensusState{}, Evidence{ Header1: MakeHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, + 5, false, }, { "first valset has too much change", + ClientState{}, + ConsensusState{}, Evidence{ Header1: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, + 5, true, }, { "second valset has too much change", + ClientState{}, + ConsensusState{}, Evidence{ Header1: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, + 5, true, }, { "both valsets have too much change", + ClientState{}, + ConsensusState{}, Evidence{ Header1: MakeHeader("gaia", 5, altValSet, altValSet, altSigners), Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, + 5, true, }, } @@ -81,7 +90,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { for _, tc := range testCases { tc := tc // pin for scopelint suite.Run(tc.name, func() { - err := CheckMisbehaviour(committer, tc.evidence) + err := checkMisbehaviour(tc.clientState, tc.consensusState, tc.evidence, tc.height) if tc.expErr { suite.Error(err, "CheckMisbehaviour passed unexpectedly") } else { diff --git a/x/ibc/07-tendermint/test_utils.go b/x/ibc/07-tendermint/test_utils.go index bc2b998c2e71..f36ab02f2328 100644 --- a/x/ibc/07-tendermint/test_utils.go +++ b/x/ibc/07-tendermint/test_utils.go @@ -57,8 +57,7 @@ func MakeHeader(chainID string, height int64, valSet *tmtypes.ValidatorSet, next } return Header{ - SignedHeader: signedHeader, - ValidatorSet: valSet, - NextValidatorSet: nextValSet, + SignedHeader: signedHeader, + ValidatorSet: valSet, } } diff --git a/x/ibc/keeper/querier.go b/x/ibc/keeper/querier.go index 68447885bbd1..4210f455dd68 100644 --- a/x/ibc/keeper/querier.go +++ b/x/ibc/keeper/querier.go @@ -21,14 +21,8 @@ func NewQuerier(k Keeper) sdk.Querier { switch path[0] { case client.SubModuleName: switch path[1] { - case client.QueryClientState: - res, err = client.QuerierClientState(ctx, req, k.ClientKeeper) case client.QueryAllClients: res, err = client.QuerierClients(ctx, req, k.ClientKeeper) - case client.QueryConsensusState: - res, err = client.QuerierConsensusState(ctx, req, k.ClientKeeper) - case client.QueryVerifiedRoot: - res, err = client.QuerierVerifiedRoot(ctx, req, k.ClientKeeper) default: err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC %s query endpoint", client.SubModuleName) } From 1b177357090a9b08642467bab0bb535bafdb86bd Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jan 2020 14:38:16 +0100 Subject: [PATCH 07/35] move keys to ibc/types --- x/ibc/02-client/types/keys.go | 87 -------------- x/ibc/03-connection/keeper/verify.go | 161 ++++++++++++++++++++++++++ x/ibc/03-connection/types/keys.go | 47 -------- x/ibc/04-channel/types/keys.go | 119 -------------------- x/ibc/07-tendermint/client_state.go | 8 +- x/ibc/types/keys.go | 162 +++++++++++++++++++++++++-- 6 files changed, 321 insertions(+), 263 deletions(-) create mode 100644 x/ibc/03-connection/keeper/verify.go diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index 9ec474343483..850e26a6f340 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -1,11 +1,5 @@ package types -import ( - "fmt" - - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" -) - const ( // SubModuleName defines the IBC client name SubModuleName string = "client" @@ -19,84 +13,3 @@ const ( // QuerierRoute is the querier route for IBC client QuerierRoute string = SubModuleName ) - -// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space - -// ClientStatePath takes an Identifier and returns a Path under which to store a -// particular client state -func ClientStatePath(clientID string) string { - return string(KeyClientState(clientID)) -} - -// ClientTypePath takes an Identifier and returns Path under which to store the -// type of a particular client. -func ClientTypePath(clientID string) string { - return string(KeyClientType(clientID)) -} - -// ConsensusStatePath takes an Identifier and returns a Path under which to -// store the consensus state of a client. -func ConsensusStatePath(clientID string, height uint64) string { - return string(KeyConsensusState(clientID, height)) -} - -// RootPath takes an Identifier and returns a Path under which to -// store the root for a particular height of a client. -func RootPath(clientID string, height uint64) string { - return string(KeyRoot(clientID, height)) -} - -// KeyClientState returns the store key for a particular client state -func KeyClientState(clientID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyClientPrefix), - []byte(clientStatePath(clientID))..., - ) -} - -// KeyClientType returns the store key for type of a particular client -func KeyClientType(clientID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyClientTypePrefix), - []byte(clientTypePath(clientID))..., - ) -} - -// KeyConsensusState returns the store key for the consensus state of a particular -// client -func KeyConsensusState(clientID string, height uint64) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyConsensusStatePrefix), - []byte(consensusStatePath(clientID, height))..., - ) -} - -// KeyRoot returns the store key for a commitment root of a particular -// client at a given height -func KeyRoot(clientID string, height uint64) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyRootPrefix), - []byte(rootPath(clientID, height))..., - ) -} - -// GetClientKeysPrefix return the ICS02 prefix bytes -func GetClientKeysPrefix(prefix int) []byte { - return []byte(fmt.Sprintf("%d/clients", prefix)) -} - -func clientStatePath(clientID string) string { - return fmt.Sprintf("clients/%s/state", clientID) -} - -func clientTypePath(clientID string) string { - return fmt.Sprintf("clients/%s/type", clientID) -} - -func consensusStatePath(clientID string, height uint64) string { - return fmt.Sprintf("consensusState/%s/%d", clientID, height) -} - -func rootPath(clientID string, height uint64) string { - return fmt.Sprintf("clients/%s/roots/%d", clientID, height) -} diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go new file mode 100644 index 000000000000..7565fd8abf96 --- /dev/null +++ b/x/ibc/03-connection/keeper/verify.go @@ -0,0 +1,161 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// VerifyClientConsensusState verifies a proof of the consensus state of the +// specified client stored on the target machine. +func (k Keeper) VerifyClientConsensusState( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + proof commitment.ProofI, + consensusState clientexported.ConsensusState, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return clientState.VerifyClientConsensusState( + k.cdc, height, connection.Counterparty.Prefix, proof, consensusState, + ) +} + +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored on the target machine. +func (k Keeper) VerifyConnectionState( + ctx sdk.Context, + height uint64, + proof commitment.ProofI, + connectionID string, + connection types.ConnectionEnd, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyConnectionState( + ctx, clientState, height, connection.Counterparty.Prefix, proof, connectionID, connection, + ) +} + +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the target machine. +func (k Keeper) VerifyChannelState( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + channel channeltypes.Channel, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyChannelState( + ctx, clientState, height, prefix, proof, portID, channelID, channel, + ) +} + +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. +func (k Keeper) VerifyPacketCommitment( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + commitmentBz []byte, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyPacketCommitment( + ctx, clientState, height, prefix, proof, portID, channelID, sequence, commitmentBz, + ) +} + +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. +func (k Keeper) VerifyPacketAcknowledgement( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyPacketAcknowledgement( + ctx, clientState, height, prefix, proof, portID, channelID, sequence, acknowledgement, + ) +} + +// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an +// incoming packet acknowledgement at the specified port, specified channel, and +// specified sequence. +func (k Keeper) VerifyPacketAcknowledgementAbsence( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyPacketAcknowledgementAbsence( + ctx, clientState, height, prefix, proof, portID, channelID, sequence, + ) +} + +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. +func (k Keeper) VerifyNextSequenceRecv( + ctx sdk.Context, + connection types.ConnectionEnd, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + nextSequenceRecv uint64, +) (bool, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + if !found { + return false, clienterrors.ErrClientNotFound + } + + return k.clientKeeper.VerifyNextSequenceRecv( + ctx, clientState, height, prefix, proof, portID, channelID, nextSequenceRecv, + ) +} diff --git a/x/ibc/03-connection/types/keys.go b/x/ibc/03-connection/types/keys.go index 345b6b58d023..23478ec7591a 100644 --- a/x/ibc/03-connection/types/keys.go +++ b/x/ibc/03-connection/types/keys.go @@ -1,11 +1,5 @@ package types -import ( - "fmt" - - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" -) - const ( // SubModuleName defines the IBC connection name SubModuleName = "connection" @@ -19,44 +13,3 @@ const ( // QuerierRoute is the querier route for IBC connections QuerierRoute = SubModuleName ) - -// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#store-paths - -// ClientConnectionsPath defines a reverse mapping from clients to a set of connections -func ClientConnectionsPath(clientID string) string { - return string(KeyClientConnections(clientID)) -} - -// ConnectionPath defines the path under which connection paths are stored -func ConnectionPath(connectionID string) string { - return string(KeyConnection(connectionID)) -} - -// KeyClientConnections returns the store key for the connectios of a given client -func KeyClientConnections(clientID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyClientConnectionsPrefix), - []byte(clientConnectionsPath(clientID))..., - ) -} - -// KeyConnection returns the store key for a particular connection -func KeyConnection(connectionID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyConnectionPrefix), - []byte(connectionPath(connectionID))..., - ) -} - -// GetConnectionsKeysPrefix return the connection prefixes -func GetConnectionsKeysPrefix(prefix int) []byte { - return []byte(fmt.Sprintf("%d/connections", prefix)) -} - -func clientConnectionsPath(clientID string) string { - return fmt.Sprintf("clients/%s/connections", clientID) -} - -func connectionPath(connectionID string) string { - return fmt.Sprintf("connections/%s", connectionID) -} diff --git a/x/ibc/04-channel/types/keys.go b/x/ibc/04-channel/types/keys.go index 1fcbb4f44806..21372dc48d99 100644 --- a/x/ibc/04-channel/types/keys.go +++ b/x/ibc/04-channel/types/keys.go @@ -1,11 +1,5 @@ package types -import ( - "fmt" - - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" -) - const ( // SubModuleName defines the IBC channels name SubModuleName = "channels" @@ -19,116 +13,3 @@ const ( // QuerierRoute is the querier route for IBC channels QuerierRoute = SubModuleName ) - -// ChannelPath defines the path under which channels are stored -func ChannelPath(portID, channelID string) string { - return string(KeyChannel(portID, channelID)) -} - -// ChannelCapabilityPath defines the path under which capability keys associated -// with a channel are stored -func ChannelCapabilityPath(portID, channelID string) string { - return string(KeyChannelCapabilityPath(portID, channelID)) -} - -// NextSequenceSendPath defines the next send sequence counter store path -func NextSequenceSendPath(portID, channelID string) string { - return string(KeyNextSequenceSend(portID, channelID)) -} - -// NextSequenceRecvPath defines the next receive sequence counter store path -func NextSequenceRecvPath(portID, channelID string) string { - return string(KeyNextSequenceRecv(portID, channelID)) -} - -// PacketCommitmentPath defines the commitments to packet data fields store path -func PacketCommitmentPath(portID, channelID string, sequence uint64) string { - return string(KeyPacketCommitment(portID, channelID, sequence)) -} - -// PacketAcknowledgementPath defines the packet acknowledgement store path -func PacketAcknowledgementPath(portID, channelID string, sequence uint64) string { - return string(KeyPacketAcknowledgement(portID, channelID, sequence)) -} - -// KeyChannel returns the store key for a particular channel -func KeyChannel(portID, channelID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyChannelPrefix), - []byte(channelPath(portID, channelID))..., - ) -} - -// KeyChannelCapabilityPath returns the store key for the capability key of a -// particular channel binded to a specific port -func KeyChannelCapabilityPath(portID, channelID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyChannelCapabilityPrefix), - []byte(channelCapabilityPath(portID, channelID))..., - ) -} - -// KeyNextSequenceSend returns the store key for the send sequence of a particular -// channel binded to a specific port -func KeyNextSequenceSend(portID, channelID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyNextSeqSendPrefix), - []byte(nextSequenceSendPath(portID, channelID))..., - ) -} - -// KeyNextSequenceRecv returns the store key for the receive sequence of a particular -// channel binded to a specific port -func KeyNextSequenceRecv(portID, channelID string) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyNextSeqRecvPrefix), - []byte(nextSequenceRecvPath(portID, channelID))..., - ) -} - -// KeyPacketCommitment returns the store key of under which a packet commitment -// is stored -func KeyPacketCommitment(portID, channelID string, sequence uint64) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyPacketCommitmentPrefix), - []byte(packetCommitmentPath(portID, channelID, sequence))..., - ) -} - -// KeyPacketAcknowledgement returns the store key of under which a packet -// acknowledgement is stored -func KeyPacketAcknowledgement(portID, channelID string, sequence uint64) []byte { - return append( - ibctypes.KeyPrefixBytes(ibctypes.KeyPacketAckPrefix), - []byte(packetAcknowledgementPath(portID, channelID, sequence))..., - ) -} - -// GetChannelKeysPrefix returns the prefix bytes for ICS04 iterators -func GetChannelKeysPrefix(prefix int) []byte { - return []byte(fmt.Sprintf("%d/ports/", prefix)) -} - -func channelPath(portID, channelID string) string { - return fmt.Sprintf("ports/%s/channels/%s", portID, channelID) -} - -func channelCapabilityPath(portID, channelID string) string { - return channelPath(portID, channelID) + "/key" -} - -func nextSequenceSendPath(portID, channelID string) string { - return channelPath(portID, channelID) + "/nextSequenceSend" -} - -func nextSequenceRecvPath(portID, channelID string) string { - return channelPath(portID, channelID) + "/nextSequenceRecv" -} - -func packetCommitmentPath(portID, channelID string, sequence uint64) string { - return channelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) -} - -func packetAcknowledgementPath(portID, channelID string, sequence uint64) string { - return channelPath(portID, channelID) + fmt.Sprintf("/acknowledgements/%d", sequence) -} diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index fc00e46f16f0..06bed7158532 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,6 +1,8 @@ package tendermint import ( + "fmt" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" @@ -56,7 +58,8 @@ func (cs ClientState) VerifyClientConsensusState( prefix commitment.PrefixI, proof commitment.ProofI, clientID string, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + // TODO: use path function + path, err := commitment.ApplyPrefix(prefix, fmt.Sprintf("consensusState/%s", clientID)) if err != nil { return nil } @@ -88,7 +91,8 @@ func (cs ClientState) VerifyConnectionState( // connectionEnd connection, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + // TODO: use path function + path, err := commitment.ApplyPrefix(prefix, fmt.Sprintf("connection/%s", connectionID)) if err != nil { return nil } diff --git a/x/ibc/types/keys.go b/x/ibc/types/keys.go index f4f39edcd495..453f5fdc04ae 100644 --- a/x/ibc/types/keys.go +++ b/x/ibc/types/keys.go @@ -16,16 +16,18 @@ const ( RouterKey string = ModuleName ) +// KVStore key prefixes for IBC +var ( + KeyClientPrefix = []byte("clientState") + KeyClientTypePrefix = []byte("clientType") + KeyConsensusStatePrefix = []byte("consensusState") + KeyClientConnectionsPrefix = []byte("clientConnections") + KeyConnectionPrefix = []byte("connection") +) + // KVStore key prefixes for IBC const ( - KeyClientPrefix int = iota + 1 - KeyClientTypePrefix - KeyConsensusStatePrefix - KeyRootPrefix - KeyCommiterPrefix - KeyClientConnectionsPrefix - KeyConnectionPrefix - KeyChannelPrefix + KeyChannelPrefix int = iota + 1 KeyChannelCapabilityPrefix KeyNextSeqSendPrefix KeyNextSeqRecvPrefix @@ -38,3 +40,147 @@ const ( func KeyPrefixBytes(prefix int) []byte { return []byte(fmt.Sprintf("%d/", prefix)) } + +// ICS02 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space + +// ClientStatePath takes an Identifier and returns a Path under which to store a +// particular client state +func ClientStatePath(clientID string) string { + return fmt.Sprintf("clientState/%s", clientID) +} + +// ClientTypePath takes an Identifier and returns Path under which to store the +// type of a particular client. +func ClientTypePath(clientID string) string { + return fmt.Sprintf("clientType/%s", clientID) +} + +// ConsensusStatePath takes an Identifier and returns a Path under which to +// store the consensus state of a client. +func ConsensusStatePath(clientID string, height uint64) string { + return fmt.Sprintf("consensusState/%s/%d", clientID, height) +} + +// KeyClientState returns the store key for a particular client state +func KeyClientState(clientID string) []byte { + return []byte(ClientStatePath(clientID)) +} + +// KeyClientType returns the store key for type of a particular client +func KeyClientType(clientID string) []byte { + return []byte(ClientTypePath(clientID)) +} + +// KeyConsensusState returns the store key for the consensus state of a particular +// client +func KeyConsensusState(clientID string, height uint64) []byte { + return []byte(ConsensusStatePath(clientID, height)) +} + +// ICS03 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#store-paths + +// ClientConnectionsPath defines a reverse mapping from clients to a set of connections +func ClientConnectionsPath(clientID string) string { + return fmt.Sprintf("clientConnections/%s/", clientID) +} + +// ConnectionPath defines the path under which connection paths are stored +func ConnectionPath(connectionID string) string { + return fmt.Sprintf("connection/%s", connectionID) +} + +// KeyClientConnections returns the store key for the connectios of a given client +func KeyClientConnections(clientID string) []byte { + return []byte(ClientConnectionsPath(clientID)) +} + +// KeyConnection returns the store key for a particular connection +func KeyConnection(connectionID string) []byte { + return []byte(ConnectionPath(connectionID)) +} + +// ICS04 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#store-paths + +// ChannelPath defines the path under which channels are stored +func ChannelPath(portID, channelID string) string { + return fmt.Sprintf("ports/%s/channels/%s", portID, channelID) +} + +// ChannelCapabilityPath defines the path under which capability keys associated +// with a channel are stored +func ChannelCapabilityPath(portID, channelID string) string { + return ChannelPath(portID, channelID) + "/key" +} + +// NextSequenceSendPath defines the next send sequence counter store path +func NextSequenceSendPath(portID, channelID string) string { + return ChannelPath(portID, channelID) + "/nextSequenceSend" +} + +// NextSequenceRecvPath defines the next receive sequence counter store path +func NextSequenceRecvPath(portID, channelID string) string { + return ChannelPath(portID, channelID) + "/nextSequenceRecv" +} + +// PacketCommitmentPath defines the commitments to packet data fields store path +func PacketCommitmentPath(portID, channelID string, sequence uint64) string { + return ChannelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) +} + +// PacketAcknowledgementPath defines the packet acknowledgement store path +func PacketAcknowledgementPath(portID, channelID string, sequence uint64) string { + return ChannelPath(portID, channelID) + fmt.Sprintf("/acknowledgements/%d", sequence) +} + +// KeyChannel returns the store key for a particular channel +func KeyChannel(portID, channelID string) []byte { + return []byte(ChannelPath(portID, channelID)) +} + +// KeyChannelCapabilityPath returns the store key for the capability key of a +// particular channel binded to a specific port +func KeyChannelCapabilityPath(portID, channelID string) []byte { + return []byte(ChannelCapabilityPath(portID, channelID)) +} + +// KeyNextSequenceSend returns the store key for the send sequence of a particular +// channel binded to a specific port +func KeyNextSequenceSend(portID, channelID string) []byte { + return []byte(NextSequenceSendPath(portID, channelID)) +} + +// KeyNextSequenceRecv returns the store key for the receive sequence of a particular +// channel binded to a specific port +func KeyNextSequenceRecv(portID, channelID string) []byte { + return []byte(NextSequenceRecvPath(portID, channelID)) +} + +// KeyPacketCommitment returns the store key of under which a packet commitment +// is stored +func KeyPacketCommitment(portID, channelID string, sequence uint64) []byte { + return []byte(PacketCommitmentPath(portID, channelID, sequence)) +} + +// KeyPacketAcknowledgement returns the store key of under which a packet +// acknowledgement is stored +func KeyPacketAcknowledgement(portID, channelID string, sequence uint64) []byte { + return []byte(PacketAcknowledgementPath(portID, channelID, sequence)) +} + +// ICS05 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#store-paths + +// PortPath defines the path under which ports paths are stored +func PortPath(portID string) string { + return fmt.Sprintf("ports/%s", portID) +} + +// KeyPort returns the store key for a particular port +func KeyPort(portID string) []byte { + return []byte(PortPath(portID)) +} + + From 9c15f5d33737ae9c549b9ee62446e34636111f8e Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jan 2020 17:07:12 +0100 Subject: [PATCH 08/35] fix paths --- x/ibc/02-client/alias.go | 8 -------- x/ibc/02-client/client/utils/utils.go | 5 +++-- x/ibc/02-client/keeper/keeper.go | 14 +++++++------- x/ibc/02-client/types/querier.go | 5 +++-- x/ibc/03-connection/alias.go | 4 ---- x/ibc/03-connection/client/utils/utils.go | 5 +++-- x/ibc/03-connection/keeper/handshake.go | 6 +++--- x/ibc/03-connection/keeper/keeper.go | 10 +++++----- x/ibc/03-connection/types/querier.go | 5 +++-- x/ibc/04-channel/alias.go | 12 ------------ x/ibc/04-channel/client/utils/utils.go | 5 +++-- x/ibc/04-channel/types/querier.go | 7 ++++--- x/ibc/20-transfer/client/utils/utils.go | 3 ++- x/ibc/types/keys.go | 22 ++++++++++++---------- 14 files changed, 48 insertions(+), 63 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index b23defe586eb..ab73bd2be6f7 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -39,14 +39,6 @@ var ( ErrRootNotFound = errors.ErrRootNotFound ErrInvalidHeader = errors.ErrInvalidHeader ErrInvalidEvidence = errors.ErrInvalidEvidence - ClientStatePath = types.ClientStatePath - ClientTypePath = types.ClientTypePath - ConsensusStatePath = types.ConsensusStatePath - RootPath = types.RootPath - KeyClientState = types.KeyClientState - KeyClientType = types.KeyClientType - KeyConsensusState = types.KeyConsensusState - KeyRoot = types.KeyRoot NewMsgCreateClient = types.NewMsgCreateClient NewMsgUpdateClient = types.NewMsgUpdateClient diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go index bd7a181ec14d..62d09a94db1e 100644 --- a/x/ibc/02-client/client/utils/utils.go +++ b/x/ibc/02-client/client/utils/utils.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // QueryAllClientStates returns all the light client states. It _does not_ return @@ -43,7 +44,7 @@ func QueryClientState( ) (types.StateResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyClientState(clientID), + Data: ibctypes.KeyClientState(clientID), Prove: prove, } @@ -71,7 +72,7 @@ func QueryConsensusState( req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyConsensusState(clientID, height), + Data: ibctypes.KeyConsensusState(clientID, height), Prove: prove, } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index b1c7461143b3..e40f534e542f 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -37,7 +37,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { // GetClientState gets a particular client from the store func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyClientState(clientID)) + bz := store.Get(ibctypes.KeyClientState(clientID)) if bz == nil { return nil, false } @@ -51,13 +51,13 @@ func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.Clien func (k Keeper) SetClientState(ctx sdk.Context, clientState exported.ClientState) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) - store.Set(types.KeyClientState(clientState.GetID()), bz) + store.Set(ibctypes.KeyClientState(clientState.GetID()), bz) } // GetClientType gets the consensus type for a specific client func (k Keeper) GetClientType(ctx sdk.Context, clientID string) (exported.ClientType, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyClientType(clientID)) + bz := store.Get(ibctypes.KeyClientType(clientID)) if bz == nil { return 0, false } @@ -68,13 +68,13 @@ func (k Keeper) GetClientType(ctx sdk.Context, clientID string) (exported.Client // SetClientType sets the specific client consensus type to the provable store func (k Keeper) SetClientType(ctx sdk.Context, clientID string, clientType exported.ClientType) { store := ctx.KVStore(k.storeKey) - store.Set(types.KeyClientType(clientID), []byte{byte(clientType)}) + store.Set(ibctypes.KeyClientType(clientID), []byte{byte(clientType)}) } // GetConsensusState creates a new client state and populates it with a given consensus state func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string, height uint64) (exported.ConsensusState, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyConsensusState(clientID, height)) + bz := store.Get(ibctypes.KeyConsensusState(clientID, height)) if bz == nil { return nil, false } @@ -88,7 +88,7 @@ func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string, height uint6 func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, height uint64, consensusState exported.ConsensusState) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState) - store.Set(types.KeyConsensusState(clientID, height), bz) + store.Set(ibctypes.KeyConsensusState(clientID, height), bz) } // IterateClients provides an iterator over all stored light client State @@ -96,7 +96,7 @@ func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, height uint6 // the iterator will close and stop. func (k Keeper) IterateClients(ctx sdk.Context, cb func(exported.ClientState) bool) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.GetClientKeysPrefix(ibctypes.KeyClientPrefix)) + iterator := sdk.KVStorePrefixIterator(store, ibctypes.KeyClientPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 7c7827ceadc3..31a1b3586006 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // query routes supported by the IBC client Querier @@ -47,7 +48,7 @@ func NewClientStateResponse( return StateResponse{ ClientState: clientState, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ClientStatePath(clientID), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.ClientStatePath(clientID), "/")), ProofHeight: uint64(height), } } @@ -68,7 +69,7 @@ func NewConsensusStateResponse( return ConsensusStateResponse{ ConsensusState: cs, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ConsensusStatePath(clientID, uint64(height)), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.ConsensusStatePath(clientID, uint64(height)), "/")), ProofHeight: uint64(height), } } diff --git a/x/ibc/03-connection/alias.go b/x/ibc/03-connection/alias.go index 2cb4faf887ff..e7017e05e9d9 100644 --- a/x/ibc/03-connection/alias.go +++ b/x/ibc/03-connection/alias.go @@ -47,10 +47,6 @@ var ( ErrConnectionPath = types.ErrConnectionPath ErrInvalidConnectionState = types.ErrInvalidConnectionState ErrInvalidCounterparty = types.ErrInvalidCounterparty - ConnectionPath = types.ConnectionPath - ClientConnectionsPath = types.ClientConnectionsPath - KeyConnection = types.KeyConnection - KeyClientConnections = types.KeyClientConnections NewMsgConnectionOpenInit = types.NewMsgConnectionOpenInit NewMsgConnectionOpenTry = types.NewMsgConnectionOpenTry NewMsgConnectionOpenAck = types.NewMsgConnectionOpenAck diff --git a/x/ibc/03-connection/client/utils/utils.go b/x/ibc/03-connection/client/utils/utils.go index 55dcdcbff49e..29c52a33be9a 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/codec" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // QueryAllConnections returns all the connections. It _does not_ return @@ -44,7 +45,7 @@ func QueryConnection( ) (types.ConnectionResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyConnection(connectionID), + Data: ibctypes.KeyConnection(connectionID), Prove: prove, } @@ -70,7 +71,7 @@ func QueryClientConnections( ) (types.ClientConnectionsResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyClientConnections(clientID), + Data: ibctypes.KeyClientConnections(clientID), Prove: prove, } diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 8a40c427de52..82bfa3b400d1 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -86,7 +86,7 @@ func (k Keeper) ConnOpenTry( ok := k.VerifyMembership( ctx, connection, proofHeight, proofInit, - types.ConnectionPath(counterparty.ConnectionID), expConnBz, + ibctypes.ConnectionPath(counterparty.ConnectionID), expConnBz, ) if !ok { return errors.New("couldn't verify connection membership on counterparty's client") // TODO: sdk.Error @@ -182,7 +182,7 @@ func (k Keeper) ConnOpenAck( ok := k.VerifyMembership( ctx, connection, proofHeight, proofTry, - types.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, + ibctypes.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, ) if !ok { return errors.New("couldn't verify connection membership on counterparty's client") // TODO: sdk.Error @@ -244,7 +244,7 @@ func (k Keeper) ConnOpenConfirm( ok := k.VerifyMembership( ctx, connection, proofHeight, proofAck, - types.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, + ibctypes.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, ) if !ok { return errors.New("couldn't verify connection membership on counterparty's client") diff --git a/x/ibc/03-connection/keeper/keeper.go b/x/ibc/03-connection/keeper/keeper.go index 5edec4cdf32b..59bd8ee65fd6 100644 --- a/x/ibc/03-connection/keeper/keeper.go +++ b/x/ibc/03-connection/keeper/keeper.go @@ -44,7 +44,7 @@ func (k Keeper) GetCommitmentPrefix() commitment.PrefixI { // GetConnection returns a connection with a particular identifier func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.ConnectionEnd, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyConnection(connectionID)) + bz := store.Get(ibctypes.KeyConnection(connectionID)) if bz == nil { return types.ConnectionEnd{}, false } @@ -58,14 +58,14 @@ func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.Conne func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(connection) - store.Set(types.KeyConnection(connectionID), bz) + store.Set(ibctypes.KeyConnection(connectionID), bz) } // GetClientConnectionPaths returns all the connection paths stored under a // particular client func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]string, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyClientConnections(clientID)) + bz := store.Get(ibctypes.KeyClientConnections(clientID)) if bz == nil { return nil, false } @@ -79,7 +79,7 @@ func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]st func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths []string) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(paths) - store.Set(types.KeyClientConnections(clientID), bz) + store.Set(ibctypes.KeyClientConnections(clientID), bz) } // IterateConnections provides an iterator over all ConnectionEnd objects. @@ -87,7 +87,7 @@ func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths // iterator will close and stop. func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.ConnectionEnd) bool) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.GetConnectionsKeysPrefix(ibctypes.KeyConnectionPrefix)) + iterator := sdk.KVStorePrefixIterator(store, ibctypes.KeyConnectionPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { diff --git a/x/ibc/03-connection/types/querier.go b/x/ibc/03-connection/types/querier.go index 02581d42b15e..5cd3c6907b60 100644 --- a/x/ibc/03-connection/types/querier.go +++ b/x/ibc/03-connection/types/querier.go @@ -6,6 +6,7 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // query routes supported by the IBC connection Querier @@ -31,7 +32,7 @@ func NewConnectionResponse( return ConnectionResponse{ Connection: connection, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ConnectionPath(connectionID), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.ConnectionPath(connectionID), "/")), ProofHeight: uint64(height), } } @@ -81,7 +82,7 @@ func NewClientConnectionsResponse( return ClientConnectionsResponse{ ConnectionPaths: connectionPaths, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ClientConnectionsPath(clientID), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.ClientConnectionsPath(clientID), "/")), ProofHeight: uint64(height), } } diff --git a/x/ibc/04-channel/alias.go b/x/ibc/04-channel/alias.go index ff4895eae42a..fb8506f2dd4f 100644 --- a/x/ibc/04-channel/alias.go +++ b/x/ibc/04-channel/alias.go @@ -59,18 +59,6 @@ var ( ErrPacketTimeout = types.ErrPacketTimeout ErrInvalidChannel = types.ErrInvalidChannel ErrInvalidChannelState = types.ErrInvalidChannelState - ChannelPath = types.ChannelPath - ChannelCapabilityPath = types.ChannelCapabilityPath - NextSequenceSendPath = types.NextSequenceSendPath - NextSequenceRecvPath = types.NextSequenceRecvPath - PacketCommitmentPath = types.PacketCommitmentPath - PacketAcknowledgementPath = types.PacketAcknowledgementPath - KeyChannel = types.KeyChannel - KeyChannelCapabilityPath = types.KeyChannelCapabilityPath - KeyNextSequenceSend = types.KeyNextSequenceSend - KeyNextSequenceRecv = types.KeyNextSequenceRecv - KeyPacketCommitment = types.KeyPacketCommitment - KeyPacketAcknowledgement = types.KeyPacketAcknowledgement NewMsgChannelOpenInit = types.NewMsgChannelOpenInit NewMsgChannelOpenTry = types.NewMsgChannelOpenTry NewMsgChannelOpenAck = types.NewMsgChannelOpenAck diff --git a/x/ibc/04-channel/client/utils/utils.go b/x/ibc/04-channel/client/utils/utils.go index 912c77d463e9..8cdcd3eb8eb1 100644 --- a/x/ibc/04-channel/client/utils/utils.go +++ b/x/ibc/04-channel/client/utils/utils.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // QueryPacket returns a packet from the store @@ -15,7 +16,7 @@ func QueryPacket( ) (types.PacketResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyPacketCommitment(portID, channelID, sequence), + Data: ibctypes.KeyPacketCommitment(portID, channelID, sequence), Prove: prove, } @@ -61,7 +62,7 @@ func QueryChannel( ) (types.ChannelResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: types.KeyChannel(portID, channelID), + Data: ibctypes.KeyChannel(portID, channelID), Prove: prove, } diff --git a/x/ibc/04-channel/types/querier.go b/x/ibc/04-channel/types/querier.go index e010c6991d88..c08e3aa2b7f6 100644 --- a/x/ibc/04-channel/types/querier.go +++ b/x/ibc/04-channel/types/querier.go @@ -6,6 +6,7 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // query routes supported by the IBC channel Querier @@ -30,7 +31,7 @@ func NewChannelResponse( return ChannelResponse{ Channel: channel, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(ChannelPath(portID, channelID), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.ChannelPath(portID, channelID), "/")), ProofHeight: uint64(height), } } @@ -81,7 +82,7 @@ func NewPacketResponse( return PacketResponse{ Packet: packet, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(PacketCommitmentPath(portID, channelID, sequence), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.PacketCommitmentPath(portID, channelID, sequence), "/")), ProofHeight: uint64(height), } } @@ -103,7 +104,7 @@ func NewRecvResponse( return RecvResponse{ NextSequenceRecv: sequenceRecv, Proof: commitment.Proof{Proof: proof}, - ProofPath: commitment.NewPath(strings.Split(NextSequenceRecvPath(portID, channelID), "/")), + ProofPath: commitment.NewPath(strings.Split(ibctypes.NextSequenceRecvPath(portID, channelID), "/")), ProofHeight: uint64(height), } } diff --git a/x/ibc/20-transfer/client/utils/utils.go b/x/ibc/20-transfer/client/utils/utils.go index b89b2116b8d4..b2fe98034b5a 100644 --- a/x/ibc/20-transfer/client/utils/utils.go +++ b/x/ibc/20-transfer/client/utils/utils.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // QueryNextSequenceRecv queries the store to get the next receive sequence and @@ -16,7 +17,7 @@ func QueryNextSequenceRecv( ) (channeltypes.RecvResponse, error) { req := abci.RequestQuery{ Path: "store/ibc/key", - Data: channeltypes.KeyNextSequenceRecv(portID, channelID), + Data: ibctypes.KeyNextSequenceRecv(portID, channelID), Prove: prove, } diff --git a/x/ibc/types/keys.go b/x/ibc/types/keys.go index 453f5fdc04ae..59a0051793cc 100644 --- a/x/ibc/types/keys.go +++ b/x/ibc/types/keys.go @@ -102,37 +102,37 @@ func KeyConnection(connectionID string) []byte { } // ICS04 -// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#store-paths +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#store-paths // ChannelPath defines the path under which channels are stored func ChannelPath(portID, channelID string) string { - return fmt.Sprintf("ports/%s/channels/%s", portID, channelID) + return fmt.Sprintf("%d/", KeyChannelPrefix) + channelPath(portID, channelID) } // ChannelCapabilityPath defines the path under which capability keys associated // with a channel are stored func ChannelCapabilityPath(portID, channelID string) string { - return ChannelPath(portID, channelID) + "/key" + return fmt.Sprintf("%d/", KeyChannelCapabilityPrefix) + channelPath(portID, channelID) + "/key" } // NextSequenceSendPath defines the next send sequence counter store path func NextSequenceSendPath(portID, channelID string) string { - return ChannelPath(portID, channelID) + "/nextSequenceSend" + return fmt.Sprintf("%d/", KeyNextSeqSendPrefix) + channelPath(portID, channelID) + "/nextSequenceSend" } // NextSequenceRecvPath defines the next receive sequence counter store path func NextSequenceRecvPath(portID, channelID string) string { - return ChannelPath(portID, channelID) + "/nextSequenceRecv" + return fmt.Sprintf("%d/", KeyNextSeqRecvPrefix) + channelPath(portID, channelID) + "/nextSequenceRecv" } // PacketCommitmentPath defines the commitments to packet data fields store path func PacketCommitmentPath(portID, channelID string, sequence uint64) string { - return ChannelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) + return fmt.Sprintf("%d/", KeyPacketCommitmentPrefix) + channelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) } // PacketAcknowledgementPath defines the packet acknowledgement store path func PacketAcknowledgementPath(portID, channelID string, sequence uint64) string { - return ChannelPath(portID, channelID) + fmt.Sprintf("/acknowledgements/%d", sequence) + return fmt.Sprintf("%d/", KeyPacketAckPrefix) + channelPath(portID, channelID) + fmt.Sprintf("/acknowledgements/%d", sequence) } // KeyChannel returns the store key for a particular channel @@ -170,17 +170,19 @@ func KeyPacketAcknowledgement(portID, channelID string, sequence uint64) []byte return []byte(PacketAcknowledgementPath(portID, channelID, sequence)) } +func channelPath(portID, channelID string) string { + return fmt.Sprintf("ports/%s/channels/%s", portID, channelID) +} + // ICS05 // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#store-paths // PortPath defines the path under which ports paths are stored func PortPath(portID string) string { - return fmt.Sprintf("ports/%s", portID) + return fmt.Sprintf("%d/ports/%s", KeyPortsPrefix, portID) } // KeyPort returns the store key for a particular port func KeyPort(portID string) []byte { return []byte(PortPath(portID)) } - - From 9a4a93debff20e622c37617a9afcba19a3f3af0c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 15 Jan 2020 11:57:07 +0100 Subject: [PATCH 09/35] update ICS03 connection verification --- x/ibc/02-client/exported/exported.go | 6 +- x/ibc/03-connection/exported/exported.go | 24 +++ x/ibc/03-connection/keeper/handshake.go | 137 +++++++----------- x/ibc/03-connection/keeper/keeper.go | 35 +---- x/ibc/03-connection/keeper/verify.go | 62 ++++---- x/ibc/03-connection/types/codec.go | 8 +- x/ibc/03-connection/types/connection.go | 60 +++++++- x/ibc/03-connection/types/expected_keepers.go | 9 -- x/ibc/04-channel/types/codec.go | 5 +- x/ibc/04-channel/types/expected_keepers.go | 16 +- x/ibc/07-tendermint/client_state.go | 70 +++++---- 11 files changed, 220 insertions(+), 212 deletions(-) create mode 100644 x/ibc/03-connection/exported/exported.go diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 0111ca194025..469a4d1a9af5 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -6,6 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -32,7 +34,7 @@ type ClientState interface { prefix commitment.PrefixI, proof commitment.ProofI, connectionID string, - // connectionEnd connection, + connectionEnd connectionexported.ConnectionI, consensusState ConsensusState, ) error VerifyChannelState( @@ -42,7 +44,7 @@ type ClientState interface { proof commitment.ProofI, portID, channelID string, - // channelEnd channel, + channelEnd channeltypes.Channel, consensusState ConsensusState, ) error VerifyPacketCommitment( diff --git a/x/ibc/03-connection/exported/exported.go b/x/ibc/03-connection/exported/exported.go new file mode 100644 index 000000000000..6e2142035313 --- /dev/null +++ b/x/ibc/03-connection/exported/exported.go @@ -0,0 +1,24 @@ +package exported + +// ConnectionI describes the required methods for a connection. +type ConnectionI interface { + GetState() StateI + GetClientID() string + GetCounterparty() CounterpartyI + GetVersions() []string + ValidateBasic() error +} + +// CounterpartyI describes the required methods for a counterparty connection. +type CounterpartyI interface { + GetClientID() string + GetConnectionID() string + ValidateBasic() error +} + +// StateI describes the required methods for a connection state. +type StateI interface { + String() string + MarshalJSON() ([]byte, error) + UnmarshalJSON(data []byte) error +} \ No newline at end of file diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 82bfa3b400d1..afe014073a5f 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -1,11 +1,11 @@ package keeper import ( - "errors" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -56,22 +56,20 @@ func (k Keeper) ConnOpenTry( consensusHeight uint64, ) error { // XXX: blocked by #5078 - /* - if consensusHeight > uint64(ctx.BlockHeight()) { - return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") - } + // if consensusHeight > uint64(ctx.BlockHeight()) { + // return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") + // } - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, clientID) - if !found { - return errors.New("client consensus state not found") // TODO: use ICS02 error - } - */ + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, clientID) + if !found { + return clienterrors.ErrConsensusStateNotFound + } - // expectedConn defines Chain A's ConnectionEnd + // expectedConnection defines Chain A's ConnectionEnd // NOTE: chain A's counterparty is chain B (i.e where this code is executed) prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(clientID, connectionID, prefix) - expectedConn := types.NewConnectionEnd(types.INIT, counterparty.ClientID, expectedCounterparty, counterpartyVersions) + expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientID, expectedCounterparty, counterpartyVersions) // chain B picks a version from Chain A's available versions that is compatible // with the supported IBC versions @@ -79,38 +77,25 @@ func (k Keeper) ConnOpenTry( // connection defines chain B's ConnectionEnd connection := types.NewConnectionEnd(types.UNINITIALIZED, clientID, counterparty, []string{version}) - expConnBz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedConn) - if err != nil { - return err - } - ok := k.VerifyMembership( - ctx, connection, proofHeight, proofInit, - ibctypes.ConnectionPath(counterparty.ConnectionID), expConnBz, + err := k.VerifyConnectionState( + ctx, proofHeight, proofInit, counterparty.ConnectionID, + expectedConnection, expectedConsensusState, ) - if !ok { - return errors.New("couldn't verify connection membership on counterparty's client") // TODO: sdk.Error + if err != nil { + return err } // XXX: blocked by #5078 - /* - expConsStateBz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedConsensusState) - if err != nil { - return err - } + // err = k.VerifyClientConsensusState( + // ctx, proofHeight, proofInit, expectedConsensusState, + // ) + // if err != nil { + // return err + // } - ok = k.VerifyMembership( - ctx, connection, proofHeight, proofConsensus, - clienttypes.ConsensusStatePath(counterparty.ClientID), expConsStateBz, - ) - if !ok { - fmt.Sprintf("couldn't verify consensus state membership on counterparty's client\n") - return errors.New("couldn't verify consensus state membership on counterparty's client") // TODO: sdk.Error - } - */ - - _, found := k.GetConnection(ctx, connectionID) + _, found = k.GetConnection(ctx, connectionID) if found { return sdkerrors.Wrap(types.ErrConnectionExists, "cannot relay connection attempt") } @@ -140,11 +125,10 @@ func (k Keeper) ConnOpenAck( consensusHeight uint64, ) error { // XXX: blocked by #5078 - /* - if consensusHeight > uint64(ctx.BlockHeight()) { - return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") - } - */ + // if consensusHeight > uint64(ctx.BlockHeight()) { + // return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") + // } + connection, found := k.GetConnection(ctx, connectionID) if !found { return sdkerrors.Wrap(types.ErrConnectionNotFound, "cannot relay ACK of open attempt") @@ -164,46 +148,31 @@ func (k Keeper) ConnOpenAck( ) } - // XXX: blocked by #5078 - /* - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID) - if !found { - return errors.New("client consensus state not found") // TODO: use ICS02 error - } - */ + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID) + if !found { + return clienterrors.ErrConsensusStateNotFound + } + prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) - expectedConn := types.NewConnectionEnd(types.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version}) + expectedConnection := types.NewConnectionEnd(types.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version}) - expConnBz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedConn) + err := k.VerifyConnectionState( + ctx, proofHeight, proofTry, connection.Counterparty.ConnectionID, + expectedConnection, expectedConsensusState, + ) if err != nil { return err } - ok := k.VerifyMembership( - ctx, connection, proofHeight, proofTry, - ibctypes.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, - ) - if !ok { - return errors.New("couldn't verify connection membership on counterparty's client") // TODO: sdk.Error - } - // XXX: blocked by #5078 - /* - expConsStateBz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedConsensusState) - if err != nil { - return err - } - - ok = k.VerifyMembership( - ctx, connection, proofHeight, proofConsensus, - clienttypes.ConsensusStatePath(connection.Counterparty.ClientID), expConsStateBz, - ) - if !ok { - return errors.New("couldn't verify consensus state membership on counterparty's client") // TODO: sdk.Error - } - - */ + // err = k.VerifyClientConsensusState( + // ctx, connection, proofHeight, proofInit, expectedConsensusState, + // ) + // if err != nil { + // return err + // } + connection.State = types.OPEN connection.Versions = []string{version} k.SetConnection(ctx, connectionID, connection) @@ -233,23 +202,23 @@ func (k Keeper) ConnOpenConfirm( ) } + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID) + if !found { + return clienterrors.ErrConsensusStateNotFound + } + prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) - expectedConn := types.NewConnectionEnd(types.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions) + expectedConnection := types.NewConnectionEnd(types.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions) - expConnBz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedConn) + err := k.VerifyConnectionState( + ctx, proofHeight, proofAck, connection.Counterparty.ConnectionID, + expectedConnection, expectedConsensusState, + ) if err != nil { return err } - ok := k.VerifyMembership( - ctx, connection, proofHeight, proofAck, - ibctypes.ConnectionPath(connection.Counterparty.ConnectionID), expConnBz, - ) - if !ok { - return errors.New("couldn't verify connection membership on counterparty's client") - } - connection.State = types.OPEN k.SetConnection(ctx, connectionID, connection) k.Logger(ctx).Info(fmt.Sprintf("connection %s state updated: TRYOPEN -> OPEN ", connectionID)) diff --git a/x/ibc/03-connection/keeper/keeper.go b/x/ibc/03-connection/keeper/keeper.go index 59bd8ee65fd6..9d71b244817e 100644 --- a/x/ibc/03-connection/keeper/keeper.go +++ b/x/ibc/03-connection/keeper/keeper.go @@ -142,37 +142,4 @@ func (k Keeper) removeConnectionFromClient(ctx sdk.Context, clientID, connection k.SetClientConnectionPaths(ctx, clientID, conns) return nil -} - -// VerifyMembership helper function for state membership verification -func (k Keeper) VerifyMembership( - ctx sdk.Context, - connection types.ConnectionEnd, - height uint64, - proof commitment.ProofI, - pathStr string, - value []byte, -) bool { - path, err := commitment.ApplyPrefix(connection.Counterparty.Prefix, pathStr) - if err != nil { - return false - } - - return k.clientKeeper.VerifyMembership(ctx, connection.ClientID, height, proof, path, value) -} - -// VerifyNonMembership helper function for state non-membership verification -func (k Keeper) VerifyNonMembership( - ctx sdk.Context, - connection types.ConnectionEnd, - height uint64, - proof commitment.ProofI, - pathStr string, -) bool { - path, err := commitment.ApplyPrefix(connection.Counterparty.Prefix, pathStr) - if err != nil { - return false - } - - return k.clientKeeper.VerifyNonMembership(ctx, connection.ClientID, height, proof, path) -} +} \ No newline at end of file diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 7565fd8abf96..64dbd18d6840 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -17,14 +17,14 @@ func (k Keeper) VerifyClientConsensusState( height uint64, proof commitment.ProofI, consensusState clientexported.ConsensusState, -) (bool, error) { +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } return clientState.VerifyClientConsensusState( - k.cdc, height, connection.Counterparty.Prefix, proof, consensusState, + k.cdc, height, connection.Counterparty.Prefix, proof, clientState.GetID(), consensusState, ) } @@ -36,14 +36,15 @@ func (k Keeper) VerifyConnectionState( proof commitment.ProofI, connectionID string, connection types.ConnectionEnd, -) (bool, error) { + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyConnectionState( - ctx, clientState, height, connection.Counterparty.Prefix, proof, connectionID, connection, + return clientState.VerifyConnectionState( + k.cdc, height, connection.Counterparty.Prefix, proof, connectionID, connection, consensusState, ) } @@ -58,14 +59,15 @@ func (k Keeper) VerifyChannelState( portID, channelID string, channel channeltypes.Channel, -) (bool, error) { + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyChannelState( - ctx, clientState, height, prefix, proof, portID, channelID, channel, + return clientState.VerifyChannelState( + k.cdc, height, prefix, proof, portID, channelID, channel, consensusState, ) } @@ -80,15 +82,16 @@ func (k Keeper) VerifyPacketCommitment( portID, channelID string, sequence uint64, - commitmentBz []byte, -) (bool, error) { + commitmentBytes []byte, + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyPacketCommitment( - ctx, clientState, height, prefix, proof, portID, channelID, sequence, commitmentBz, + return clientState.VerifyPacketCommitment( + height, prefix, proof, portID, channelID, sequence, commitmentBytes, consensusState, ) } @@ -104,14 +107,15 @@ func (k Keeper) VerifyPacketAcknowledgement( channelID string, sequence uint64, acknowledgement []byte, -) (bool, error) { + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyPacketAcknowledgement( - ctx, clientState, height, prefix, proof, portID, channelID, sequence, acknowledgement, + return clientState.VerifyPacketAcknowledgement( + height, prefix, proof, portID, channelID, sequence, acknowledgement, consensusState, ) } @@ -127,14 +131,15 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence( portID, channelID string, sequence uint64, -) (bool, error) { + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyPacketAcknowledgementAbsence( - ctx, clientState, height, prefix, proof, portID, channelID, sequence, + return clientState.VerifyPacketAcknowledgementAbsence( + height, prefix, proof, portID, channelID, sequence, consensusState, ) } @@ -149,13 +154,14 @@ func (k Keeper) VerifyNextSequenceRecv( portID, channelID string, nextSequenceRecv uint64, -) (bool, error) { + consensusState clientexported.ConsensusState, +) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return false, clienterrors.ErrClientNotFound + return clienterrors.ErrClientNotFound } - return k.clientKeeper.VerifyNextSequenceRecv( - ctx, clientState, height, prefix, proof, portID, channelID, nextSequenceRecv, + return clientState.VerifyNextSequenceRecv( + height, prefix, proof, portID, channelID, nextSequenceRecv, consensusState, ) } diff --git a/x/ibc/03-connection/types/codec.go b/x/ibc/03-connection/types/codec.go index 4317d3c6d539..797b0fc69fb7 100644 --- a/x/ibc/03-connection/types/codec.go +++ b/x/ibc/03-connection/types/codec.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" ) // SubModuleCdc defines the IBC connection codec. @@ -9,15 +10,20 @@ var SubModuleCdc *codec.Codec // RegisterCodec registers the IBC connection types func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.ConnectionI)(nil), nil) + cdc.RegisterInterface((*exported.CounterpartyI)(nil), nil) + cdc.RegisterInterface((*exported.StateI)(nil), nil) + cdc.RegisterConcrete(ConnectionEnd{}, "ibc/connection/ConnectionEnd", nil) + cdc.RegisterConcrete(MsgConnectionOpenInit{}, "ibc/connection/MsgConnectionOpenInit", nil) cdc.RegisterConcrete(MsgConnectionOpenTry{}, "ibc/connection/MsgConnectionOpenTry", nil) cdc.RegisterConcrete(MsgConnectionOpenAck{}, "ibc/connection/MsgConnectionOpenAck", nil) cdc.RegisterConcrete(MsgConnectionOpenConfirm{}, "ibc/connection/MsgConnectionOpenConfirm", nil) - cdc.RegisterConcrete(ConnectionEnd{}, "ibc/connection/ConnectionEnd", nil) SetSubModuleCodec(cdc) } +// SetSubModuleCodec sets the ibc connection codec func SetSubModuleCodec(cdc *codec.Codec) { SubModuleCdc = cdc } diff --git a/x/ibc/03-connection/types/connection.go b/x/ibc/03-connection/types/connection.go index 5a441c22eb26..e9d19166b728 100644 --- a/x/ibc/03-connection/types/connection.go +++ b/x/ibc/03-connection/types/connection.go @@ -4,10 +4,13 @@ import ( "encoding/json" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + exported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) +var _ exported.ConnectionI = ConnectionEnd{} + // ICS03 - Connection Data Structures as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#data-structures // ConnectionEnd defines a stateful object on a chain connected to another separate @@ -35,6 +38,34 @@ func NewConnectionEnd(state State, clientID string, counterparty Counterparty, v } } +// GetState implements the Connection interface +func (c ConnectionEnd) GetState() exported.StateI { + return &c.State +} + +// GetClientID implements the Connection interface +func (c ConnectionEnd) GetClientID() string { + return c.ClientID +} + +// GetCounterparty implements the Connection interface +func (c ConnectionEnd) GetCounterparty() exported.CounterpartyI { + return c.Counterparty +} + +// GetVersions implements the Connection interface +func (c ConnectionEnd) GetVersions() []string { + return c.Versions +} + +// ValidateBasic implements the Connection interface +func (c ConnectionEnd) ValidateBasic() error { + // TODO: + return c.Counterparty.ValidateBasic() +} + +var _ exported.CounterpartyI = Counterparty{} + // Counterparty defines the counterparty chain associated with a connection end. type Counterparty struct { ClientID string `json:"client_id" yaml:"client_id"` @@ -51,6 +82,16 @@ func NewCounterparty(clientID, connectionID string, prefix commitment.PrefixI) C } } +// GetClientID implements the CounterpartyI interface +func (c Counterparty) GetClientID() string { + return c.ClientID +} + +// GetConnectionID implements the CounterpartyI interface +func (c Counterparty) GetConnectionID() string { + return c.ConnectionID +} + // ValidateBasic performs a basic validation check of the identifiers and prefix func (c Counterparty) ValidateBasic() error { if err := host.DefaultConnectionIdentifierValidator(c.ConnectionID); err != nil { @@ -75,6 +116,9 @@ func (c Counterparty) ValidateBasic() error { return nil } +var s = State(0) +var _ exported.StateI = &s + // State defines the state of a connection between two disctinct // chains type State byte @@ -96,8 +140,8 @@ const ( ) // String implements the Stringer interface -func (cs State) String() string { - switch cs { +func (s State) String() string { + switch s { case INIT: return StateInit case TRYOPEN: @@ -124,18 +168,18 @@ func StateFromString(state string) State { } // MarshalJSON marshal to JSON using string. -func (cs State) MarshalJSON() ([]byte, error) { - return json.Marshal(cs.String()) +func (s State) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) } // UnmarshalJSON decodes from JSON assuming Bech32 encoding. -func (cs *State) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) +func (s *State) UnmarshalJSON(data []byte) error { + var str string + err := json.Unmarshal(data, &str) if err != nil { return err } - *cs = StateFromString(s) + *s = StateFromString(str) return nil } diff --git a/x/ibc/03-connection/types/expected_keepers.go b/x/ibc/03-connection/types/expected_keepers.go index 3ea564899f5a..624c05999a55 100644 --- a/x/ibc/03-connection/types/expected_keepers.go +++ b/x/ibc/03-connection/types/expected_keepers.go @@ -3,19 +3,10 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // ClientKeeper expected account IBC client keeper type ClientKeeper interface { GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) - VerifyMembership( - ctx sdk.Context, clientID string, height uint64, - proof commitment.ProofI, path commitment.PathI, value []byte, - ) bool - VerifyNonMembership( - ctx sdk.Context, clientID string, height uint64, - proof commitment.ProofI, path commitment.PathI, - ) bool } diff --git a/x/ibc/04-channel/types/codec.go b/x/ibc/04-channel/types/codec.go index dea16064dbcd..04dcbc07ca4f 100644 --- a/x/ibc/04-channel/types/codec.go +++ b/x/ibc/04-channel/types/codec.go @@ -2,9 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // SubModuleCdc defines the IBC channel codec. @@ -12,14 +10,13 @@ var SubModuleCdc *codec.Codec func init() { SubModuleCdc = codec.New() - commitment.RegisterCodec(SubModuleCdc) - client.RegisterCodec(SubModuleCdc) RegisterCodec(SubModuleCdc) } // RegisterCodec registers all the necessary types and interfaces for the // IBC channel. func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.PacketI)(nil), nil) cdc.RegisterInterface((*exported.PacketDataI)(nil), nil) cdc.RegisterConcrete(Channel{}, "ibc/channel/Channel", nil) cdc.RegisterConcrete(Packet{}, "ibc/channel/Packet", nil) diff --git a/x/ibc/04-channel/types/expected_keepers.go b/x/ibc/04-channel/types/expected_keepers.go index 4cb012e82a18..b728aee4bb42 100644 --- a/x/ibc/04-channel/types/expected_keepers.go +++ b/x/ibc/04-channel/types/expected_keepers.go @@ -2,27 +2,17 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" ) // ClientKeeper expected account IBC client keeper type ClientKeeper interface { - GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) + // GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) } // ConnectionKeeper expected account IBC connection keeper type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) - VerifyMembership( - ctx sdk.Context, connection connectiontypes.ConnectionEnd, height uint64, - proof commitment.ProofI, path string, value []byte, - ) bool - VerifyNonMembership( - ctx sdk.Context, connection connectiontypes.ConnectionEnd, height uint64, - proof commitment.ProofI, path string, - ) bool + GetConnection(ctx sdk.Context, connectionID string) (connectionexported.ConnectionI, bool) } // PortKeeper expected account IBC port keeper diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 06bed7158532..9dd5c1710745 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,12 +1,12 @@ package tendermint import ( - "fmt" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -54,12 +54,14 @@ func (cs ClientState) IsFrozen() bool { } func (cs ClientState) VerifyClientConsensusState( - cdc *codec.Codec, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, - clientID string, consensusState clientexported.ConsensusState, + cdc *codec.Codec, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + clientID string, + consensusState clientexported.ConsensusState, ) error { - // TODO: use path function - path, err := commitment.ApplyPrefix(prefix, fmt.Sprintf("consensusState/%s", clientID)) + path, err := commitment.ApplyPrefix(prefix, ibctypes.ConsensusStatePath(clientID, height)) if err != nil { return nil } @@ -85,14 +87,15 @@ func (cs ClientState) VerifyClientConsensusState( } func (cs ClientState) VerifyConnectionState( - cdc *codec.Codec, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, + cdc *codec.Codec, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, connectionID string, - // connectionEnd connection, + connectionEnd connectionexported.ConnectionI, consensusState clientexported.ConsensusState, ) error { - // TODO: use path function - path, err := commitment.ApplyPrefix(prefix, fmt.Sprintf("connection/%s", connectionID)) + path, err := commitment.ApplyPrefix(prefix, ibctypes.ConnectionPath(connectionID)) if err != nil { return nil } @@ -105,7 +108,7 @@ func (cs ClientState) VerifyConnectionState( return clienterrors.ErrClientFrozen } - bz, err := cdc.MarshalBinaryBare(consensusState) + bz, err := cdc.MarshalBinaryBare(connectionEnd) if err != nil { return err } @@ -118,13 +121,16 @@ func (cs ClientState) VerifyConnectionState( } func (cs ClientState) VerifyChannelState( - cdc *codec.Codec, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, - portID, channelID string, - // channelEnd channel, + cdc *codec.Codec, + height uint64, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + channelEnd channeltypes.Channel, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + path, err := commitment.ApplyPrefix(prefix, ibctypes.ChannelPath(portID, channelID)) if err != nil { return nil } @@ -137,7 +143,7 @@ func (cs ClientState) VerifyChannelState( return clienterrors.ErrClientFrozen } - bz, err := cdc.MarshalBinaryBare(consensusState) + bz, err := cdc.MarshalBinaryBare(channelEnd) if err != nil { return err } @@ -151,12 +157,15 @@ func (cs ClientState) VerifyChannelState( func (cs ClientState) VerifyPacketCommitment( height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, - portID, channelID string, - sequence uint64, commitmentBytes []byte, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketCommitmentPath(portID, channelID, sequence)) if err != nil { return nil } @@ -178,12 +187,15 @@ func (cs ClientState) VerifyPacketCommitment( func (cs ClientState) VerifyPacketAcknowledgement( height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, - portID, channelID string, - sequence uint64, acknowledgement []byte, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { return nil } @@ -209,7 +221,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( portID, channelID string, sequence uint64, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { return nil } @@ -238,7 +250,7 @@ func (cs ClientState) VerifyNextSequenceRecv( nextSequenceRecv uint64, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, "") + path, err := commitment.ApplyPrefix(prefix, ibctypes.NextSequenceRecvPath(portID, channelID)) if err != nil { return nil } From 4db2bdfcf8014c15c1dc9c2a774f076d3a4d6d29 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 16 Jan 2020 11:29:54 +0100 Subject: [PATCH 10/35] move order and states to exported pkg --- x/ibc/02-client/exported/exported.go | 4 +- x/ibc/03-connection/alias.go | 10 - x/ibc/03-connection/exported/exported.go | 79 +++++- x/ibc/03-connection/exported/exported_test.go | 46 ++++ x/ibc/03-connection/keeper/handshake.go | 24 +- x/ibc/03-connection/keeper/handshake_test.go | 23 +- x/ibc/03-connection/keeper/keeper_test.go | 9 +- x/ibc/03-connection/keeper/verify.go | 11 +- x/ibc/03-connection/types/codec.go | 1 - x/ibc/03-connection/types/connection.go | 89 +------ x/ibc/03-connection/types/connection_test.go | 45 +--- x/ibc/04-channel/client/cli/tx.go | 7 +- x/ibc/04-channel/client/rest/rest.go | 19 +- x/ibc/04-channel/exported/exported.go | 154 ++++++++++++ x/ibc/04-channel/exported/exported_test.go | 92 +++++++ x/ibc/04-channel/keeper/handshake.go | 232 ++++++++++-------- x/ibc/04-channel/keeper/keeper.go | 28 +-- x/ibc/04-channel/keeper/packet.go | 42 ++-- x/ibc/04-channel/types/channel.go | 189 ++++---------- x/ibc/04-channel/types/channel_test.go | 90 +------ x/ibc/04-channel/types/errors.go | 1 + x/ibc/04-channel/types/expected_keepers.go | 14 +- x/ibc/04-channel/types/msgs.go | 8 +- x/ibc/07-tendermint/client_state.go | 6 +- x/ibc/types/keys.go | 5 + 25 files changed, 660 insertions(+), 568 deletions(-) create mode 100644 x/ibc/03-connection/exported/exported_test.go create mode 100644 x/ibc/04-channel/exported/exported_test.go diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 469a4d1a9af5..ab7f9843030a 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -44,7 +44,7 @@ type ClientState interface { proof commitment.ProofI, portID, channelID string, - channelEnd channeltypes.Channel, + channel channelexported.ChannelI, consensusState ConsensusState, ) error VerifyPacketCommitment( diff --git a/x/ibc/03-connection/alias.go b/x/ibc/03-connection/alias.go index e7017e05e9d9..289e9a7c5175 100644 --- a/x/ibc/03-connection/alias.go +++ b/x/ibc/03-connection/alias.go @@ -12,14 +12,6 @@ import ( ) const ( - UNINITIALIZED = types.UNINITIALIZED - INIT = types.INIT - TRYOPEN = types.TRYOPEN - OPEN = types.OPEN - StateUninitialized = types.StateUninitialized - StateInit = types.StateInit - StateTryOpen = types.StateTryOpen - StateOpen = types.StateOpen AttributeKeyConnectionID = types.AttributeKeyConnectionID AttributeKeyCounterpartyClientID = types.AttributeKeyCounterpartyClientID SubModuleName = types.SubModuleName @@ -40,7 +32,6 @@ var ( RegisterCodec = types.RegisterCodec NewConnectionEnd = types.NewConnectionEnd NewCounterparty = types.NewCounterparty - StateFromString = types.StateFromString ErrConnectionExists = types.ErrConnectionExists ErrConnectionNotFound = types.ErrConnectionNotFound ErrClientConnectionPathsNotFound = types.ErrClientConnectionPathsNotFound @@ -72,7 +63,6 @@ type ( Keeper = keeper.Keeper ConnectionEnd = types.ConnectionEnd Counterparty = types.Counterparty - State = types.State ClientKeeper = types.ClientKeeper MsgConnectionOpenInit = types.MsgConnectionOpenInit MsgConnectionOpenTry = types.MsgConnectionOpenTry diff --git a/x/ibc/03-connection/exported/exported.go b/x/ibc/03-connection/exported/exported.go index 6e2142035313..a45570c80130 100644 --- a/x/ibc/03-connection/exported/exported.go +++ b/x/ibc/03-connection/exported/exported.go @@ -1,8 +1,14 @@ package exported +import ( + "encoding/json" + + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + // ConnectionI describes the required methods for a connection. type ConnectionI interface { - GetState() StateI + GetState() State GetClientID() string GetCounterparty() CounterpartyI GetVersions() []string @@ -13,12 +19,71 @@ type ConnectionI interface { type CounterpartyI interface { GetClientID() string GetConnectionID() string + GetPrefix() commitment.PrefixI ValidateBasic() error } -// StateI describes the required methods for a connection state. -type StateI interface { - String() string - MarshalJSON() ([]byte, error) - UnmarshalJSON(data []byte) error -} \ No newline at end of file +// State defines the state of a connection between two disctinct +// chains +type State byte + +// available connection states +const ( + UNINITIALIZED State = iota // default State + INIT + TRYOPEN + OPEN +) + +// string representation of the connection states +const ( + StateUninitialized string = "UNINITIALIZED" + StateInit string = "INIT" + StateTryOpen string = "TRYOPEN" + StateOpen string = "OPEN" +) + +// String implements the Stringer interface +func (s State) String() string { + switch s { + case INIT: + return StateInit + case TRYOPEN: + return StateTryOpen + case OPEN: + return StateOpen + default: + return StateUninitialized + } +} + +// StateFromString parses a string into a connection state +func StateFromString(state string) State { + switch state { + case StateInit: + return INIT + case StateTryOpen: + return TRYOPEN + case StateOpen: + return OPEN + default: + return UNINITIALIZED + } +} + +// MarshalJSON marshal to JSON using string. +func (s State) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// UnmarshalJSON decodes from JSON assuming Bech32 encoding. +func (s *State) UnmarshalJSON(data []byte) error { + var str string + err := json.Unmarshal(data, &str) + if err != nil { + return err + } + + *s = StateFromString(str) + return nil +} diff --git a/x/ibc/03-connection/exported/exported_test.go b/x/ibc/03-connection/exported/exported_test.go new file mode 100644 index 000000000000..c45dfe6cca2b --- /dev/null +++ b/x/ibc/03-connection/exported/exported_test.go @@ -0,0 +1,46 @@ +package exported + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConnectionStateString(t *testing.T) { + cases := []struct { + name string + state State + }{ + {StateUninitialized, UNINITIALIZED}, + {StateInit, INIT}, + {StateTryOpen, TRYOPEN}, + {StateOpen, OPEN}, + } + + for _, tt := range cases { + tt := tt + require.Equal(t, tt.state, StateFromString(tt.name)) + require.Equal(t, tt.name, tt.state.String()) + } +} + +func TestConnectionlStateMarshalJSON(t *testing.T) { + cases := []struct { + name string + state State + }{ + {StateUninitialized, UNINITIALIZED}, + {StateInit, INIT}, + {StateTryOpen, TRYOPEN}, + {StateOpen, OPEN}, + } + + for _, tt := range cases { + tt := tt + bz, err := tt.state.MarshalJSON() + require.NoError(t, err) + var state State + require.NoError(t, state.UnmarshalJSON(bz)) + require.Equal(t, tt.name, state.String()) + } +} diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index afe014073a5f..601c81675c63 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" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -26,7 +27,7 @@ func (k Keeper) ConnOpenInit( } // connection defines chain A's ConnectionEnd - connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.GetCompatibleVersions()) + connection := types.NewConnectionEnd(exported.INIT, clientID, counterparty, types.GetCompatibleVersions()) k.SetConnection(ctx, connectionID, connection) err := k.addConnectionToClient(ctx, clientID, connectionID) @@ -69,14 +70,14 @@ func (k Keeper) ConnOpenTry( // NOTE: chain A's counterparty is chain B (i.e where this code is executed) prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(clientID, connectionID, prefix) - expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientID, expectedCounterparty, counterpartyVersions) + expectedConnection := types.NewConnectionEnd(exported.INIT, counterparty.ClientID, expectedCounterparty, counterpartyVersions) // chain B picks a version from Chain A's available versions that is compatible // with the supported IBC versions version := types.PickVersion(counterpartyVersions, types.GetCompatibleVersions()) // connection defines chain B's ConnectionEnd - connection := types.NewConnectionEnd(types.UNINITIALIZED, clientID, counterparty, []string{version}) + connection := types.NewConnectionEnd(exported.UNINITIALIZED, clientID, counterparty, []string{version}) err := k.VerifyConnectionState( ctx, proofHeight, proofInit, counterparty.ConnectionID, @@ -94,13 +95,12 @@ func (k Keeper) ConnOpenTry( // return err // } - _, found = k.GetConnection(ctx, connectionID) if found { return sdkerrors.Wrap(types.ErrConnectionExists, "cannot relay connection attempt") } - connection.State = types.TRYOPEN + connection.State = exported.TRYOPEN err = k.addConnectionToClient(ctx, clientID, connectionID) if err != nil { return sdkerrors.Wrap(err, "cannot relay connection attempt") @@ -134,7 +134,7 @@ func (k Keeper) ConnOpenAck( return sdkerrors.Wrap(types.ErrConnectionNotFound, "cannot relay ACK of open attempt") } - if connection.State != types.INIT { + if connection.State != exported.INIT { return sdkerrors.Wrapf( types.ErrInvalidConnectionState, "connection state is not INIT (got %s)", connection.State.String(), @@ -155,7 +155,7 @@ func (k Keeper) ConnOpenAck( prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) - expectedConnection := types.NewConnectionEnd(types.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version}) + expectedConnection := types.NewConnectionEnd(exported.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version}) err := k.VerifyConnectionState( ctx, proofHeight, proofTry, connection.Counterparty.ConnectionID, @@ -172,8 +172,8 @@ func (k Keeper) ConnOpenAck( // if err != nil { // return err // } - - connection.State = types.OPEN + + connection.State = exported.OPEN connection.Versions = []string{version} k.SetConnection(ctx, connectionID, connection) k.Logger(ctx).Info(fmt.Sprintf("connection %s state updated: INIT -> OPEN ", connectionID)) @@ -195,7 +195,7 @@ func (k Keeper) ConnOpenConfirm( return sdkerrors.Wrap(types.ErrConnectionNotFound, "cannot relay ACK of open attempt") } - if connection.State != types.TRYOPEN { + if connection.State != exported.TRYOPEN { return sdkerrors.Wrapf( types.ErrInvalidConnectionState, "connection state is not TRYOPEN (got %s)", connection.State.String(), @@ -209,7 +209,7 @@ func (k Keeper) ConnOpenConfirm( prefix := k.GetCommitmentPrefix() expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) - expectedConnection := types.NewConnectionEnd(types.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions) + expectedConnection := types.NewConnectionEnd(exported.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions) err := k.VerifyConnectionState( ctx, proofHeight, proofAck, connection.Counterparty.ConnectionID, @@ -219,7 +219,7 @@ func (k Keeper) ConnOpenConfirm( return err } - connection.State = types.OPEN + connection.State = exported.OPEN k.SetConnection(ctx, connectionID, connection) k.Logger(ctx).Info(fmt.Sprintf("connection %s state updated: TRYOPEN -> OPEN ", connectionID)) return nil diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index 86a21be54e16..40a48249d536 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -5,10 +5,10 @@ import ( abci "github.com/tendermint/tendermint/abci/types" - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) func (suite *KeeperTestSuite) TestConnOpenInit() { @@ -52,8 +52,8 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { suite.createClient(testClientID1) suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.INIT) - connectionKey := connection.KeyConnection(testConnectionID2) - consensusKey := client.KeyConsensusState(testClientID2) + connectionKey := ibctypes.KeyConnection(testConnectionID2) + consensusKey := ibctypes.KeyConsensusState(testClientID2) invalidProof := func() error { proofInit, proofHeight := suite.queryProof(connectionKey) @@ -119,8 +119,8 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { suite.createClient(testClientID1) suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, connection.TRYOPEN) - connectionKey := connection.KeyConnection(testConnectionID1) - consensusKey := client.KeyConsensusState(testClientID1) + connectionKey := ibctypes.KeyConnection(testConnectionID1) + consensusKey := ibctypes.KeyConsensusState(testClientID1) connectionNotFound := func() error { //suite.updateClient(testClientID2) @@ -196,7 +196,7 @@ func (suite *KeeperTestSuite) TestConnOpenConfirm() { suite.createClient(testClientID1) suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.OPEN) - connKey := connection.KeyConnection(testConnectionID2) + connKey := ibctypes.KeyConnection(testConnectionID2) proof, h := suite.queryProof(connKey) connectionNotFound := func() error { @@ -267,10 +267,8 @@ func (suite *KeeperTestSuite) createClient(clientID string) { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) consensusState := tendermint.ConsensusState{ - ChainID: chainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, + Root: commitment.NewRoot(commitID.Hash), + ValidatorSetHash: suite.valSet.Hash(), } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) @@ -286,13 +284,10 @@ func (suite *KeeperTestSuite) updateClient(clientID string) { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) state := tendermint.ConsensusState{ - ChainID: chainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), + Root: commitment.NewRoot(commitID.Hash), } suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, state) - suite.app.IBCKeeper.ClientKeeper.SetVerifiedRoot(suite.ctx, clientID, state.GetHeight(), state.GetRoot()) } func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state connection.State) { diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index 8b3fb7f78cce..cacafd4a35c2 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -63,7 +64,7 @@ func (suite *KeeperTestSuite) TestSetAndGetConnection() { counterparty := types.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) expConn := types.ConnectionEnd{ - State: types.INIT, + State: exported.INIT, ClientID: testClientID1, Counterparty: counterparty, Versions: types.GetCompatibleVersions(), @@ -91,21 +92,21 @@ func (suite KeeperTestSuite) TestGetAllConnections() { counterparty3 := types.NewCounterparty(testClientID3, testConnectionID3, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) conn1 := types.ConnectionEnd{ - State: types.INIT, + State: exported.INIT, ClientID: testClientID1, Counterparty: counterparty3, Versions: types.GetCompatibleVersions(), } conn2 := types.ConnectionEnd{ - State: types.INIT, + State: exported.INIT, ClientID: testClientID2, Counterparty: counterparty1, Versions: types.GetCompatibleVersions(), } conn3 := types.ConnectionEnd{ - State: types.UNINITIALIZED, + State: exported.UNINITIALIZED, ClientID: testClientID3, Counterparty: counterparty2, Versions: types.GetCompatibleVersions(), diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 64dbd18d6840..013596143321 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -5,7 +5,7 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -54,20 +54,19 @@ func (k Keeper) VerifyChannelState( ctx sdk.Context, connection types.ConnectionEnd, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, - channel channeltypes.Channel, + channel channelexported.ChannelI, consensusState clientexported.ConsensusState, ) error { - clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return clienterrors.ErrClientNotFound } return clientState.VerifyChannelState( - k.cdc, height, prefix, proof, portID, channelID, channel, consensusState, + k.cdc, height, connection.Counterparty.Prefix, proof, portID, channelID, channel, consensusState, ) } @@ -91,7 +90,7 @@ func (k Keeper) VerifyPacketCommitment( } return clientState.VerifyPacketCommitment( - height, prefix, proof, portID, channelID, sequence, commitmentBytes, consensusState, + height, prefix, proof, portID, channelID, sequence, commitmentBytes, consensusState, ) } diff --git a/x/ibc/03-connection/types/codec.go b/x/ibc/03-connection/types/codec.go index 797b0fc69fb7..263ec4c3f4ae 100644 --- a/x/ibc/03-connection/types/codec.go +++ b/x/ibc/03-connection/types/codec.go @@ -12,7 +12,6 @@ var SubModuleCdc *codec.Codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.ConnectionI)(nil), nil) cdc.RegisterInterface((*exported.CounterpartyI)(nil), nil) - cdc.RegisterInterface((*exported.StateI)(nil), nil) cdc.RegisterConcrete(ConnectionEnd{}, "ibc/connection/ConnectionEnd", nil) cdc.RegisterConcrete(MsgConnectionOpenInit{}, "ibc/connection/MsgConnectionOpenInit", nil) diff --git a/x/ibc/03-connection/types/connection.go b/x/ibc/03-connection/types/connection.go index e9d19166b728..880f43f45bae 100644 --- a/x/ibc/03-connection/types/connection.go +++ b/x/ibc/03-connection/types/connection.go @@ -1,10 +1,8 @@ package types import ( - "encoding/json" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - exported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) @@ -18,8 +16,8 @@ var _ exported.ConnectionI = ConnectionEnd{} // NOTE: there must only be 2 defined ConnectionEnds to stablish a connection // between two chains. type ConnectionEnd struct { - State State `json:"state" yaml:"state"` - ClientID string `json:"client_id" yaml:"client_id"` + State exported.State `json:"state" yaml:"state"` + ClientID string `json:"client_id" yaml:"client_id"` // Counterparty chain associated with this connection. Counterparty Counterparty `json:"counterparty" yaml:"counterparty"` @@ -29,7 +27,7 @@ type ConnectionEnd struct { } // NewConnectionEnd creates a new ConnectionEnd instance. -func NewConnectionEnd(state State, clientID string, counterparty Counterparty, versions []string) ConnectionEnd { +func NewConnectionEnd(state exported.State, clientID string, counterparty Counterparty, versions []string) ConnectionEnd { return ConnectionEnd{ State: state, ClientID: clientID, @@ -39,8 +37,8 @@ func NewConnectionEnd(state State, clientID string, counterparty Counterparty, v } // GetState implements the Connection interface -func (c ConnectionEnd) GetState() exported.StateI { - return &c.State +func (c ConnectionEnd) GetState() exported.State { + return c.State } // GetClientID implements the Connection interface @@ -60,7 +58,7 @@ func (c ConnectionEnd) GetVersions() []string { // ValidateBasic implements the Connection interface func (c ConnectionEnd) ValidateBasic() error { - // TODO: + // TODO: move validations from msg.ValidateBasic() return c.Counterparty.ValidateBasic() } @@ -92,6 +90,11 @@ func (c Counterparty) GetConnectionID() string { return c.ConnectionID } +// GetPrefix implements the CounterpartyI interface +func (c Counterparty) GetPrefix() commitment.PrefixI { + return c.Prefix +} + // ValidateBasic performs a basic validation check of the identifiers and prefix func (c Counterparty) ValidateBasic() error { if err := host.DefaultConnectionIdentifierValidator(c.ConnectionID); err != nil { @@ -115,71 +118,3 @@ func (c Counterparty) ValidateBasic() error { } return nil } - -var s = State(0) -var _ exported.StateI = &s - -// State defines the state of a connection between two disctinct -// chains -type State byte - -// available connection states -const ( - UNINITIALIZED State = iota // default State - INIT - TRYOPEN - OPEN -) - -// string representation of the connection states -const ( - StateUninitialized string = "UNINITIALIZED" - StateInit string = "INIT" - StateTryOpen string = "TRYOPEN" - StateOpen string = "OPEN" -) - -// String implements the Stringer interface -func (s State) String() string { - switch s { - case INIT: - return StateInit - case TRYOPEN: - return StateTryOpen - case OPEN: - return StateOpen - default: - return StateUninitialized - } -} - -// StateFromString parses a string into a connection state -func StateFromString(state string) State { - switch state { - case StateInit: - return INIT - case StateTryOpen: - return TRYOPEN - case StateOpen: - return OPEN - default: - return UNINITIALIZED - } -} - -// MarshalJSON marshal to JSON using string. -func (s State) MarshalJSON() ([]byte, error) { - return json.Marshal(s.String()) -} - -// UnmarshalJSON decodes from JSON assuming Bech32 encoding. -func (s *State) UnmarshalJSON(data []byte) error { - var str string - err := json.Unmarshal(data, &str) - if err != nil { - return err - } - - *s = StateFromString(str) - return nil -} diff --git a/x/ibc/03-connection/types/connection_test.go b/x/ibc/03-connection/types/connection_test.go index 7202b6f72a0b..079ad171875a 100644 --- a/x/ibc/03-connection/types/connection_test.go +++ b/x/ibc/03-connection/types/connection_test.go @@ -1,46 +1,3 @@ package types -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestConnectionStateString(t *testing.T) { - cases := []struct { - name string - state State - }{ - {StateUninitialized, UNINITIALIZED}, - {StateInit, INIT}, - {StateTryOpen, TRYOPEN}, - {StateOpen, OPEN}, - } - - for _, tt := range cases { - tt := tt - require.Equal(t, tt.state, StateFromString(tt.name)) - require.Equal(t, tt.name, tt.state.String()) - } -} - -func TestConnectionlStateMarshalJSON(t *testing.T) { - cases := []struct { - name string - state State - }{ - {StateUninitialized, UNINITIALIZED}, - {StateInit, INIT}, - {StateTryOpen, TRYOPEN}, - {StateOpen, OPEN}, - } - - for _, tt := range cases { - tt := tt - bz, err := tt.state.MarshalJSON() - require.NoError(t, err) - var state State - require.NoError(t, state.UnmarshalJSON(bz)) - require.Equal(t, tt.name, state.String()) - } -} +// TODO: connection and counterpaty validations diff --git a/x/ibc/04-channel/client/cli/tx.go b/x/ibc/04-channel/client/cli/tx.go index df0c53cddf82..0255947e50c5 100644 --- a/x/ibc/04-channel/client/cli/tx.go +++ b/x/ibc/04-channel/client/cli/tx.go @@ -15,6 +15,7 @@ import ( authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils" connectionutils "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/client/utils" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" ) // IBC Channel flags @@ -242,9 +243,9 @@ func GetMsgChannelCloseConfirmCmd(storeKey string, cdc *codec.Codec) *cobra.Comm } } -func channelOrder() types.Order { +func channelOrder() exported.Order { if viper.GetBool(FlagOrdered) { - return types.ORDERED + return exported.ORDERED } - return types.UNORDERED + return exported.UNORDERED } diff --git a/x/ibc/04-channel/client/rest/rest.go b/x/ibc/04-channel/client/rest/rest.go index 4c869297d6e7..7ee17790ea88 100644 --- a/x/ibc/04-channel/client/rest/rest.go +++ b/x/ibc/04-channel/client/rest/rest.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -22,14 +23,14 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) // ChannelOpenInitReq defines the properties of a channel open init request's body. type ChannelOpenInitReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - PortID string `json:"port_id" yaml:"port_id"` - ChannelID string `json:"channel_id" yaml:"channel_id"` - Version string `json:"version" yaml:"version"` - ChannelOrder types.Order `json:"channel_order" yaml:"channel_order"` - ConnectionHops []string `json:"connection_hops" yaml:"connection_hops"` - CounterpartyPortID string `json:"counterparty_port_id" yaml:"counterparty_port_id"` - CounterpartyChannelID string `json:"counterparty_channel_id" yaml:"counterparty_channel_id"` + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + PortID string `json:"port_id" yaml:"port_id"` + ChannelID string `json:"channel_id" yaml:"channel_id"` + Version string `json:"version" yaml:"version"` + ChannelOrder exported.Order `json:"channel_order" yaml:"channel_order"` + ConnectionHops []string `json:"connection_hops" yaml:"connection_hops"` + CounterpartyPortID string `json:"counterparty_port_id" yaml:"counterparty_port_id"` + CounterpartyChannelID string `json:"counterparty_channel_id" yaml:"counterparty_channel_id"` } // ChannelOpenTryReq defines the properties of a channel open try request's body. @@ -38,7 +39,7 @@ type ChannelOpenTryReq struct { PortID string `json:"port_id" yaml:"port_id"` ChannelID string `json:"channel_id" yaml:"channel_id"` Version string `json:"version" yaml:"version"` - ChannelOrder types.Order `json:"channel_order" yaml:"channel_order"` + ChannelOrder exported.Order `json:"channel_order" yaml:"channel_order"` ConnectionHops []string `json:"connection_hops" yaml:"connection_hops"` CounterpartyPortID string `json:"counterparty_port_id" yaml:"counterparty_port_id"` CounterpartyChannelID string `json:"counterparty_channel_id" yaml:"counterparty_channel_id"` diff --git a/x/ibc/04-channel/exported/exported.go b/x/ibc/04-channel/exported/exported.go index 75608bd532a7..68976a5b79ad 100644 --- a/x/ibc/04-channel/exported/exported.go +++ b/x/ibc/04-channel/exported/exported.go @@ -1,5 +1,25 @@ package exported +import ( + "encoding/json" + "fmt" +) + +type ChannelI interface { + GetState() State + GetOrdering() Order + GetCounterparty() CounterpartyI + GetConnectionHops() + GetVersion() string + ValidateBasic() error +} + +type CounterpartyI interface { + GetPortID() string + GetChannelID() string + ValidateBasic() error +} + // PacketI defines the standard interface for IBC packets type PacketI interface { GetSequence() uint64 @@ -22,3 +42,137 @@ type PacketDataI interface { ValidateBasic() error // ValidateBasic validates basic properties of the packet data, implements sdk.Msg Type() string // Type returns human readable identifier, implements sdk.Msg } + +// Order defines if a channel is ORDERED or UNORDERED +type Order byte + +// string representation of the channel ordering +const ( + NONE Order = iota // zero-value for channel ordering + UNORDERED // packets can be delivered in any order, which may differ from the order in which they were sent. + ORDERED // packets are delivered exactly in the order which they were sent +) + +// channel order types +const ( + OrderNone string = "" + OrderUnordered string = "UNORDERED" + OrderOrdered string = "ORDERED" +) + +// String implements the Stringer interface +func (o Order) String() string { + switch o { + case UNORDERED: + return OrderUnordered + case ORDERED: + return OrderOrdered + default: + return OrderNone + } +} + +// MarshalJSON marshal to JSON using string. +func (o Order) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + +// UnmarshalJSON decodes from JSON. +func (o *Order) UnmarshalJSON(data []byte) error { + var s string + err := json.Unmarshal(data, &s) + if err != nil { + return err + } + + order := OrderFromString(s) + if order == 0 { + return fmt.Errorf("invalid order '%s'", s) + } + + *o = order + return nil +} + +// OrderFromString parses a string into a channel order byte +func OrderFromString(order string) Order { + switch order { + case OrderUnordered: + return UNORDERED + case OrderOrdered: + return ORDERED + default: + return NONE + } +} + +// State defines if a channel is in one of the following states: +// CLOSED, INIT, OPENTRY or OPEN +type State byte + +// channel state types +const ( + UNINITIALIZED State = iota // Default State + INIT // A channel end has just started the opening handshake. + TRYOPEN // A channel end has acknowledged the handshake step on the counterparty chain. + OPEN // A channel end has completed the handshake and is ready to send and receive packets. + CLOSED // A channel end has been closed and can no longer be used to send or receive packets. +) + +// string representation of the channel states +const ( + StateUninitialized string = "UNINITIALIZED" + StateInit string = "INIT" + StateTryOpen string = "TRYOPEN" + StateOpen string = "OPEN" + StateClosed string = "CLOSED" +) + +// String implements the Stringer interface +func (s State) String() string { + switch s { + case INIT: + return StateInit + case TRYOPEN: + return StateTryOpen + case OPEN: + return StateOpen + case CLOSED: + return StateClosed + default: + return StateUninitialized + } +} + +// MarshalJSON marshal to JSON using string. +func (s State) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// UnmarshalJSON decodes from JSON. +func (s *State) UnmarshalJSON(data []byte) error { + var stateStr string + err := json.Unmarshal(data, &stateStr) + if err != nil { + return err + } + + *s = StateFromString(stateStr) + return nil +} + +// StateFromString parses a string into a channel state byte +func StateFromString(state string) State { + switch state { + case StateClosed: + return CLOSED + case StateInit: + return INIT + case StateTryOpen: + return TRYOPEN + case StateOpen: + return OPEN + default: + return UNINITIALIZED + } +} diff --git a/x/ibc/04-channel/exported/exported_test.go b/x/ibc/04-channel/exported/exported_test.go new file mode 100644 index 000000000000..e3a64e1fc09f --- /dev/null +++ b/x/ibc/04-channel/exported/exported_test.go @@ -0,0 +1,92 @@ +package exported + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + + +func TestChannelStateString(t *testing.T) { + cases := []struct { + name string + state State + }{ + {StateUninitialized, UNINITIALIZED}, + {StateInit, INIT}, + {StateTryOpen, TRYOPEN}, + {StateOpen, OPEN}, + {StateClosed, CLOSED}, + } + + for _, tt := range cases { + tt := tt + require.Equal(t, tt.state, StateFromString(tt.name)) + require.Equal(t, tt.name, tt.state.String()) + } +} + +func TestChannelStateMarshalJSON(t *testing.T) { + cases := []struct { + name string + state State + }{ + {StateUninitialized, UNINITIALIZED}, + {StateInit, INIT}, + {StateTryOpen, TRYOPEN}, + {StateOpen, OPEN}, + {StateClosed, CLOSED}, + } + + for _, tt := range cases { + tt := tt + bz, err := tt.state.MarshalJSON() + require.NoError(t, err) + var state State + require.NoError(t, state.UnmarshalJSON(bz)) + require.Equal(t, tt.name, state.String()) + } +} + +func TestOrderString(t *testing.T) { + cases := []struct { + name string + order Order + }{ + {OrderNone, NONE}, + {OrderUnordered, UNORDERED}, + {OrderOrdered, ORDERED}, + } + + for _, tt := range cases { + tt := tt + require.Equal(t, tt.order, OrderFromString(tt.name)) + require.Equal(t, tt.name, tt.order.String()) + } +} + +func TestOrderMarshalJSON(t *testing.T) { + cases := []struct { + msg string + name string + order Order + expectPass bool + }{ + {"none ordering should have failed", OrderNone, NONE, false}, + {"unordered should have passed", OrderUnordered, UNORDERED, true}, + {"ordered should have passed", OrderOrdered, ORDERED, true}, + } + + for _, tt := range cases { + tt := tt + bz, err := tt.order.MarshalJSON() + require.NoError(t, err) + var order Order + if tt.expectPass { + require.NoError(t, order.UnmarshalJSON(bz), tt.msg) + require.Equal(t, tt.name, order.String(), tt.msg) + } else { + require.Error(t, order.UnmarshalJSON(bz), tt.msg) + } + } +} \ No newline at end of file diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index c09dac9344f7..5348f175d022 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -1,13 +1,14 @@ package keeper import ( - "errors" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -20,7 +21,7 @@ func (k Keeper) CounterpartyHops(ctx sdk.Context, ch types.Channel) ([]string, b if !found { return []string{}, false } - counterPartyHops[len(counterPartyHops)-1-i] = connection.Counterparty.ConnectionID + counterPartyHops[len(counterPartyHops)-1-i] = connection.GetCounterparty().GetConnectionID() } return counterPartyHops, true } @@ -29,14 +30,15 @@ func (k Keeper) CounterpartyHops(ctx sdk.Context, ch types.Channel) ([]string, b // a module on another chain. func (k Keeper) ChanOpenInit( ctx sdk.Context, - order types.Order, + order exported.Order, connectionHops []string, portID, channelID string, counterparty types.Counterparty, version string, ) error { - // TODO: abortTransactionUnless(validateChannelIdentifier(portIdentifier, channelIdentifier)) + // channel identifier and connection hop length checked on msg.ValidateBasic() + _, found := k.GetChannel(ctx, portID, channelID) if found { return sdkerrors.Wrap(types.ErrChannelExists, channelID) @@ -47,26 +49,19 @@ func (k Keeper) ChanOpenInit( return sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0]) } - if connectionEnd.State == connection.UNINITIALIZED { + if connectionEnd.GetState() == connectionexported.UNINITIALIZED { return sdkerrors.Wrap( connection.ErrInvalidConnectionState, "connection state cannot be UNINITIALIZED", ) } - /* - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) - - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } - - */ - channel := types.NewChannel(types.INIT, order, counterparty, connectionHops, version) + channel := types.NewChannel(exported.INIT, order, counterparty, connectionHops, version) k.SetChannel(ctx, portID, channelID, channel) // TODO: generate channel capability key and set it to store + // key := "" + // k.SetChannelCapability(ctx, portID, channelID, key) k.SetNextSequenceSend(ctx, portID, channelID, 1) k.SetNextSequenceRecv(ctx, portID, channelID, 1) @@ -77,7 +72,7 @@ func (k Keeper) ChanOpenInit( // handshake initiated by a module on another chain. func (k Keeper) ChanOpenTry( ctx sdk.Context, - order types.Order, + order exported.Order, connectionHops []string, portID, channelID string, @@ -87,37 +82,45 @@ func (k Keeper) ChanOpenTry( proofInit commitment.ProofI, proofHeight uint64, ) error { - _, found := k.GetChannel(ctx, portID, channelID) + // channel identifier and connection hop length checked on msg.ValidateBasic() + + previousChannel, found := k.GetChannel(ctx, portID, channelID) if found { return sdkerrors.Wrap(types.ErrChannelExists, channelID) + } else if !(previousChannel.State == exported.INIT && + previousChannel.Ordering == order && + previousChannel.Counterparty.PortID == counterparty.PortID && + previousChannel.Counterparty.ChannelID == counterparty.ChannelID && + previousChannel.ConnectionHops[0] == connectionHops[0] && + previousChannel.Version == version) { + sdkerrors.Wrap(types.ErrInvalidChannel, "cannot relay connection attempt") } - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) - - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } + // TODO: use key authentication + // key := sdk.NewKVStoreKey(portID) + // if !k.portKeeper.Authenticate(key, portID) { + // return sdkerrors.Wrap(port.ErrInvalidPort, portID) + // } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) if !found { return sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } // NOTE: this step has been switched with the one below to reverse the connection // hops - channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version) + channel := types.NewChannel(exported.TRYOPEN, order, counterparty, connectionHops, version) counterpartyHops, found := k.CounterpartyHops(ctx, channel) if !found { - // Should not reach here, connectionEnd was able to be retrieved above + // should not reach here, connectionEnd was able to be retrieved above panic("cannot find connection") } @@ -125,26 +128,28 @@ func (k Keeper) ChanOpenTry( // (i.e self) expectedCounterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( - types.INIT, channel.Ordering, expectedCounterparty, + exported.INIT, channel.Ordering, expectedCounterparty, counterpartyHops, channel.Version, ) - bz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedChannel) - if err != nil { - return err + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return clienterrors.ErrConsensusStateNotFound } - if !k.connectionKeeper.VerifyMembership( + err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofInit, - types.ChannelPath(counterparty.PortID, counterparty.ChannelID), - bz, - ) { - return sdkerrors.Wrap(types.ErrInvalidCounterparty, "channel membership verification failed") + counterparty.PortID, counterparty.ChannelID, expectedChannel, consensusState, + ) + if err != nil { + return sdkerrors.Wrap(err, "channel membership verification failed") } k.SetChannel(ctx, portID, channelID, channel) // TODO: generate channel capability key and set it to store + // key := "" + // k.SetChannelCapability(ctx, portID, channelID, key) k.SetNextSequenceSend(ctx, portID, channelID, 1) k.SetNextSequenceRecv(ctx, portID, channelID, 1) @@ -166,61 +171,60 @@ func (k Keeper) ChanOpenAck( return sdkerrors.Wrap(types.ErrChannelNotFound, channelID) } - if channel.State != types.INIT { + if channel.State != exported.INIT || channel.State != exported.TRYOPEN { return sdkerrors.Wrapf( types.ErrInvalidChannelState, - "channel state is not INIT (got %s)", channel.State.String(), + "channel state should be INIT or TRYOPEN (got %s)", channel.State.String(), ) } - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) - - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } + // TODO: use key authentication + // key := sdk.NewKVStoreKey(portID) + // if !k.portKeeper.Authenticate(key, portID) { + // return sdkerrors.Wrap(port.ErrInvalidPort, portID) + // } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } counterpartyHops, found := k.CounterpartyHops(ctx, channel) if !found { - // Should not reach here, connectionEnd was able to be retrieved above + // should not reach here, connectionEnd was able to be retrieved above panic("cannot find connection") } // counterparty of the counterparty channel end (i.e self) counterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( - types.TRYOPEN, channel.Ordering, counterparty, + exported.TRYOPEN, channel.Ordering, counterparty, counterpartyHops, channel.Version, ) - bz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedChannel) - if err != nil { - return err + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return clienterrors.ErrConsensusStateNotFound } - if !k.connectionKeeper.VerifyMembership( + err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofTry, - types.ChannelPath(channel.Counterparty.PortID, channel.Counterparty.ChannelID), - bz, - ) { - return sdkerrors.Wrap( - types.ErrInvalidCounterparty, "channel membership verification failed", - ) + channel.Counterparty.PortID, channel.Counterparty.ChannelID, + expectedChannel, consensusState, + ) + + if err != nil { + return sdkerrors.Wrap(err, "channel membership verification failed") } - channel.State = types.OPEN + channel.State = exported.OPEN channel.Version = counterpartyVersion k.SetChannel(ctx, portID, channelID, channel) @@ -241,29 +245,33 @@ func (k Keeper) ChanOpenConfirm( return sdkerrors.Wrap(types.ErrChannelNotFound, channelID) } - if channel.State != types.TRYOPEN { + if channel.State != exported.TRYOPEN { return sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPENTRY (got %s)", channel.State.String(), ) } - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) + // TODO: use key authentication + // capkey, found := k.GetChannelCapability(ctx, portID, channelID) + // if !found { + // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) + // } - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } + // key := sdk.NewKVStoreKey(capkey) + // if !k.portKeeper.Authenticate(key, portID) { + // return sdkerrors.Wrap(port.ErrInvalidPort, portID) + // } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } @@ -275,24 +283,26 @@ func (k Keeper) ChanOpenConfirm( counterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( - types.OPEN, channel.Ordering, counterparty, + exported.OPEN, channel.Ordering, counterparty, counterpartyHops, channel.Version, ) - bz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedChannel) - if err != nil { - return err + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return clienterrors.ErrConsensusStateNotFound } - if !k.connectionKeeper.VerifyMembership( + err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofAck, - types.ChannelPath(channel.Counterparty.PortID, channel.Counterparty.ChannelID), - bz, - ) { - return sdkerrors.Wrap(types.ErrInvalidCounterparty, "channel membership verification failed") + channel.Counterparty.PortID, channel.Counterparty.ChannelID, + expectedChannel, consensusState, + ) + + if err != nil { + return sdkerrors.Wrap(err, "channel membership verification failed") } - channel.State = types.OPEN + channel.State = exported.OPEN k.SetChannel(ctx, portID, channelID, channel) return nil @@ -310,19 +320,23 @@ func (k Keeper) ChanCloseInit( portID, channelID string, ) error { - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) + // TODO: use key authentication + // capkey, found := k.GetChannelCapability(ctx, portID, channelID) + // if !found { + // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) + // } - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } + // key := sdk.NewKVStoreKey(capkey) + // if !k.portKeeper.Authenticate(key, portID) { + // return sdkerrors.Wrap(port.ErrInvalidPort, portID) + // } channel, found := k.GetChannel(ctx, portID, channelID) if !found { return sdkerrors.Wrap(types.ErrChannelNotFound, channelID) } - if channel.State == types.CLOSED { + if channel.State == exported.CLOSED { return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") } @@ -331,14 +345,14 @@ func (k Keeper) ChanCloseInit( return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } - channel.State = types.CLOSED + channel.State = exported.CLOSED k.SetChannel(ctx, portID, channelID, channel) return nil @@ -353,19 +367,23 @@ func (k Keeper) ChanCloseConfirm( proofInit commitment.ProofI, proofHeight uint64, ) error { - // TODO: Maybe not right - key := sdk.NewKVStoreKey(portID) + // TODO: use key authentication + // capkey, found := k.GetChannelCapability(ctx, portID, channelID) + // if !found { + // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) + // } - if !k.portKeeper.Authenticate(key, portID) { - return sdkerrors.Wrap(port.ErrInvalidPort, portID) - } + // key := sdk.NewKVStoreKey(capkey) + // if !k.portKeeper.Authenticate(key, portID) { + // return sdkerrors.Wrap(port.ErrInvalidPort, portID) + // } channel, found := k.GetChannel(ctx, portID, channelID) if !found { return sdkerrors.Wrap(types.ErrChannelNotFound, channelID) } - if channel.State == types.CLOSED { + if channel.State == exported.CLOSED { return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") } @@ -374,10 +392,10 @@ func (k Keeper) ChanCloseConfirm( return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } @@ -389,24 +407,26 @@ func (k Keeper) ChanCloseConfirm( counterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( - types.CLOSED, channel.Ordering, counterparty, + exported.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, ) - bz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedChannel) - if err != nil { - return errors.New("failed to marshal expected channel") + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return clienterrors.ErrConsensusStateNotFound } - if !k.connectionKeeper.VerifyMembership( + err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofInit, - types.ChannelPath(channel.Counterparty.PortID, channel.Counterparty.ChannelID), - bz, - ) { - return sdkerrors.Wrap(types.ErrInvalidCounterparty, "channel membership verification failed") + channel.Counterparty.PortID, channel.Counterparty.ChannelID, + expectedChannel, consensusState, + ) + + if err != nil { + return sdkerrors.Wrap(err, "channel membership verification failed") } - channel.State = types.CLOSED + channel.State = exported.CLOSED k.SetChannel(ctx, portID, channelID, channel) return nil diff --git a/x/ibc/04-channel/keeper/keeper.go b/x/ibc/04-channel/keeper/keeper.go index 92b1a3bca1f1..ab756c044971 100644 --- a/x/ibc/04-channel/keeper/keeper.go +++ b/x/ibc/04-channel/keeper/keeper.go @@ -44,7 +44,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { // GetChannel returns a channel with a particular identifier binded to a specific port func (k Keeper) GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyChannel(portID, channelID)) + bz := store.Get(ibctypes.KeyChannel(portID, channelID)) if bz == nil { return types.Channel{}, false } @@ -58,13 +58,13 @@ func (k Keeper) GetChannel(ctx sdk.Context, portID, channelID string) (types.Cha func (k Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel types.Channel) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(channel) - store.Set(types.KeyChannel(portID, channelID), bz) + store.Set(ibctypes.KeyChannel(portID, channelID), bz) } // GetChannelCapability gets a channel's capability key from the store func (k Keeper) GetChannelCapability(ctx sdk.Context, portID, channelID string) (string, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyChannelCapabilityPath(portID, channelID)) + bz := store.Get(ibctypes.KeyChannelCapabilityPath(portID, channelID)) if bz == nil { return "", false } @@ -75,13 +75,13 @@ func (k Keeper) GetChannelCapability(ctx sdk.Context, portID, channelID string) // SetChannelCapability sets a channel's capability key to the store func (k Keeper) SetChannelCapability(ctx sdk.Context, portID, channelID string, key string) { store := ctx.KVStore(k.storeKey) - store.Set(types.KeyChannelCapabilityPath(portID, channelID), []byte(key)) + store.Set(ibctypes.KeyChannelCapabilityPath(portID, channelID), []byte(key)) } // GetNextSequenceSend gets a channel's next send sequence from the store func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyNextSequenceSend(portID, channelID)) + bz := store.Get(ibctypes.KeyNextSequenceSend(portID, channelID)) if bz == nil { return 0, false } @@ -93,13 +93,13 @@ func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) ( func (k Keeper) SetNextSequenceSend(ctx sdk.Context, portID, channelID string, sequence uint64) { store := ctx.KVStore(k.storeKey) bz := sdk.Uint64ToBigEndian(sequence) - store.Set(types.KeyNextSequenceSend(portID, channelID), bz) + store.Set(ibctypes.KeyNextSequenceSend(portID, channelID), bz) } // GetNextSequenceRecv gets a channel's next receive sequence from the store func (k Keeper) GetNextSequenceRecv(ctx sdk.Context, portID, channelID string) (uint64, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyNextSequenceRecv(portID, channelID)) + bz := store.Get(ibctypes.KeyNextSequenceRecv(portID, channelID)) if bz == nil { return 0, false } @@ -111,37 +111,37 @@ func (k Keeper) GetNextSequenceRecv(ctx sdk.Context, portID, channelID string) ( func (k Keeper) SetNextSequenceRecv(ctx sdk.Context, portID, channelID string, sequence uint64) { store := ctx.KVStore(k.storeKey) bz := sdk.Uint64ToBigEndian(sequence) - store.Set(types.KeyNextSequenceRecv(portID, channelID), bz) + store.Set(ibctypes.KeyNextSequenceRecv(portID, channelID), bz) } // GetPacketCommitment gets the packet commitment hash from the store func (k Keeper) GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyPacketCommitment(portID, channelID, sequence)) + bz := store.Get(ibctypes.KeyPacketCommitment(portID, channelID, sequence)) return bz } // SetPacketCommitment sets the packet commitment hash to the store func (k Keeper) SetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64, commitmentHash []byte) { store := ctx.KVStore(k.storeKey) - store.Set(types.KeyPacketCommitment(portID, channelID, sequence), commitmentHash) + store.Set(ibctypes.KeyPacketCommitment(portID, channelID, sequence), commitmentHash) } func (k Keeper) deletePacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) { store := ctx.KVStore(k.storeKey) - store.Delete(types.KeyPacketCommitment(portID, channelID, sequence)) + store.Delete(ibctypes.KeyPacketCommitment(portID, channelID, sequence)) } // SetPacketAcknowledgement sets the packet ack hash to the store func (k Keeper) SetPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64, ackHash []byte) { store := ctx.KVStore(k.storeKey) - store.Set(types.KeyPacketAcknowledgement(portID, channelID, sequence), ackHash) + store.Set(ibctypes.KeyPacketAcknowledgement(portID, channelID, sequence), ackHash) } // GetPacketAcknowledgement gets the packet ack hash from the store func (k Keeper) GetPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) ([]byte, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyPacketAcknowledgement(portID, channelID, sequence)) + bz := store.Get(ibctypes.KeyPacketAcknowledgement(portID, channelID, sequence)) if bz == nil { return nil, false } @@ -153,7 +153,7 @@ func (k Keeper) GetPacketAcknowledgement(ctx sdk.Context, portID, channelID stri // and stop. func (k Keeper) IterateChannels(ctx sdk.Context, cb func(types.Channel) bool) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.GetChannelKeysPrefix(ibctypes.KeyChannelPrefix)) + iterator := sdk.KVStorePrefixIterator(store, ibctypes.GetChannelPortsKeysPrefix(ibctypes.KeyChannelPrefix)) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index a2db8d3f66a2..4e1b7b79870b 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -8,10 +8,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // CleanupPacket is called by a module to remove a received packet commitment @@ -38,7 +40,7 @@ func (k Keeper) CleanupPacket( return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) } - if channel.State != types.OPEN { + if channel.State != exported.OPEN { return nil, sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), @@ -83,16 +85,16 @@ func (k Keeper) CleanupPacket( var ok bool switch channel.Ordering { - case types.ORDERED: + case exported.ORDERED: ok = k.connectionKeeper.VerifyMembership( ctx, connectionEnd, proofHeight, proof, - types.NextSequenceRecvPath(packet.GetDestPort(), packet.GetDestChannel()), + ibctypes.NextSequenceRecvPath(packet.GetDestPort(), packet.GetDestChannel()), sdk.Uint64ToBigEndian(nextSequenceRecv), ) - case types.UNORDERED: + case exported.UNORDERED: ok = k.connectionKeeper.VerifyMembership( ctx, connectionEnd, proofHeight, proof, - types.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), + ibctypes.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), acknowledgement, ) default: @@ -124,7 +126,7 @@ func (k Keeper) SendPacket( return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) } - if channel.State == types.CLOSED { + if channel.State == exported.CLOSED { return sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel is CLOSED (got %s)", channel.State.String(), @@ -154,14 +156,14 @@ func (k Keeper) SendPacket( return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State == connection.UNINITIALIZED { + if connectionEnd.GetState() == connectionexported.UNINITIALIZED { return sdkerrors.Wrap( connection.ErrInvalidConnectionState, "connection is closed (i.e NONE)", ) } - _, found = k.clientKeeper.GetConsensusState(ctx, connectionEnd.ClientID) + _, found = k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) if !found { return client.ErrConsensusStateNotFound } @@ -203,7 +205,7 @@ func (k Keeper) RecvPacket( return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) } - if channel.State != types.OPEN { + if channel.State != exported.OPEN { return nil, sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), @@ -233,14 +235,14 @@ func (k Keeper) RecvPacket( return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return nil, sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } - if channel.Ordering == types.ORDERED { + if channel.Ordering == exported.ORDERED { nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { return nil, types.ErrSequenceReceiveNotFound @@ -260,7 +262,7 @@ func (k Keeper) RecvPacket( if !k.connectionKeeper.VerifyMembership( ctx, connectionEnd, proofHeight, proof, - types.PacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()), + ibctypes.PacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()), types.CommitPacket(packet.GetData()), ) { return nil, errors.New("couldn't verify counterparty packet commitment") @@ -282,21 +284,21 @@ func (k Keeper) PacketExecuted( return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetDestChannel()) } - if channel.State != types.OPEN { + if channel.State != exported.OPEN { return sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) } - if acknowledgement != nil || channel.Ordering == types.UNORDERED { + if acknowledgement != nil || channel.Ordering == exported.UNORDERED { k.SetPacketAcknowledgement( ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), types.CommitAcknowledgement(acknowledgement), ) } - if channel.Ordering == types.ORDERED { + if channel.Ordering == exported.ORDERED { nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { return types.ErrSequenceReceiveNotFound @@ -334,7 +336,7 @@ func (k Keeper) AcknowledgePacket( return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) } - if channel.State != types.OPEN { + if channel.State != exported.OPEN { return nil, sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), @@ -364,10 +366,10 @@ func (k Keeper) AcknowledgePacket( return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if connectionEnd.State != connection.OPEN { + if connectionEnd.GetState() != connectionexported.OPEN { return nil, sdkerrors.Wrapf( connection.ErrInvalidConnectionState, - "connection state is not OPEN (got %s)", connectionEnd.State.String(), + "connection state is not OPEN (got %s)", connectionEnd.GetState().String(), ) } @@ -378,7 +380,7 @@ func (k Keeper) AcknowledgePacket( if !k.connectionKeeper.VerifyMembership( ctx, connectionEnd, proofHeight, proof, - types.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), + ibctypes.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), acknowledgement.GetBytes(), ) { return nil, errors.New("invalid acknowledgement on counterparty chain") diff --git a/x/ibc/04-channel/types/channel.go b/x/ibc/04-channel/types/channel.go index ae38cb2212df..640945ef8f81 100644 --- a/x/ibc/04-channel/types/channel.go +++ b/x/ibc/04-channel/types/channel.go @@ -1,25 +1,26 @@ package types import ( - "encoding/json" "strings" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) +// Channel defines... type Channel struct { - State State `json:"state" yaml:"state"` - Ordering Order `json:"ordering" yaml:"ordering"` - Counterparty Counterparty `json:"counterparty" yaml:"counterparty"` - ConnectionHops []string `json:"connection_hops" yaml:"connection_hops"` - Version string `json:"version" yaml:"version "` + State exported.State `json:"state" yaml:"state"` + Ordering exported.Order `json:"ordering" yaml:"ordering"` + Counterparty Counterparty `json:"counterparty" yaml:"counterparty"` + ConnectionHops []string `json:"connection_hops" yaml:"connection_hops"` + Version string `json:"version" yaml:"version "` } // NewChannel creates a new Channel instance func NewChannel( - state State, ordering Order, counterparty Counterparty, + state exported.State, ordering exported.Order, counterparty Counterparty, hops []string, version string, ) Channel { return Channel{ @@ -31,6 +32,31 @@ func NewChannel( } } +// GetState implements Channel interface. +func (ch Channel) GetState() exported.State { + return ch.State +} + +// GetOrdering implements Channel interface. +func (ch Channel) GetOrdering() exported.Order { + return ch.Ordering +} + +// GetCounterparty implements Channel interface. +func (ch Channel) GetCounterparty() Counterparty { + return ch.Counterparty +} + +// GetConnectionHops implements Channel interface. +func (ch Channel) GetConnectionHops() []string { + return ch.ConnectionHops +} + +// GetVersion implements Channel interface. +func (ch Channel) GetVersion() string { + return ch.Version +} + // ValidateBasic performs a basic validation of the channel fields func (ch Channel) ValidateBasic() error { if ch.State.String() == "" { @@ -40,7 +66,10 @@ func (ch Channel) ValidateBasic() error { return sdkerrors.Wrap(ErrInvalidChannel, ErrInvalidChannelOrdering.Error()) } if len(ch.ConnectionHops) != 1 { - return sdkerrors.Wrap(ErrInvalidChannel, "IBC v1 only supports one connection hop") + return sdkerrors.Wrap( + ErrInvalidChannel, + sdkerrors.Wrap(ErrTooManyConnectionHops, "IBC v1.0 only supports one connection hop").Error(), + ) } if err := host.DefaultConnectionIdentifierValidator(ch.ConnectionHops[0]); err != nil { return sdkerrors.Wrap( @@ -71,6 +100,16 @@ func NewCounterparty(portID, channelID string) Counterparty { } } +// GetPortID implements CounterpartyI interface +func (c Counterparty) GetPortID() string { + return c.PortID +} + +// GetChannelID implements CounterpartyI interface +func (c Counterparty) GetChannelID() string { + return c.ChannelID +} + // ValidateBasic performs a basic validation check of the identifiers func (c Counterparty) ValidateBasic() error { if err := host.DefaultPortIdentifierValidator(c.PortID); err != nil { @@ -87,137 +126,3 @@ func (c Counterparty) ValidateBasic() error { } return nil } - -// Order defines if a channel is ORDERED or UNORDERED -type Order byte - -// string representation of the channel ordering -const ( - NONE Order = iota // zero-value for channel ordering - UNORDERED // packets can be delivered in any order, which may differ from the order in which they were sent. - ORDERED // packets are delivered exactly in the order which they were sent -) - -// channel order types -const ( - OrderNone string = "" - OrderUnordered string = "UNORDERED" - OrderOrdered string = "ORDERED" -) - -// String implements the Stringer interface -func (o Order) String() string { - switch o { - case UNORDERED: - return OrderUnordered - case ORDERED: - return OrderOrdered - default: - return OrderNone - } -} - -// MarshalJSON marshal to JSON using string. -func (o Order) MarshalJSON() ([]byte, error) { - return json.Marshal(o.String()) -} - -// UnmarshalJSON decodes from JSON. -func (o *Order) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - order := OrderFromString(s) - if order == 0 { - return sdkerrors.Wrap(ErrInvalidChannelOrdering, s) - } - - *o = order - return nil -} - -// OrderFromString parses a string into a channel order byte -func OrderFromString(order string) Order { - switch order { - case OrderUnordered: - return UNORDERED - case OrderOrdered: - return ORDERED - default: - return NONE - } -} - -// State defines if a channel is in one of the following states: -// CLOSED, INIT, OPENTRY or OPEN -type State byte - -// channel state types -const ( - UNINITIALIZED State = iota // Default State - INIT // A channel end has just started the opening handshake. - TRYOPEN // A channel end has acknowledged the handshake step on the counterparty chain. - OPEN // A channel end has completed the handshake and is ready to send and receive packets. - CLOSED // A channel end has been closed and can no longer be used to send or receive packets. -) - -// string representation of the channel states -const ( - StateUninitialized string = "UNINITIALIZED" - StateInit string = "INIT" - StateTryOpen string = "TRYOPEN" - StateOpen string = "OPEN" - StateClosed string = "CLOSED" -) - -// String implements the Stringer interface -func (s State) String() string { - switch s { - case INIT: - return StateInit - case TRYOPEN: - return StateTryOpen - case OPEN: - return StateOpen - case CLOSED: - return StateClosed - default: - return StateUninitialized - } -} - -// MarshalJSON marshal to JSON using string. -func (s State) MarshalJSON() ([]byte, error) { - return json.Marshal(s.String()) -} - -// UnmarshalJSON decodes from JSON. -func (s *State) UnmarshalJSON(data []byte) error { - var stateStr string - err := json.Unmarshal(data, &stateStr) - if err != nil { - return err - } - - *s = StateFromString(stateStr) - return nil -} - -// StateFromString parses a string into a channel state byte -func StateFromString(state string) State { - switch state { - case StateClosed: - return CLOSED - case StateInit: - return INIT - case StateTryOpen: - return TRYOPEN - case StateOpen: - return OPEN - default: - return UNINITIALIZED - } -} diff --git a/x/ibc/04-channel/types/channel_test.go b/x/ibc/04-channel/types/channel_test.go index 56b0dfe5eefa..2cd07f4c7d71 100644 --- a/x/ibc/04-channel/types/channel_test.go +++ b/x/ibc/04-channel/types/channel_test.go @@ -1,91 +1,3 @@ package types -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestChannelStateString(t *testing.T) { - cases := []struct { - name string - state State - }{ - {StateUninitialized, UNINITIALIZED}, - {StateInit, INIT}, - {StateTryOpen, TRYOPEN}, - {StateOpen, OPEN}, - {StateClosed, CLOSED}, - } - - for _, tt := range cases { - tt := tt - require.Equal(t, tt.state, StateFromString(tt.name)) - require.Equal(t, tt.name, tt.state.String()) - } -} - -func TestChannelStateMarshalJSON(t *testing.T) { - cases := []struct { - name string - state State - }{ - {StateUninitialized, UNINITIALIZED}, - {StateInit, INIT}, - {StateTryOpen, TRYOPEN}, - {StateOpen, OPEN}, - {StateClosed, CLOSED}, - } - - for _, tt := range cases { - tt := tt - bz, err := tt.state.MarshalJSON() - require.NoError(t, err) - var state State - require.NoError(t, state.UnmarshalJSON(bz)) - require.Equal(t, tt.name, state.String()) - } -} - -func TestOrderString(t *testing.T) { - cases := []struct { - name string - order Order - }{ - {OrderNone, NONE}, - {OrderUnordered, UNORDERED}, - {OrderOrdered, ORDERED}, - } - - for _, tt := range cases { - tt := tt - require.Equal(t, tt.order, OrderFromString(tt.name)) - require.Equal(t, tt.name, tt.order.String()) - } -} - -func TestOrderMarshalJSON(t *testing.T) { - cases := []struct { - msg string - name string - order Order - expectPass bool - }{ - {"none ordering should have failed", OrderNone, NONE, false}, - {"unordered should have passed", OrderUnordered, UNORDERED, true}, - {"ordered should have passed", OrderOrdered, ORDERED, true}, - } - - for _, tt := range cases { - tt := tt - bz, err := tt.order.MarshalJSON() - require.NoError(t, err) - var order Order - if tt.expectPass { - require.NoError(t, order.UnmarshalJSON(bz), tt.msg) - require.Equal(t, tt.name, order.String(), tt.msg) - } else { - require.Error(t, order.UnmarshalJSON(bz), tt.msg) - } - } -} +// TODO: channel and counterparty validation diff --git a/x/ibc/04-channel/types/errors.go b/x/ibc/04-channel/types/errors.go index 40e526476eae..9e6ee6fcf74c 100644 --- a/x/ibc/04-channel/types/errors.go +++ b/x/ibc/04-channel/types/errors.go @@ -17,4 +17,5 @@ var ( ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 9, "sequence receive not found") ErrInvalidPacket = sdkerrors.Register(SubModuleName, 10, "invalid packet") ErrPacketTimeout = sdkerrors.Register(SubModuleName, 11, "packet timeout") + ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 12, "too many connection hops") ) diff --git a/x/ibc/04-channel/types/expected_keepers.go b/x/ibc/04-channel/types/expected_keepers.go index b728aee4bb42..965665d77d3a 100644 --- a/x/ibc/04-channel/types/expected_keepers.go +++ b/x/ibc/04-channel/types/expected_keepers.go @@ -2,17 +2,29 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // ClientKeeper expected account IBC client keeper type ClientKeeper interface { - // GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) + GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) } // ConnectionKeeper expected account IBC connection keeper type ConnectionKeeper interface { GetConnection(ctx sdk.Context, connectionID string) (connectionexported.ConnectionI, bool) + VerifyChannelState( + ctx sdk.Context, + connection connectionexported.ConnectionI, + height uint64, + proof commitment.ProofI, + portID, + channelID string, + channel Channel, + consensusState clientexported.ConsensusState, + ) error } // PortKeeper expected account IBC port keeper diff --git a/x/ibc/04-channel/types/msgs.go b/x/ibc/04-channel/types/msgs.go index 3f578425dda0..c655cfda0f45 100644 --- a/x/ibc/04-channel/types/msgs.go +++ b/x/ibc/04-channel/types/msgs.go @@ -22,11 +22,11 @@ type MsgChannelOpenInit struct { // NewMsgChannelOpenInit creates a new MsgChannelCloseInit MsgChannelOpenInit func NewMsgChannelOpenInit( - portID, channelID string, version string, channelOrder Order, connectionHops []string, + portID, channelID string, version string, channelOrder exported.Order, connectionHops []string, counterpartyPortID, counterpartyChannelID string, signer sdk.AccAddress, ) MsgChannelOpenInit { counterparty := NewCounterparty(counterpartyPortID, counterpartyChannelID) - channel := NewChannel(INIT, channelOrder, counterparty, connectionHops, version) + channel := NewChannel(exported.INIT, channelOrder, counterparty, connectionHops, version) return MsgChannelOpenInit{ PortID: portID, ChannelID: channelID, @@ -81,12 +81,12 @@ type MsgChannelOpenTry struct { // NewMsgChannelOpenTry creates a new MsgChannelOpenTry instance func NewMsgChannelOpenTry( - portID, channelID, version string, channelOrder Order, connectionHops []string, + portID, channelID, version string, channelOrder exported.Order, connectionHops []string, counterpartyPortID, counterpartyChannelID, counterpartyVersion string, proofInit commitment.ProofI, proofHeight uint64, signer sdk.AccAddress, ) MsgChannelOpenTry { counterparty := NewCounterparty(counterpartyPortID, counterpartyChannelID) - channel := NewChannel(INIT, channelOrder, counterparty, connectionHops, version) + channel := NewChannel(exported.INIT, channelOrder, counterparty, connectionHops, version) return MsgChannelOpenTry{ PortID: portID, ChannelID: channelID, diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 9dd5c1710745..74d3853a13ef 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -6,7 +6,7 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -127,7 +127,7 @@ func (cs ClientState) VerifyChannelState( proof commitment.ProofI, portID, channelID string, - channelEnd channeltypes.Channel, + channel channelexported.ChannelI, consensusState clientexported.ConsensusState, ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.ChannelPath(portID, channelID)) @@ -143,7 +143,7 @@ func (cs ClientState) VerifyChannelState( return clienterrors.ErrClientFrozen } - bz, err := cdc.MarshalBinaryBare(channelEnd) + bz, err := cdc.MarshalBinaryBare(channel) if err != nil { return err } diff --git a/x/ibc/types/keys.go b/x/ibc/types/keys.go index 59a0051793cc..8789ec33cc82 100644 --- a/x/ibc/types/keys.go +++ b/x/ibc/types/keys.go @@ -104,6 +104,11 @@ func KeyConnection(connectionID string) []byte { // ICS04 // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#store-paths +// GetChannelPortsKeysPrefix returns the prefix bytes for ICS04 and ICS05 iterators +func GetChannelPortsKeysPrefix(prefix int) []byte { + return []byte(fmt.Sprintf("%d/ports/", prefix)) +} + // ChannelPath defines the path under which channels are stored func ChannelPath(portID, channelID string) string { return fmt.Sprintf("%d/", KeyChannelPrefix) + channelPath(portID, channelID) From 3fc142fd3c756ba48b0330dadbcba217381f7b19 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 16 Jan 2020 12:59:39 +0100 Subject: [PATCH 11/35] update ICS04 to latest spec --- x/ibc/03-connection/keeper/verify.go | 38 +-- x/ibc/04-channel/alias.go | 19 -- x/ibc/04-channel/keeper/packet.go | 277 +++++++++++---------- x/ibc/04-channel/keeper/timeout.go | 185 ++++++++------ x/ibc/04-channel/types/expected_keepers.go | 43 ++++ x/ibc/20-transfer/keeper/keeper.go | 2 + 6 files changed, 317 insertions(+), 247 deletions(-) diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 013596143321..cabe99a7937f 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -52,7 +53,7 @@ func (k Keeper) VerifyConnectionState( // channel end, under the specified port, stored on the target machine. func (k Keeper) VerifyChannelState( ctx sdk.Context, - connection types.ConnectionEnd, + connection exported.ConnectionI, height uint64, proof commitment.ProofI, portID, @@ -66,7 +67,8 @@ func (k Keeper) VerifyChannelState( } return clientState.VerifyChannelState( - k.cdc, height, connection.Counterparty.Prefix, proof, portID, channelID, channel, consensusState, + k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, + portID, channelID, channel, consensusState, ) } @@ -74,9 +76,8 @@ func (k Keeper) VerifyChannelState( // the specified port, specified channel, and specified sequence. func (k Keeper) VerifyPacketCommitment( ctx sdk.Context, - connection types.ConnectionEnd, + connection exported.ConnectionI, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, @@ -84,13 +85,14 @@ func (k Keeper) VerifyPacketCommitment( commitmentBytes []byte, consensusState clientexported.ConsensusState, ) error { - clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return clienterrors.ErrClientNotFound } return clientState.VerifyPacketCommitment( - height, prefix, proof, portID, channelID, sequence, commitmentBytes, consensusState, + height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, commitmentBytes, consensusState, ) } @@ -98,9 +100,8 @@ func (k Keeper) VerifyPacketCommitment( // acknowledgement at the specified port, specified channel, and specified sequence. func (k Keeper) VerifyPacketAcknowledgement( ctx sdk.Context, - connection types.ConnectionEnd, + connection exported.ConnectionI, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, @@ -108,13 +109,14 @@ func (k Keeper) VerifyPacketAcknowledgement( acknowledgement []byte, consensusState clientexported.ConsensusState, ) error { - clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return clienterrors.ErrClientNotFound } return clientState.VerifyPacketAcknowledgement( - height, prefix, proof, portID, channelID, sequence, acknowledgement, consensusState, + height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, acknowledgement, consensusState, ) } @@ -123,22 +125,22 @@ func (k Keeper) VerifyPacketAcknowledgement( // specified sequence. func (k Keeper) VerifyPacketAcknowledgementAbsence( ctx sdk.Context, - connection types.ConnectionEnd, + connection exported.ConnectionI, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, sequence uint64, consensusState clientexported.ConsensusState, ) error { - clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return clienterrors.ErrClientNotFound } return clientState.VerifyPacketAcknowledgementAbsence( - height, prefix, proof, portID, channelID, sequence, consensusState, + height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, consensusState, ) } @@ -146,21 +148,21 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence( // received of the specified channel at the specified port. func (k Keeper) VerifyNextSequenceRecv( ctx sdk.Context, - connection types.ConnectionEnd, + connection exported.ConnectionI, height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, portID, channelID string, nextSequenceRecv uint64, consensusState clientexported.ConsensusState, ) error { - clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return clienterrors.ErrClientNotFound } return clientState.VerifyNextSequenceRecv( - height, prefix, proof, portID, channelID, nextSequenceRecv, consensusState, + height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + nextSequenceRecv, consensusState, ) } diff --git a/x/ibc/04-channel/alias.go b/x/ibc/04-channel/alias.go index fb8506f2dd4f..196f81da52a1 100644 --- a/x/ibc/04-channel/alias.go +++ b/x/ibc/04-channel/alias.go @@ -12,21 +12,6 @@ import ( ) const ( - UNINITIALIZED = types.UNINITIALIZED - UNORDERED = types.UNORDERED - ORDERED = types.ORDERED - OrderNone = types.OrderNone - OrderUnordered = types.OrderUnordered - OrderOrdered = types.OrderOrdered - CLOSED = types.CLOSED - INIT = types.INIT - TRYOPEN = types.TRYOPEN - OPEN = types.OPEN - StateUninitialized = types.StateUninitialized - StateInit = types.StateInit - StateTryOpen = types.StateTryOpen - StateOpen = types.StateOpen - StateClosed = types.StateClosed AttributeKeySenderPort = types.AttributeKeySenderPort AttributeKeyReceiverPort = types.AttributeKeyReceiverPort AttributeKeyChannelID = types.AttributeKeyChannelID @@ -46,8 +31,6 @@ var ( QuerierChannel = keeper.QuerierChannel NewChannel = types.NewChannel NewCounterparty = types.NewCounterparty - OrderFromString = types.OrderFromString - StateFromString = types.StateFromString RegisterCodec = types.RegisterCodec ErrChannelExists = types.ErrChannelExists ErrChannelNotFound = types.ErrChannelNotFound @@ -87,8 +70,6 @@ type ( Keeper = keeper.Keeper Channel = types.Channel Counterparty = types.Counterparty - Order = types.Order - State = types.State ClientKeeper = types.ClientKeeper ConnectionKeeper = types.ConnectionKeeper PortKeeper = types.PortKeeper diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index 4e1b7b79870b..bb8b9e5404dd 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -2,120 +2,26 @@ package keeper import ( "bytes" - "errors" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) -// CleanupPacket is called by a module to remove a received packet commitment -// from storage. The receiving end must have already processed the packet -// (whether regularly or past timeout). -// -// In the ORDERED channel case, CleanupPacket cleans-up a packet on an ordered -// channel by proving that the packet has been received on the other end. -// -// In the UNORDERED channel case, CleanupPacket cleans-up a packet on an -// unordered channel by proving that the associated acknowledgement has been -//written. -func (k Keeper) CleanupPacket( - ctx sdk.Context, - packet exported.PacketI, - proof commitment.ProofI, - proofHeight, - nextSequenceRecv uint64, - acknowledgement []byte, - portCapability sdk.CapabilityKey, -) (exported.PacketI, error) { - channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) - } - - if channel.State != exported.OPEN { - return nil, sdkerrors.Wrapf( - types.ErrInvalidChannelState, - "channel state is not OPEN (got %s)", channel.State.String(), - ) - } - - _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return nil, types.ErrChannelCapabilityNotFound - } - - if !k.portKeeper.Authenticate(portCapability, packet.GetSourcePort()) { - return nil, sdkerrors.Wrapf(port.ErrInvalidPort, "invalid source port: %s", packet.GetSourcePort()) - } - - if packet.GetDestChannel() != channel.Counterparty.ChannelID { - return nil, sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelID, - ) - } - - connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - if !found { - return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) - } - - if packet.GetDestPort() != channel.Counterparty.PortID { - return nil, sdkerrors.Wrapf(types.ErrInvalidPacket, - "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, - ) - } - - if nextSequenceRecv >= packet.GetSequence() { - return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet already received") - } - - commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - if !bytes.Equal(commitment, types.CommitPacket(packet.GetData())) { - return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") - } - - var ok bool - switch channel.Ordering { - case exported.ORDERED: - ok = k.connectionKeeper.VerifyMembership( - ctx, connectionEnd, proofHeight, proof, - ibctypes.NextSequenceRecvPath(packet.GetDestPort(), packet.GetDestChannel()), - sdk.Uint64ToBigEndian(nextSequenceRecv), - ) - case exported.UNORDERED: - ok = k.connectionKeeper.VerifyMembership( - ctx, connectionEnd, proofHeight, proof, - ibctypes.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), - acknowledgement, - ) - default: - panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) - } - - if !ok { - return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet verification failed") - } - - k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - return packet, nil -} - // SendPacket is called by a module in order to send an IBC packet on a channel // end owned by the calling module to the corresponding module on the counterparty // chain. func (k Keeper) SendPacket( ctx sdk.Context, packet exported.PacketI, - portCapability sdk.CapabilityKey, ) error { if err := packet.ValidateBasic(); err != nil { return err @@ -133,7 +39,14 @@ func (k Keeper) SendPacket( ) } - if !k.portKeeper.Authenticate(portCapability, packet.GetSourcePort()) { + capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return types.ErrChannelCapabilityNotFound + } + + portCapabilityKey := sdk.NewKVStoreKey(capKey) + + if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { return sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) } @@ -156,6 +69,7 @@ func (k Keeper) SendPacket( return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } + // NOTE: assume UNINITIALIZED is a closed connection if connectionEnd.GetState() == connectionexported.UNINITIALIZED { return sdkerrors.Wrap( connection.ErrInvalidConnectionState, @@ -163,13 +77,14 @@ func (k Keeper) SendPacket( ) } - _, found = k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID()) if !found { return client.ErrConsensusStateNotFound } - if uint64(ctx.BlockHeight()) >= packet.GetTimeoutHeight() { - return types.ErrPacketTimeout + // check if packet timeouted on the receiving chain + if clientState.GetSequence() >= packet.GetTimeoutHeight() { + return sdkerrors.Wrap(types.ErrPacketTimeout, "timeout already passed ond the receiving chain") } nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) @@ -188,6 +103,7 @@ func (k Keeper) SendPacket( k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend) k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), types.CommitPacket(packet.GetData())) + k.Logger(ctx).Info(fmt.Sprintf("packet sent %v", packet)) // TODO: use packet.String() return nil } @@ -199,7 +115,6 @@ func (k Keeper) RecvPacket( proof commitment.ProofI, proofHeight uint64, ) (exported.PacketI, error) { - channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) @@ -212,7 +127,7 @@ func (k Keeper) RecvPacket( ) } - // RecvPacket is called by the antehandler which acts upon the packet.Route(), + // NOTE: RecvPacket is called by the AnteHandler which acts upon the packet.Route(), // so the capability authentication can be omitted here // packet must come from the channel's counterparty @@ -242,30 +157,23 @@ func (k Keeper) RecvPacket( ) } - if channel.Ordering == exported.ORDERED { - nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) - if !found { - return nil, types.ErrSequenceReceiveNotFound - } - - if packet.GetSequence() != nextSequenceRecv { - return nil, sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv, - ) - } - } - + // check if packet timeouted by comparing it with the latest height of the chain if uint64(ctx.BlockHeight()) >= packet.GetTimeoutHeight() { return nil, types.ErrPacketTimeout } - if !k.connectionKeeper.VerifyMembership( + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return nil, clienterrors.ErrConsensusStateNotFound + } + + err := k.connectionKeeper.VerifyPacketCommitment( ctx, connectionEnd, proofHeight, proof, - ibctypes.PacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()), - types.CommitPacket(packet.GetData()), - ) { - return nil, errors.New("couldn't verify counterparty packet commitment") + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), + types.CommitPacket(packet.GetData()), consensusState, + ) + if err != nil { + return nil, sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") } return packet, nil @@ -284,6 +192,7 @@ func (k Keeper) PacketExecuted( return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetDestChannel()) } + // sanity check if channel.State != exported.OPEN { return sdkerrors.Wrapf( types.ErrInvalidChannelState, @@ -316,6 +225,8 @@ func (k Keeper) PacketExecuted( k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv) } + // log that a packet has been received & acknowledged + k.Logger(ctx).Info(fmt.Sprintf("packet received %v", packet)) // TODO: use packet.String() return nil } @@ -343,7 +254,7 @@ func (k Keeper) AcknowledgePacket( ) } - // RecvPacket is called by the antehandler which acts upon the packet.Route(), + // NOTE: RecvPacket is called by the AnteHandler which acts upon the packet.Route(), // so the capability authentication can be omitted here // packet must come from the channel's counterparty @@ -374,16 +285,23 @@ func (k Keeper) AcknowledgePacket( } commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, types.CommitPacket(packet.GetData())) { return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } - if !k.connectionKeeper.VerifyMembership( - ctx, connectionEnd, proofHeight, proof, - ibctypes.PacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), - acknowledgement.GetBytes(), - ) { - return nil, errors.New("invalid acknowledgement on counterparty chain") + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return nil, clienterrors.ErrConsensusStateNotFound + } + + err := k.connectionKeeper.VerifyPacketAcknowledgement( + ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(), + packet.GetSequence(), acknowledgement.GetBytes(), consensusState, + ) + if err != nil { + return nil, sdkerrors.Wrap(err, "invalid acknowledgement on counterparty chain") } return packet, nil @@ -394,3 +312,106 @@ func (k Keeper) AcknowledgePacket( func (k Keeper) AcknowledgementExecuted(ctx sdk.Context, packet exported.PacketI) { k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) } + +// CleanupPacket is called by a module to remove a received packet commitment +// from storage. The receiving end must have already processed the packet +// (whether regularly or past timeout). +// +// In the ORDERED channel case, CleanupPacket cleans-up a packet on an ordered +// channel by proving that the packet has been received on the other end. +// +// In the UNORDERED channel case, CleanupPacket cleans-up a packet on an +// unordered channel by proving that the associated acknowledgement has been +//written. +func (k Keeper) CleanupPacket( + ctx sdk.Context, + packet exported.PacketI, + proof commitment.ProofI, + proofHeight, + nextSequenceRecv uint64, + acknowledgement []byte, +) (exported.PacketI, error) { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) + } + + if channel.State != exported.OPEN { + return nil, sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) + } + + capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return nil, types.ErrChannelCapabilityNotFound + } + + portCapabilityKey := sdk.NewKVStoreKey(capKey) + + if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { + return nil, sdkerrors.Wrapf(port.ErrInvalidPort, "invalid source port: %s", packet.GetSourcePort()) + } + + if packet.GetDestPort() != channel.Counterparty.PortID { + return nil, sdkerrors.Wrapf(types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, + ) + } + + if packet.GetDestChannel() != channel.Counterparty.ChannelID { + return nil, sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelID, + ) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + // check that packet has been received on the other end + if nextSequenceRecv <= packet.GetSequence() { + return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet already received") + } + + commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // verify we sent the packet and haven't cleared it out yet + if !bytes.Equal(commitment, types.CommitPacket(packet.GetData())) { + return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") + } + + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return nil, clienterrors.ErrConsensusStateNotFound + } + + var err error + switch channel.Ordering { + case exported.ORDERED: + // check that the recv sequence is as claimed + err = k.connectionKeeper.VerifyNextSequenceRecv( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, + consensusState, + ) + case exported.UNORDERED: + err = k.connectionKeeper.VerifyPacketAcknowledgement( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + acknowledgement, consensusState, + ) + default: + panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) + } + + if err != nil { + return nil, sdkerrors.Wrap(err, "packet verification failed") + } + + k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + return packet, nil +} diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index ccbffd25bd83..7340970280c7 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -2,13 +2,14 @@ package keeper import ( "bytes" - "errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -21,7 +22,7 @@ func (k Keeper) TimeoutPacket( ctx sdk.Context, packet exported.PacketI, proof commitment.ProofI, - proofHeight uint64, + proofHeight, nextSequenceRecv uint64, ) (exported.PacketI, error) { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) @@ -32,19 +33,21 @@ func (k Keeper) TimeoutPacket( ) } - if channel.State != types.OPEN { + if channel.State != exported.OPEN { return nil, sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) } - // TimeoutPacket is called by the antehandler which acts upon the packet.Route(), + // NOTE: TimeoutPacket is called by the AnteHandler which acts upon the packet.Route(), // so the capability authentication can be omitted here - _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return nil, types.ErrChannelCapabilityNotFound + if packet.GetDestPort() != channel.Counterparty.PortID { + return nil, sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, + ) } if packet.GetDestChannel() != channel.Counterparty.ChannelID { @@ -62,56 +65,78 @@ func (k Keeper) TimeoutPacket( ) } - if packet.GetDestPort() != channel.Counterparty.PortID { - return nil, sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, - ) - } - + // check that timeout height has passed on the other end if proofHeight < packet.GetTimeoutHeight() { return nil, types.ErrPacketTimeout } + // check that packet has not been received if nextSequenceRecv >= packet.GetSequence() { - return nil, sdkerrors.Wrap( - types.ErrInvalidPacket, - "packet already received", - ) + return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet already received") } commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, types.CommitPacket(packet.GetData())) { - return nil, sdkerrors.Wrap( - types.ErrInvalidPacket, - "packet hasn't been sent", - ) + return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } - var ok bool + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return nil, clienterrors.ErrConsensusStateNotFound + } + + var err error switch channel.Ordering { - case types.ORDERED: - ok = k.connectionKeeper.VerifyMembership( + case exported.ORDERED: + // check that the recv sequence is as claimed + err = k.connectionKeeper.VerifyNextSequenceRecv( ctx, connectionEnd, proofHeight, proof, - types.NextSequenceRecvPath(packet.GetDestPort(), packet.GetDestChannel()), - sdk.Uint64ToBigEndian(nextSequenceRecv), + packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, + consensusState, ) - case types.UNORDERED: - ok = k.connectionKeeper.VerifyNonMembership( + case exported.UNORDERED: + err = k.connectionKeeper.VerifyPacketAcknowledgementAbsence( ctx, connectionEnd, proofHeight, proof, - types.PacketAcknowledgementPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()), + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + consensusState, ) default: panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) } - if !ok { - return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet verification failed") + if err != nil { + return nil, sdkerrors.Wrap(err, "packet verification failed") } + // NOTE: the remaining code is located on the TimeoutExecuted function return packet, nil } +// TimeoutExecuted deletes the commitment send from this chain after it verifies timeout +func (k Keeper) TimeoutExecuted(ctx sdk.Context, packet exported.PacketI) error { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) + } + + // check if the packet is linked to a capability key + _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return types.ErrChannelCapabilityNotFound + } + + k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + if channel.Ordering == exported.ORDERED { + channel.State = exported.CLOSED + k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel) + } + + return nil +} + // TimeoutOnClose is called by a module in order to prove that the channel to // which an unreceived packet was addressed has been closed, so the packet will // never be received (even if the timeoutHeight has not yet been reached). @@ -120,21 +145,30 @@ func (k Keeper) TimeoutOnClose( packet types.Packet, proofNonMembership, proofClosed commitment.ProofI, - proofHeight uint64, - portCapability sdk.CapabilityKey, + proofHeight, + nextSequenceRecv uint64, ) (exported.PacketI, error) { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) } - _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { return nil, types.ErrChannelCapabilityNotFound } - if !k.portKeeper.Authenticate(portCapability, packet.GetSourcePort()) { - return nil, errors.New("port is not valid") + portCapabilityKey := sdk.NewKVStoreKey(capKey) + + if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { + return nil, sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) + } + + if packet.GetDestPort() != channel.Counterparty.PortID { + return nil, sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, + ) } if packet.GetDestChannel() != channel.Counterparty.ChannelID { @@ -149,14 +183,9 @@ func (k Keeper) TimeoutOnClose( return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0]) } - if packet.GetDestPort() != channel.Counterparty.PortID { - return nil, sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID, - ) - } - commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, types.CommitPacket(packet.GetData())) { return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } @@ -169,55 +198,47 @@ func (k Keeper) TimeoutOnClose( counterparty := types.NewCounterparty(packet.GetSourcePort(), packet.GetSourceChannel()) expectedChannel := types.NewChannel( - types.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, + exported.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, ) - bz, err := k.cdc.MarshalBinaryLengthPrefixed(expectedChannel) - if err != nil { - return nil, errors.New("failed to marshal expected channel") + consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + if !found { + return nil, clienterrors.ErrConsensusStateNotFound } - if !k.connectionKeeper.VerifyMembership( + // check that the opposing channel end has closed + err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofClosed, - types.ChannelPath(channel.Counterparty.PortID, channel.Counterparty.ChannelID), - bz, - ) { - return nil, sdkerrors.Wrap( - types.ErrInvalidCounterparty, - "channel membership verification failed", - ) - } - - if !k.connectionKeeper.VerifyNonMembership( - ctx, connectionEnd, proofHeight, proofNonMembership, - types.PacketAcknowledgementPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()), - ) { - return nil, errors.New("cannot verify absence of acknowledgement at packet index") + channel.Counterparty.PortID, channel.Counterparty.ChannelID, + expectedChannel, consensusState, + ) + if err != nil { + return nil, sdkerrors.Wrap(err, "channel membership verification failed") } - k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - - return packet, nil -} - -// TimeoutExecuted deletes the commitment send from this chain after it verifies timeout -func (k Keeper) TimeoutExecuted(ctx sdk.Context, packet exported.PacketI) error { - channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) + switch channel.Ordering { + case exported.ORDERED: + // check that the recv sequence is as claimed + err = k.connectionKeeper.VerifyNextSequenceRecv( + ctx, connectionEnd, proofHeight, proofClosed, + packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, + consensusState, + ) + case exported.UNORDERED: + err = k.connectionKeeper.VerifyPacketAcknowledgementAbsence( + ctx, connectionEnd, proofHeight, proofClosed, + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), + consensusState, + ) + default: + panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) } - _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return types.ErrChannelCapabilityNotFound + if err != nil { + return nil, sdkerrors.Wrap(err, "packet verification failed") } k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - if channel.Ordering == types.ORDERED { - channel.State = types.CLOSED - k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel) - } - - return nil + return packet, nil } diff --git a/x/ibc/04-channel/types/expected_keepers.go b/x/ibc/04-channel/types/expected_keepers.go index 965665d77d3a..20e97c8b3a81 100644 --- a/x/ibc/04-channel/types/expected_keepers.go +++ b/x/ibc/04-channel/types/expected_keepers.go @@ -9,6 +9,7 @@ import ( // ClientKeeper expected account IBC client keeper type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) } @@ -25,6 +26,48 @@ type ConnectionKeeper interface { channel Channel, consensusState clientexported.ConsensusState, ) error + VerifyPacketCommitment( + ctx sdk.Context, + connection connectionexported.ConnectionI, + height uint64, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, + consensusState clientexported.ConsensusState, + ) error + VerifyPacketAcknowledgement( + ctx sdk.Context, + connection connectionexported.ConnectionI, + height uint64, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, + consensusState clientexported.ConsensusState, + ) error + VerifyPacketAcknowledgementAbsence( + ctx sdk.Context, + connection connectionexported.ConnectionI, + height uint64, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + consensusState clientexported.ConsensusState, + ) error + VerifyNextSequenceRecv( + ctx sdk.Context, + connection connectionexported.ConnectionI, + height uint64, + proof commitment.ProofI, + portID, + channelID string, + nextSequenceRecv uint64, + consensusState clientexported.ConsensusState, + ) error } // PortKeeper expected account IBC port keeper diff --git a/x/ibc/20-transfer/keeper/keeper.go b/x/ibc/20-transfer/keeper/keeper.go index 05639f581b14..c57ca802fa4c 100644 --- a/x/ibc/20-transfer/keeper/keeper.go +++ b/x/ibc/20-transfer/keeper/keeper.go @@ -63,6 +63,8 @@ func (k Keeper) GetTransferAccount(ctx sdk.Context) supplyexported.ModuleAccount return k.supplyKeeper.GetModuleAccount(ctx, types.GetModuleAccountName()) } +// PacketExecuted defines a wrapper function for the channel Keeper's function +// in order to expose it to the ICS20 trasfer handler. func (k Keeper) PacketExecuted(ctx sdk.Context, packet channelexported.PacketI, acknowledgement channelexported.PacketDataI) error { return k.channelKeeper.PacketExecuted(ctx, packet, acknowledgement) } From 32160b99bda705dca1c1e7ea4083cf158c04a6a6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 16 Jan 2020 15:23:37 +0100 Subject: [PATCH 12/35] fix build --- x/ibc/03-connection/keeper/handshake.go | 8 +++++--- x/ibc/03-connection/keeper/verify.go | 11 +++++------ x/ibc/03-connection/types/expected_keepers.go | 2 +- x/ibc/04-channel/exported/exported.go | 2 +- x/ibc/04-channel/keeper/handshake.go | 16 ++++++++++++---- x/ibc/04-channel/keeper/packet.go | 12 +++++++++--- x/ibc/04-channel/keeper/timeout.go | 8 ++++++-- x/ibc/04-channel/types/channel.go | 2 +- x/ibc/04-channel/types/expected_keepers.go | 19 ++++++++++--------- x/ibc/20-transfer/keeper/relay.go | 2 +- x/ibc/20-transfer/types/expected_keepers.go | 2 +- 11 files changed, 52 insertions(+), 32 deletions(-) diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 601c81675c63..249f180f151c 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -61,7 +61,7 @@ func (k Keeper) ConnOpenTry( // return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") // } - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, clientID) + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, clientID, consensusHeight) if !found { return clienterrors.ErrConsensusStateNotFound } @@ -148,7 +148,7 @@ func (k Keeper) ConnOpenAck( ) } - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID) + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, consensusHeight) if !found { return clienterrors.ErrConsensusStateNotFound } @@ -202,7 +202,9 @@ func (k Keeper) ConnOpenConfirm( ) } - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID) + // NOTE: should be safe to use proofHeight here + // TODO: Update spec + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, proofHeight) if !found { return clienterrors.ErrConsensusStateNotFound } diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index cabe99a7937f..586cb337c38d 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -4,7 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -53,7 +52,7 @@ func (k Keeper) VerifyConnectionState( // channel end, under the specified port, stored on the target machine. func (k Keeper) VerifyChannelState( ctx sdk.Context, - connection exported.ConnectionI, + connection types.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -76,7 +75,7 @@ func (k Keeper) VerifyChannelState( // the specified port, specified channel, and specified sequence. func (k Keeper) VerifyPacketCommitment( ctx sdk.Context, - connection exported.ConnectionI, + connection types.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -100,7 +99,7 @@ func (k Keeper) VerifyPacketCommitment( // acknowledgement at the specified port, specified channel, and specified sequence. func (k Keeper) VerifyPacketAcknowledgement( ctx sdk.Context, - connection exported.ConnectionI, + connection types.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -125,7 +124,7 @@ func (k Keeper) VerifyPacketAcknowledgement( // specified sequence. func (k Keeper) VerifyPacketAcknowledgementAbsence( ctx sdk.Context, - connection exported.ConnectionI, + connection types.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -148,7 +147,7 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence( // received of the specified channel at the specified port. func (k Keeper) VerifyNextSequenceRecv( ctx sdk.Context, - connection exported.ConnectionI, + connection types.ConnectionEnd, height uint64, proof commitment.ProofI, portID, diff --git a/x/ibc/03-connection/types/expected_keepers.go b/x/ibc/03-connection/types/expected_keepers.go index 624c05999a55..a5de7514afa8 100644 --- a/x/ibc/03-connection/types/expected_keepers.go +++ b/x/ibc/03-connection/types/expected_keepers.go @@ -7,6 +7,6 @@ import ( // ClientKeeper expected account IBC client keeper type ClientKeeper interface { - GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) + GetConsensusState(ctx sdk.Context, clientID string, height uint64) (clientexported.ConsensusState, bool) } diff --git a/x/ibc/04-channel/exported/exported.go b/x/ibc/04-channel/exported/exported.go index 68976a5b79ad..718d239760f2 100644 --- a/x/ibc/04-channel/exported/exported.go +++ b/x/ibc/04-channel/exported/exported.go @@ -9,7 +9,7 @@ type ChannelI interface { GetState() State GetOrdering() Order GetCounterparty() CounterpartyI - GetConnectionHops() + GetConnectionHops() []string GetVersion() string ValidateBasic() error } diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index 5348f175d022..d536dc7a2024 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -132,7 +132,9 @@ func (k Keeper) ChanOpenTry( counterpartyHops, channel.Version, ) - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return clienterrors.ErrConsensusStateNotFound } @@ -209,7 +211,9 @@ func (k Keeper) ChanOpenAck( counterpartyHops, channel.Version, ) - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return clienterrors.ErrConsensusStateNotFound } @@ -287,7 +291,9 @@ func (k Keeper) ChanOpenConfirm( counterpartyHops, channel.Version, ) - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return clienterrors.ErrConsensusStateNotFound } @@ -411,7 +417,9 @@ func (k Keeper) ChanCloseConfirm( counterpartyHops, channel.Version, ) - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return clienterrors.ErrConsensusStateNotFound } diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index bb8b9e5404dd..f0cfa692d243 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -162,7 +162,9 @@ func (k Keeper) RecvPacket( return nil, types.ErrPacketTimeout } - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return nil, clienterrors.ErrConsensusStateNotFound } @@ -291,7 +293,9 @@ func (k Keeper) AcknowledgePacket( return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return nil, clienterrors.ErrConsensusStateNotFound } @@ -384,7 +388,9 @@ func (k Keeper) CleanupPacket( return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return nil, clienterrors.ErrConsensusStateNotFound } diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index 7340970280c7..7b221eb5ccff 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -82,7 +82,9 @@ func (k Keeper) TimeoutPacket( return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent") } - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return nil, clienterrors.ErrConsensusStateNotFound } @@ -201,7 +203,9 @@ func (k Keeper) TimeoutOnClose( exported.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, ) - consensusState, found := k.clientKeeper.GetConsensusState(ctx, connectionEnd.GetClientID()) + consensusState, found := k.clientKeeper.GetConsensusState( + ctx, connectionEnd.GetClientID(), proofHeight, + ) if !found { return nil, clienterrors.ErrConsensusStateNotFound } diff --git a/x/ibc/04-channel/types/channel.go b/x/ibc/04-channel/types/channel.go index 640945ef8f81..ebb026bc86ef 100644 --- a/x/ibc/04-channel/types/channel.go +++ b/x/ibc/04-channel/types/channel.go @@ -43,7 +43,7 @@ func (ch Channel) GetOrdering() exported.Order { } // GetCounterparty implements Channel interface. -func (ch Channel) GetCounterparty() Counterparty { +func (ch Channel) GetCounterparty() exported.CounterpartyI { return ch.Counterparty } diff --git a/x/ibc/04-channel/types/expected_keepers.go b/x/ibc/04-channel/types/expected_keepers.go index 20e97c8b3a81..a8b00b4ac014 100644 --- a/x/ibc/04-channel/types/expected_keepers.go +++ b/x/ibc/04-channel/types/expected_keepers.go @@ -3,32 +3,33 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // ClientKeeper expected account IBC client keeper type ClientKeeper interface { GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool) - GetConsensusState(ctx sdk.Context, clientID string) (clientexported.ConsensusState, bool) + GetConsensusState(ctx sdk.Context, clientID string, height uint64) (clientexported.ConsensusState, bool) } // ConnectionKeeper expected account IBC connection keeper type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectionexported.ConnectionI, bool) + GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) VerifyChannelState( ctx sdk.Context, - connection connectionexported.ConnectionI, + connection connectiontypes.ConnectionEnd, height uint64, proof commitment.ProofI, portID, channelID string, - channel Channel, + channel exported.ChannelI, consensusState clientexported.ConsensusState, ) error VerifyPacketCommitment( ctx sdk.Context, - connection connectionexported.ConnectionI, + connection connectiontypes.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -39,7 +40,7 @@ type ConnectionKeeper interface { ) error VerifyPacketAcknowledgement( ctx sdk.Context, - connection connectionexported.ConnectionI, + connection connectiontypes.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -50,7 +51,7 @@ type ConnectionKeeper interface { ) error VerifyPacketAcknowledgementAbsence( ctx sdk.Context, - connection connectionexported.ConnectionI, + connection connectiontypes.ConnectionEnd, height uint64, proof commitment.ProofI, portID, @@ -60,7 +61,7 @@ type ConnectionKeeper interface { ) error VerifyNextSequenceRecv( ctx sdk.Context, - connection connectionexported.ConnectionI, + connection connectiontypes.ConnectionEnd, height uint64, proof commitment.ProofI, portID, diff --git a/x/ibc/20-transfer/keeper/relay.go b/x/ibc/20-transfer/keeper/relay.go index d58412d26b1d..8bfa6efcfb97 100644 --- a/x/ibc/20-transfer/keeper/relay.go +++ b/x/ibc/20-transfer/keeper/relay.go @@ -202,5 +202,5 @@ func (k Keeper) createOutgoingPacket( destinationChannel, ) - return k.channelKeeper.SendPacket(ctx, packet, k.boundedCapability) + return k.channelKeeper.SendPacket(ctx, packet) } diff --git a/x/ibc/20-transfer/types/expected_keepers.go b/x/ibc/20-transfer/types/expected_keepers.go index a8bd48a215de..b79fbe5313eb 100644 --- a/x/ibc/20-transfer/types/expected_keepers.go +++ b/x/ibc/20-transfer/types/expected_keepers.go @@ -18,7 +18,7 @@ type BankKeeper interface { type ChannelKeeper interface { GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channel.Channel, found bool) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) - SendPacket(ctx sdk.Context, packet channelexported.PacketI, portCapability sdk.CapabilityKey) error + SendPacket(ctx sdk.Context, packet channelexported.PacketI) error PacketExecuted(ctx sdk.Context, packet channelexported.PacketI, acknowledgement channelexported.PacketDataI) error } From 944fec8fd624da2a022909239fa412fc8c6789a3 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 16 Jan 2020 15:35:58 +0100 Subject: [PATCH 13/35] move ics02 types/errors package to /types --- x/ibc/02-client/alias.go | 21 +++++++------- x/ibc/02-client/handler.go | 4 +-- x/ibc/02-client/keeper/client.go | 17 ++++++----- x/ibc/02-client/keeper/keeper.go | 3 +- x/ibc/02-client/types/{errors => }/errors.go | 5 +--- x/ibc/02-client/types/msgs.go | 7 ++--- x/ibc/03-connection/keeper/handshake.go | 8 +++--- x/ibc/03-connection/keeper/keeper.go | 2 +- x/ibc/03-connection/keeper/verify.go | 16 +++++------ x/ibc/04-channel/client/cli/tx.go | 2 +- x/ibc/04-channel/exported/exported_test.go | 3 +- x/ibc/04-channel/keeper/handshake.go | 10 +++---- x/ibc/04-channel/keeper/packet.go | 8 +++--- x/ibc/04-channel/keeper/timeout.go | 6 ++-- x/ibc/07-tendermint/client_state.go | 30 ++++++++++---------- x/ibc/07-tendermint/consensus_state.go | 6 ++-- x/ibc/07-tendermint/consensus_state_test.go | 2 +- x/ibc/07-tendermint/evidence.go | 16 +++++------ x/ibc/07-tendermint/header.go | 6 ++-- x/ibc/07-tendermint/misbehaviour.go | 16 +++++------ x/ibc/07-tendermint/update.go | 8 +++--- x/ibc/20-transfer/keeper/relay_test.go | 2 +- 22 files changed, 95 insertions(+), 103 deletions(-) rename x/ibc/02-client/types/{errors => }/errors.go (95%) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index ab73bd2be6f7..3b76fb13c58c 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -9,7 +9,6 @@ package client import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) const ( @@ -29,16 +28,16 @@ var ( NewKeeper = keeper.NewKeeper QuerierClients = keeper.QuerierClients RegisterCodec = types.RegisterCodec - ErrClientExists = errors.ErrClientExists - ErrClientNotFound = errors.ErrClientNotFound - ErrClientFrozen = errors.ErrClientFrozen - ErrConsensusStateNotFound = errors.ErrConsensusStateNotFound - ErrInvalidConsensus = errors.ErrInvalidConsensus - ErrClientTypeNotFound = errors.ErrClientTypeNotFound - ErrInvalidClientType = errors.ErrInvalidClientType - ErrRootNotFound = errors.ErrRootNotFound - ErrInvalidHeader = errors.ErrInvalidHeader - ErrInvalidEvidence = errors.ErrInvalidEvidence + ErrClientExists = types.ErrClientExists + ErrClientNotFound = types.ErrClientNotFound + ErrClientFrozen = types.ErrClientFrozen + ErrConsensusStateNotFound = types.ErrConsensusStateNotFound + ErrInvalidConsensus = types.ErrInvalidConsensus + ErrClientTypeNotFound = types.ErrClientTypeNotFound + ErrInvalidClientType = types.ErrInvalidClientType + ErrRootNotFound = types.ErrRootNotFound + ErrInvalidHeader = types.ErrInvalidHeader + ErrInvalidEvidence = types.ErrInvalidEvidence NewMsgCreateClient = types.NewMsgCreateClient NewMsgUpdateClient = types.NewMsgUpdateClient diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 97d91a0363eb..c9aa56f75404 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) // HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient @@ -70,7 +70,7 @@ func HandlerClientMisbehaviour(k Keeper) evidence.Handler { return func(ctx sdk.Context, evidence evidenceexported.Evidence) error { misbehaviour, ok := evidence.(exported.Misbehaviour) if !ok { - return errors.ErrInvalidEvidence + return types.ErrInvalidEvidence } return k.CheckMisbehaviourAndUpdateState(ctx, misbehaviour) diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index f8da510eea86..903bee326f24 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -7,7 +7,6 @@ import ( 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" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) @@ -19,7 +18,7 @@ func (k Keeper) CreateClient( ) (exported.ClientState, error) { _, found := k.GetClientState(ctx, clientID) if found { - return nil, sdkerrors.Wrapf(errors.ErrClientExists, "cannot create client with ID %s", clientID) + return nil, sdkerrors.Wrapf(types.ErrClientExists, "cannot create client with ID %s", clientID) } _, found = k.GetClientType(ctx, clientID) @@ -54,22 +53,22 @@ func (k Keeper) CreateClient( func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { clientType, found := k.GetClientType(ctx, clientID) if !found { - return sdkerrors.Wrapf(errors.ErrClientTypeNotFound, "cannot update client with ID %s", clientID) + return sdkerrors.Wrapf(types.ErrClientTypeNotFound, "cannot update client with ID %s", clientID) } // check that the header consensus matches the client one if header.ClientType() != clientType { - return sdkerrors.Wrapf(errors.ErrInvalidConsensus, "cannot update client with ID %s", clientID) + return sdkerrors.Wrapf(types.ErrInvalidConsensus, "cannot update client with ID %s", clientID) } clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrapf(errors.ErrClientNotFound, "cannot update client with ID %s", clientID) + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) } // addittion to spec: prevent update if the client is frozen if clientState.IsFrozen() { - return sdkerrors.Wrapf(errors.ErrClientFrozen, "cannot update client with ID %s", clientID) + return sdkerrors.Wrapf(types.ErrClientFrozen, "cannot update client with ID %s", clientID) } var ( @@ -81,7 +80,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H case exported.Tendermint: clientState, consensusState, err = tendermint.CheckValidityAndUpdateState(clientState, header, ctx.ChainID()) default: - return sdkerrors.Wrapf(errors.ErrInvalidClientType, "cannot update client with ID %s", clientID) + return sdkerrors.Wrapf(types.ErrInvalidClientType, "cannot update client with ID %s", clientID) } if err != nil { @@ -111,12 +110,12 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error { clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID()) if !found { - return sdkerrors.Wrap(errors.ErrClientNotFound, misbehaviour.GetClientID()) + return sdkerrors.Wrap(types.ErrClientNotFound, misbehaviour.GetClientID()) } consensusState, found := k.GetConsensusState(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight())) if !found { - return sdkerrors.Wrap(errors.ErrConsensusStateNotFound, misbehaviour.GetClientID()) + return sdkerrors.Wrap(types.ErrConsensusStateNotFound, misbehaviour.GetClientID()) } var err error diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index e40f534e542f..4b31f8f08cb2 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -9,7 +9,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -131,7 +130,7 @@ func (k Keeper) initialize( case exported.Tendermint: clientState = tendermint.NewClientState(clientID, height) default: - return nil, errors.ErrInvalidClientType + return nil, types.ErrInvalidClientType } k.SetConsensusState(ctx, clientID, height, consensusState) diff --git a/x/ibc/02-client/types/errors/errors.go b/x/ibc/02-client/types/errors.go similarity index 95% rename from x/ibc/02-client/types/errors/errors.go rename to x/ibc/02-client/types/errors.go index af663e4d5b19..0cdf4b61c5d0 100644 --- a/x/ibc/02-client/types/errors/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -1,12 +1,9 @@ -package errors +package types import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// SubModuleName is the error codespace -const SubModuleName string = "ibc/client" - // IBC client sentinel errors var ( ErrClientExists = sdkerrors.Register(SubModuleName, 1, "light client already exists") diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index b47a8e24db83..2790d8d18884 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -4,7 +4,6 @@ import ( 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/errors" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -49,10 +48,10 @@ func (msg MsgCreateClient) Type() string { // ValidateBasic implements sdk.Msg func (msg MsgCreateClient) ValidateBasic() error { if clientType := exported.ClientTypeFromString(msg.ClientType); clientType == 0 { - return sdkerrors.Wrap(errors.ErrInvalidClientType, msg.ClientType) + return sdkerrors.Wrap(ErrInvalidClientType, msg.ClientType) } if msg.ConsensusState == nil { - return errors.ErrInvalidConsensus + return ErrInvalidConsensus } if err := msg.ConsensusState.ValidateBasic(); err != nil { return err @@ -104,7 +103,7 @@ func (msg MsgUpdateClient) Type() string { // ValidateBasic implements sdk.Msg func (msg MsgUpdateClient) ValidateBasic() error { if msg.Header == nil { - return errors.ErrInvalidHeader + return ErrInvalidHeader } if msg.Signer.Empty() { return sdkerrors.ErrInvalidAddress diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 249f180f151c..12fad65fa54e 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -63,7 +63,7 @@ func (k Keeper) ConnOpenTry( expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, clientID, consensusHeight) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } // expectedConnection defines Chain A's ConnectionEnd @@ -150,7 +150,7 @@ func (k Keeper) ConnOpenAck( expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, consensusHeight) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } prefix := k.GetCommitmentPrefix() @@ -206,7 +206,7 @@ func (k Keeper) ConnOpenConfirm( // TODO: Update spec expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, proofHeight) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } prefix := k.GetCommitmentPrefix() diff --git a/x/ibc/03-connection/keeper/keeper.go b/x/ibc/03-connection/keeper/keeper.go index 9d71b244817e..b7b2dff3015b 100644 --- a/x/ibc/03-connection/keeper/keeper.go +++ b/x/ibc/03-connection/keeper/keeper.go @@ -142,4 +142,4 @@ func (k Keeper) removeConnectionFromClient(ctx sdk.Context, clientID, connection k.SetClientConnectionPaths(ctx, clientID, conns) return nil -} \ No newline at end of file +} diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 586cb337c38d..432505545540 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -20,7 +20,7 @@ func (k Keeper) VerifyClientConsensusState( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyClientConsensusState( @@ -40,7 +40,7 @@ func (k Keeper) VerifyConnectionState( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyConnectionState( @@ -62,7 +62,7 @@ func (k Keeper) VerifyChannelState( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyChannelState( @@ -86,7 +86,7 @@ func (k Keeper) VerifyPacketCommitment( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyPacketCommitment( @@ -110,7 +110,7 @@ func (k Keeper) VerifyPacketAcknowledgement( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyPacketAcknowledgement( @@ -134,7 +134,7 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyPacketAcknowledgementAbsence( @@ -157,7 +157,7 @@ func (k Keeper) VerifyNextSequenceRecv( ) error { clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return clienterrors.ErrClientNotFound + return clienttypes.ErrClientNotFound } return clientState.VerifyNextSequenceRecv( diff --git a/x/ibc/04-channel/client/cli/tx.go b/x/ibc/04-channel/client/cli/tx.go index 0255947e50c5..3ed2119b470f 100644 --- a/x/ibc/04-channel/client/cli/tx.go +++ b/x/ibc/04-channel/client/cli/tx.go @@ -14,8 +14,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils" connectionutils "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/client/utils" - "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) // IBC Channel flags diff --git a/x/ibc/04-channel/exported/exported_test.go b/x/ibc/04-channel/exported/exported_test.go index e3a64e1fc09f..51d94bfdebb1 100644 --- a/x/ibc/04-channel/exported/exported_test.go +++ b/x/ibc/04-channel/exported/exported_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" ) - func TestChannelStateString(t *testing.T) { cases := []struct { name string @@ -89,4 +88,4 @@ func TestOrderMarshalJSON(t *testing.T) { require.Error(t, order.UnmarshalJSON(bz), tt.msg) } } -} \ No newline at end of file +} diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index d536dc7a2024..bb916110b989 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" @@ -136,7 +136,7 @@ func (k Keeper) ChanOpenTry( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyChannelState( @@ -215,7 +215,7 @@ func (k Keeper) ChanOpenAck( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyChannelState( @@ -295,7 +295,7 @@ func (k Keeper) ChanOpenConfirm( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyChannelState( @@ -421,7 +421,7 @@ func (k Keeper) ChanCloseConfirm( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return clienterrors.ErrConsensusStateNotFound + return clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyChannelState( diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index f0cfa692d243..497fefb450e0 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" @@ -166,7 +166,7 @@ func (k Keeper) RecvPacket( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return nil, clienterrors.ErrConsensusStateNotFound + return nil, clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyPacketCommitment( @@ -297,7 +297,7 @@ func (k Keeper) AcknowledgePacket( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return nil, clienterrors.ErrConsensusStateNotFound + return nil, clienttypes.ErrConsensusStateNotFound } err := k.connectionKeeper.VerifyPacketAcknowledgement( @@ -392,7 +392,7 @@ func (k Keeper) CleanupPacket( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return nil, clienterrors.ErrConsensusStateNotFound + return nil, clienttypes.ErrConsensusStateNotFound } var err error diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index 7b221eb5ccff..927f40eb5f02 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" @@ -86,7 +86,7 @@ func (k Keeper) TimeoutPacket( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return nil, clienterrors.ErrConsensusStateNotFound + return nil, clienttypes.ErrConsensusStateNotFound } var err error @@ -207,7 +207,7 @@ func (k Keeper) TimeoutOnClose( ctx, connectionEnd.GetClientID(), proofHeight, ) if !found { - return nil, clienterrors.ErrConsensusStateNotFound + return nil, clienttypes.ErrConsensusStateNotFound } // check that the opposing channel end has closed diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 74d3853a13ef..8a4f0243741a 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -71,7 +71,7 @@ func (cs ClientState) VerifyClientConsensusState( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } bz, err := cdc.MarshalBinaryBare(consensusState) @@ -80,7 +80,7 @@ func (cs ClientState) VerifyClientConsensusState( } if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienterrors.ErrFailedClientConsensusStateVerification + return clienttypes.ErrFailedClientConsensusStateVerification } return nil @@ -105,7 +105,7 @@ func (cs ClientState) VerifyConnectionState( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } bz, err := cdc.MarshalBinaryBare(connectionEnd) @@ -114,7 +114,7 @@ func (cs ClientState) VerifyConnectionState( } if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienterrors.ErrFailedConnectionStateVerification + return clienttypes.ErrFailedConnectionStateVerification } return nil @@ -140,7 +140,7 @@ func (cs ClientState) VerifyChannelState( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } bz, err := cdc.MarshalBinaryBare(channel) @@ -149,7 +149,7 @@ func (cs ClientState) VerifyChannelState( } if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienterrors.ErrFailedChannelStateVerification + return clienttypes.ErrFailedChannelStateVerification } return nil @@ -175,11 +175,11 @@ func (cs ClientState) VerifyPacketCommitment( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } if ok := proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); !ok { - return clienterrors.ErrFailedPacketCommitmentVerification + return clienttypes.ErrFailedPacketCommitmentVerification } return nil @@ -205,11 +205,11 @@ func (cs ClientState) VerifyPacketAcknowledgement( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } if ok := proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); !ok { - return clienterrors.ErrFailedPacketAckVerification + return clienttypes.ErrFailedPacketAckVerification } return nil @@ -231,11 +231,11 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } if ok := proof.VerifyNonMembership(consensusState.GetRoot(), path); !ok { - return clienterrors.ErrFailedPacketAckAbsenceVerification + return clienttypes.ErrFailedPacketAckAbsenceVerification } return nil @@ -260,13 +260,13 @@ func (cs ClientState) VerifyNextSequenceRecv( } if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienterrors.ErrClientFrozen + return clienttypes.ErrClientFrozen } bz := sdk.Uint64ToBigEndian(nextSequenceRecv) if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienterrors.ErrFailedNextSeqRecvVerification + return clienttypes.ErrFailedNextSeqRecvVerification } return nil diff --git a/x/ibc/07-tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go index d9f1d818a4b2..a99118e08dd1 100644 --- a/x/ibc/07-tendermint/consensus_state.go +++ b/x/ibc/07-tendermint/consensus_state.go @@ -3,7 +3,7 @@ package tendermint import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -26,10 +26,10 @@ func (cs ConsensusState) GetRoot() commitment.RootI { // ValidateBasic func (cs ConsensusState) ValidateBasic() error { if cs.Root == nil { - return sdkerrors.Wrap(clienterrors.ErrInvalidConsensus, "root cannot be nil") + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be nil") } if len(cs.ValidatorSetHash) == 0 { - return sdkerrors.Wrap(clienterrors.ErrInvalidConsensus, "validator set hash cannot be empty") + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "validator set hash cannot be empty") } return nil } diff --git a/x/ibc/07-tendermint/consensus_state_test.go b/x/ibc/07-tendermint/consensus_state_test.go index de8d07304e66..1b7ad5eda026 100644 --- a/x/ibc/07-tendermint/consensus_state_test.go +++ b/x/ibc/07-tendermint/consensus_state_test.go @@ -1 +1 @@ -package tendermint \ No newline at end of file +package tendermint diff --git a/x/ibc/07-tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go index 26cf7693b9a8..a607fea6c183 100644 --- a/x/ibc/07-tendermint/evidence.go +++ b/x/ibc/07-tendermint/evidence.go @@ -10,7 +10,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" evidenceexported "github.com/cosmos/cosmos-sdk/x/evidence/exported" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) @@ -73,29 +73,29 @@ func (ev Evidence) GetHeight() int64 { // ValidateBasic implements Evidence interface func (ev Evidence) ValidateBasic() error { if err := host.DefaultClientIdentifierValidator(ev.ClientID); err != nil { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + return sdkerrors.Wrap(clienttypes.ErrInvalidEvidence, err.Error()) } // ValidateBasic on both validators if err := ev.Header1.ValidateBasic(ev.ChainID); err != nil { return sdkerrors.Wrap( - errors.ErrInvalidEvidence, + clienttypes.ErrInvalidEvidence, sdkerrors.Wrap(err, "header 1 failed validation").Error(), ) } if err := ev.Header2.ValidateBasic(ev.ChainID); err != nil { return sdkerrors.Wrap( - errors.ErrInvalidEvidence, + clienttypes.ErrInvalidEvidence, sdkerrors.Wrap(err, "header 2 failed validation").Error(), ) } // Ensure that Heights are the same if ev.Header1.Height != ev.Header2.Height { - return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "headers in evidence are on different heights (%d ≠ %d)", ev.Header1.Height, ev.Header2.Height) + return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "headers in evidence are on different heights (%d ≠ %d)", ev.Header1.Height, ev.Header2.Height) } // Ensure that Commit Hashes are different if ev.Header1.Commit.BlockID.Equals(ev.Header2.Commit.BlockID) { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, "headers commit to same blockID") + return sdkerrors.Wrap(clienttypes.ErrInvalidEvidence, "headers commit to same blockID") } if err := ValidCommit(ev.ChainID, ev.Header1.Commit, ev.Header1.ValidatorSet); err != nil { return err @@ -114,7 +114,7 @@ func (ev Evidence) ValidateBasic() error { func ValidCommit(chainID string, commit *tmtypes.Commit, valSet *tmtypes.ValidatorSet) (err error) { defer func() { if r := recover(); r != nil { - err = sdkerrors.Wrapf(errors.ErrInvalidEvidence, "invalid commit: %v", r) + err = sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "invalid commit: %v", r) } }() @@ -125,7 +125,7 @@ func ValidCommit(chainID string, commit *tmtypes.Commit, valSet *tmtypes.Validat // Check that ValidatorSet did indeed commit to blockID in Commit if !ok || !blockID.Equals(commit.BlockID) { - return sdkerrors.Wrap(errors.ErrInvalidEvidence, "validator set did not commit to header 1") + return sdkerrors.Wrap(clienttypes.ErrInvalidEvidence, "validator set did not commit to header 1") } return nil diff --git a/x/ibc/07-tendermint/header.go b/x/ibc/07-tendermint/header.go index 167fefc93a75..2b91f1f79ff1 100644 --- a/x/ibc/07-tendermint/header.go +++ b/x/ibc/07-tendermint/header.go @@ -5,7 +5,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) var _ exported.Header = Header{} @@ -32,10 +32,10 @@ func (h Header) GetHeight() uint64 { // and checks that validatorsets are not nil func (h Header) ValidateBasic(chainID string) error { if err := h.SignedHeader.ValidateBasic(chainID); err != nil { - return sdkerrors.Wrap(clienterrors.ErrInvalidHeader, err.Error()) + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, err.Error()) } if h.ValidatorSet == nil { - return sdkerrors.Wrap(clienterrors.ErrInvalidHeader, "validator set is nil") + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") } return nil } diff --git a/x/ibc/07-tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go index 31bdfaeffdb6..f8a2ac7b2c16 100644 --- a/x/ibc/07-tendermint/misbehaviour.go +++ b/x/ibc/07-tendermint/misbehaviour.go @@ -5,7 +5,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -24,21 +24,21 @@ func CheckMisbehaviourAndUpdateState( // cast the interface to specific types before checking for misbehaviour tmClientState, ok := clientState.(ClientState) if !ok { - return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "client state type is not Tendermint") + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidClientType, "client state type is not Tendermint") } tmConsensusState, ok := consensusState.(ConsensusState) if !ok { - return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "consensus state is not Tendermint") + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidClientType, "consensus state is not Tendermint") } tmEvidence, ok := misbehaviour.(Evidence) if !ok { - return nil, sdkerrors.Wrap(errors.ErrInvalidClientType, "evidence type is not Tendermint") + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidClientType, "evidence type is not Tendermint") } if err := checkMisbehaviour(tmClientState, tmConsensusState, tmEvidence, height); err != nil { - return nil, sdkerrors.Wrap(errors.ErrInvalidEvidence, err.Error()) + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidEvidence, err.Error()) } tmClientState.FrozenHeight = uint64(tmEvidence.GetHeight()) @@ -63,7 +63,7 @@ func checkMisbehaviour( if !bytes.Equal(consensusState.ValidatorSetHash, evidence.FromValidatorSet.Hash()) { return sdkerrors.Wrap( - errors.ErrInvalidEvidence, + clienttypes.ErrInvalidEvidence, "the consensus state's validator set hash doesn't match the evidence's one", ) } @@ -75,14 +75,14 @@ func checkMisbehaviour( evidence.Header1.ValidatorSet, evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit, ); err != nil { - return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err) } if err := evidence.FromValidatorSet.VerifyFutureCommit( evidence.Header2.ValidatorSet, evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit, ); err != nil { - return sdkerrors.Wrapf(errors.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err) } return nil diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go index 1874a86f6b1b..e69913305c5d 100644 --- a/x/ibc/07-tendermint/update.go +++ b/x/ibc/07-tendermint/update.go @@ -3,7 +3,7 @@ package tendermint import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienterrors "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -15,14 +15,14 @@ func CheckValidityAndUpdateState( tmClientState, ok := clientState.(ClientState) if !ok { return nil, nil, sdkerrors.Wrap( - clienterrors.ErrInvalidClientType, "light client is not from Tendermint", + clienttypes.ErrInvalidClientType, "light client is not from Tendermint", ) } tmHeader, ok := header.(Header) if !ok { return nil, nil, sdkerrors.Wrap( - clienterrors.ErrInvalidHeader, "header is not from Tendermint", + clienttypes.ErrInvalidHeader, "header is not from Tendermint", ) } @@ -40,7 +40,7 @@ func CheckValidityAndUpdateState( func checkValidity(clientState ClientState, header Header, chainID string) error { if header.GetHeight() < clientState.LatestHeight { return sdkerrors.Wrapf( - clienterrors.ErrInvalidHeader, + clienttypes.ErrInvalidHeader, "header height < latest client state height (%d < %d)", header.GetHeight(), clientState.LatestHeight, ) } diff --git a/x/ibc/20-transfer/keeper/relay_test.go b/x/ibc/20-transfer/keeper/relay_test.go index c2a7fa45fc38..2a3da8ddb105 100644 --- a/x/ibc/20-transfer/keeper/relay_test.go +++ b/x/ibc/20-transfer/keeper/relay_test.go @@ -6,9 +6,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" From 3d48b9b1770b67b3fa4c765b511e7c8fccd6d8dc Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 16 Jan 2020 18:04:17 +0100 Subject: [PATCH 14/35] update a few tests --- x/ibc/02-client/keeper/client_test.go | 8 +- x/ibc/02-client/keeper/keeper_test.go | 15 +- x/ibc/03-connection/keeper/handshake_test.go | 139 +++++++------------ x/ibc/04-channel/keeper/handshake.go | 2 +- x/ibc/04-channel/keeper/handshake_test.go | 115 ++++++++------- x/ibc/04-channel/keeper/keeper_test.go | 11 +- x/ibc/04-channel/types/msgs_test.go | 68 ++++----- x/ibc/ante/ante_test.go | 36 +++-- 8 files changed, 170 insertions(+), 224 deletions(-) diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index 8d3c9e39f371..ebdc5a5c3870 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -28,8 +28,6 @@ func (suite *KeeperTestSuite) TestCreateClient() { // Test ClientType and VerifiedRoot stored correctly clientType, _ := suite.keeper.GetClientType(suite.ctx, testClientID) require.Equal(suite.T(), exported.Tendermint, clientType, "Incorrect ClientType stored") - root, _ := suite.keeper.GetVerifiedRoot(suite.ctx, testClientID, suite.consensusState.GetHeight()) - require.Equal(suite.T(), suite.consensusState.GetRoot(), root, "Incorrect root stored") // Test that trying to CreateClient on existing client fails _, err = suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) @@ -54,7 +52,6 @@ func (suite *KeeperTestSuite) TestUpdateClient() { }, true}, {"frozen client", func() { clientState, _ := suite.keeper.GetClientState(suite.ctx, testClientID) - clientState.Frozen = true suite.keeper.SetClientState(suite.ctx, clientState) }, true}, {"past height", func() { @@ -91,7 +88,6 @@ func (suite *KeeperTestSuite) TestUpdateClient() { tmConsState, _ := retrievedConsState.(tendermint.ConsensusState) tmConsState.ValidatorSet.TotalVotingPower() tmConsState.NextValidatorSet.TotalVotingPower() - retrievedRoot, _ := suite.keeper.GetVerifiedRoot(suite.ctx, testClientID, suite.consensusState.GetHeight()+1) if tc.expErr { suite.Error(err, "Invalid UpdateClient passed", tc.name) @@ -185,8 +181,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { for _, tc := range testCases { tc := tc // pin for scopelint suite.Run(tc.name, func() { - misbehaviour := tendermint.Misbehaviour{ - Evidence: tc.evidence, + misbehaviour := tendermint.Evidence{ + tc.evidence, ClientID: tc.clientID, } diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 33fe93ba1eba..870ef0ba55fc 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -52,11 +52,8 @@ func (suite *KeeperTestSuite) SetupTest() { suite.header = tendermint.MakeHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.consensusState = tendermint.ConsensusState{ - ChainID: testClientID, - Height: 3, Root: commitment.NewRoot([]byte("hash")), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ValidatorSetHash: suite.valSet.Hash(), } } @@ -94,16 +91,6 @@ func (suite *KeeperTestSuite) TestSetConsensusState() { require.Equal(suite.T(), suite.consensusState, tmConsState, "ConsensusState not stored correctly") } -func (suite *KeeperTestSuite) TestSetVerifiedRoot() { - root := commitment.NewRoot([]byte("hash")) - suite.keeper.SetVerifiedRoot(suite.ctx, testClientID, 3, root) - - retrievedRoot, ok := suite.keeper.GetVerifiedRoot(suite.ctx, testClientID, 3) - - require.True(suite.T(), ok, "GetVerifiedRoot failed") - require.Equal(suite.T(), root, retrievedRoot, "Root stored incorrectly") -} - func (suite KeeperTestSuite) TestGetAllClients() { expClients := []exported.ClientState{ tendermint.NewClientState(testClientID2), diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index 40a48249d536..f2953314514f 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -6,6 +6,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -17,61 +18,58 @@ func (suite *KeeperTestSuite) TestConnOpenInit() { success := func() error { err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) + suite.NoError(err) conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) suite.True(existed) expectConn := connection.ConnectionEnd{ - State: connection.INIT, + State: exported.INIT, ClientID: testClientID1, Counterparty: counterparty, Versions: connection.GetCompatibleVersions(), } suite.EqualValues(expectConn, conn) - return err + return nil } connectionExists := func() error { - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) - return err + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) } var testCases = []TestCase{ - {success, true, ""}, + {success, true, "success"}, {connectionExists, false, "connection already exists"}, } for _, tc := range testCases { - suite.Equal(tc.expected, tc.fun() == nil, "error: %s", tc.errMsg) + suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) } } func (suite *KeeperTestSuite) TestConnOpenTry() { suite.createClient(testClientID2) suite.createClient(testClientID1) - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) connectionKey := ibctypes.KeyConnection(testConnectionID2) - consensusKey := ibctypes.KeyConsensusState(testClientID2) - invalidProof := func() error { - proofInit, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) + proofInit, proofHeight := suite.queryProof(connectionKey) + consensusKey := ibctypes.KeyConsensusState(testClientID2, uint64(proofHeight)) + proofConsensus, consensusHeight := suite.queryProof(consensusKey) + invalidProof := func() error { counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, testConnectionID1, counterparty, testClientID1, connection.GetCompatibleVersions(), proofInit, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err } success := func() error { suite.updateClient(testClientID1) - proofInit, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, @@ -84,32 +82,28 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { //check connection state conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) suite.True(existed) - suite.Equal(connection.TRYOPEN.String(), conn.State.String(), "invalid connection state") - return err + suite.Equal(exported.TRYOPEN.String(), conn.State.String(), "invalid connection state") + return nil } connectionExists := func() error { suite.updateClient(testClientID1) - proofInit, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, testConnectionID1, counterparty, testClientID1, connection.GetCompatibleVersions(), proofInit, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err } var testCases = []TestCase{ {invalidProof, false, "invalid proof"}, - {success, true, ""}, {connectionExists, false, "connection already exists"}, + {success, true, "success"}, } for _, tc := range testCases { - suite.Equal(tc.expected, tc.fun() == nil, "error: %s", tc.errMsg) + suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) } } @@ -118,63 +112,43 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { suite.createClient(testClientID2) suite.createClient(testClientID1) - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, connection.TRYOPEN) + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) connectionKey := ibctypes.KeyConnection(testConnectionID1) - consensusKey := ibctypes.KeyConsensusState(testClientID1) - connectionNotFound := func() error { - //suite.updateClient(testClientID2) + proofTry, proofHeight := suite.queryProof(connectionKey) + consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(proofHeight)) + proofConsensus, consensusHeight := suite.queryProof(consensusKey) - proofTry, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err + connectionNotFound := func() error { + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) } invalidConnectionState := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.UNINITIALIZED) - //suite.updateClient(testClientID2) - - proofTry, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) } invalidVersion := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.INIT) - //suite.updateClient(testClientID2) - - proofTry, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, "1.0.1", proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, "1.0.1", proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) } invalidProof := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.INIT) - //suite.updateClient(testClientID2) - - proofTry, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - return err + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) } success := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) suite.updateClient(testClientID2) - - proofTry, proofHeight := suite.queryProof(connectionKey) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) + suite.NoError(err) //check connection state conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID2) suite.True(existed) - suite.Equal(connection.OPEN.String(), conn.State.String(), "invalid connection state") - return err - + suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") + return nil } var testCases = []TestCase{ @@ -186,7 +160,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { } for _, tc := range testCases { - suite.Equal(tc.expected, tc.fun() == nil, "error: %s", tc.errMsg) + suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) } } @@ -194,42 +168,36 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { func (suite *KeeperTestSuite) TestConnOpenConfirm() { suite.createClient(testClientID2) suite.createClient(testClientID1) - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, connection.OPEN) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.OPEN) connKey := ibctypes.KeyConnection(testConnectionID2) proof, h := suite.queryProof(connKey) connectionNotFound := func() error { - //ErrConnectionNotFound - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - return err + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) } invalidConnectionState := func() error { - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, connection.INIT) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - return err + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) } invalidProof := func() error { - //Error proof - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, connection.TRYOPEN) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - return err + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) + return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) } success := func() error { - //Success - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, connection.TRYOPEN) + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) suite.updateClient(testClientID1) proof, h = suite.queryProof(connKey) err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) + suite.NoError(err) - //check connection state conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) suite.True(existed) - suite.Equal(connection.OPEN.String(), conn.State.String(), "invalid connection state") - return err + suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") + return nil } var testCases = []TestCase{ @@ -240,23 +208,23 @@ func (suite *KeeperTestSuite) TestConnOpenConfirm() { } for _, tc := range testCases { - suite.Equal(tc.expected, tc.fun() == nil, "error: %s", tc.errMsg) + suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) } } -func (suite *KeeperTestSuite) queryProof(key []byte) (proof commitment.Proof, height int64) { +func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { res := suite.app.Query(abci.RequestQuery{ Path: fmt.Sprintf("store/%s/key", storeKey), Data: key, Prove: true, }) - height = res.Height - proof = commitment.Proof{ + height := res.Height + proof := commitment.Proof{ Proof: res.Proof, } - return + return proof, height } func (suite *KeeperTestSuite) createClient(clientID string) { @@ -280,17 +248,18 @@ func (suite *KeeperTestSuite) updateClient(clientID string) { suite.app.Commit() commitID := suite.app.LastCommitID() - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) + height := suite.app.LastBlockHeight() + 1 + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) state := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(height), state) } -func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state connection.State) { +func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { counterparty := connection.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) connection := connection.ConnectionEnd{ State: state, diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index bb916110b989..4c1487a47f36 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -173,7 +173,7 @@ func (k Keeper) ChanOpenAck( return sdkerrors.Wrap(types.ErrChannelNotFound, channelID) } - if channel.State != exported.INIT || channel.State != exported.TRYOPEN { + if !(channel.State == exported.INIT || channel.State == exported.TRYOPEN) { return sdkerrors.Wrapf( types.ErrInvalidChannelState, "channel state should be INIT or TRYOPEN (got %s)", channel.State.String(), diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 8272f8677f11..79f1324c9757 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -7,6 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -21,11 +23,8 @@ func (suite *KeeperTestSuite) createClient() { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) consensusState := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ValidatorSetHash: suite.valSet.Hash(), } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) @@ -37,20 +36,18 @@ func (suite *KeeperTestSuite) updateClient() { suite.app.Commit() commitID := suite.app.LastCommitID() - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) + height := suite.app.LastBlockHeight() + 1 + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) state := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), + Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) - suite.app.IBCKeeper.ClientKeeper.SetVerifiedRoot(suite.ctx, testClient, state.GetHeight(), state.GetRoot()) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height), state) } -func (suite *KeeperTestSuite) createConnection(state connection.State) { +func (suite *KeeperTestSuite) createConnection(state connectionexported.State) { connection := connection.ConnectionEnd{ State: state, ClientID: testClient, @@ -65,7 +62,7 @@ func (suite *KeeperTestSuite) createConnection(state connection.State) { suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnection, connection) } -func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state types.State) { +func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state exported.State) { channel := types.Channel{ State: state, Ordering: testChannelOrder, @@ -82,7 +79,7 @@ func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID func (suite *KeeperTestSuite) deleteChannel(portID string, chanID string) { store := suite.ctx.KVStore(suite.app.GetKey(ibctypes.StoreKey)) - store.Delete(types.KeyChannel(portID, chanID)) + store.Delete(ibctypes.KeyChannel(portID, chanID)) } func (suite *KeeperTestSuite) bindPort(portID string) sdk.CapabilityKey { @@ -107,7 +104,7 @@ func (suite *KeeperTestSuite) queryProof(key []byte) (proof commitment.Proof, he func (suite *KeeperTestSuite) TestChanOpenInit() { counterparty := types.NewCounterparty(testPort2, testChannel2) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.INIT) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) suite.Error(err) // channel has already exist @@ -115,26 +112,26 @@ func (suite *KeeperTestSuite) TestChanOpenInit() { err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) suite.Error(err) // connection does not exist - suite.createConnection(connection.UNINITIALIZED) + suite.createConnection(connectionexported.UNINITIALIZED) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) + suite.createConnection(connectionexported.OPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) suite.NoError(err) // successfully executed channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) suite.True(found) - suite.Equal(types.INIT.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.INIT.String(), channel.State.String(), "invalid channel state") } func (suite *KeeperTestSuite) TestChanOpenTry() { counterparty := types.NewCounterparty(testPort1, testChannel1) suite.bindPort(testPort2) - channelKey := types.KeyChannel(testPort1, testChannel1) + channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.INIT) - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.INIT) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.INIT) suite.updateClient() proofInit, proofHeight := suite.queryProof(channelKey) err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) @@ -147,18 +144,18 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) suite.Error(err) // connection does not exist - suite.createConnection(connection.UNINITIALIZED) + suite.createConnection(connectionexported.UNINITIALIZED) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.TRYOPEN) + suite.createConnection(connectionexported.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.TRYOPEN) suite.updateClient() proofInit, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) suite.Error(err) // channel membership verification failed due to invalid counterparty - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.INIT) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) suite.updateClient() proofInit, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) @@ -166,43 +163,43 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) suite.True(found) - suite.Equal(types.TRYOPEN.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.TRYOPEN.String(), channel.State.String(), "invalid channel state") } func (suite *KeeperTestSuite) TestChanOpenAck() { suite.bindPort(testPort1) - channelKey := types.KeyChannel(testPort2, testChannel2) + channelKey := ibctypes.KeyChannel(testPort2, testChannel2) - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.TRYOPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) suite.updateClient() proofTry, proofHeight := suite.queryProof(channelKey) err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // channel does not exist - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.CLOSED) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // invalid channel state - suite.createChannel(testPort2, testChannel1, testConnection, testPort1, testChannel2, types.INIT) + suite.createChannel(testPort2, testChannel1, testConnection, testPort1, testChannel2, exported.INIT) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort2, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // unauthenticated port - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.INIT) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // connection does not exist - suite.createConnection(connection.UNINITIALIZED) + suite.createConnection(connectionexported.UNINITIALIZED) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.OPEN) + suite.createConnection(connectionexported.OPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) suite.updateClient() proofTry, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) suite.Error(err) // channel membership verification failed due to invalid counterparty - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.TRYOPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) suite.updateClient() proofTry, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) @@ -210,43 +207,43 @@ func (suite *KeeperTestSuite) TestChanOpenAck() { channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) suite.True(found) - suite.Equal(types.OPEN.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.OPEN.String(), channel.State.String(), "invalid channel state") } func (suite *KeeperTestSuite) TestChanOpenConfirm() { suite.bindPort(testPort2) - channelKey := types.KeyChannel(testPort1, testChannel1) + channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) suite.updateClient() proofAck, proofHeight := suite.queryProof(channelKey) err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // channel does not exist - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.OPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // invalid channel state - suite.createChannel(testPort1, testChannel2, testConnection, testPort2, testChannel1, types.TRYOPEN) + suite.createChannel(testPort1, testChannel2, testConnection, testPort2, testChannel1, exported.TRYOPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort1, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // unauthenticated port - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.TRYOPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // connection does not exist - suite.createConnection(connection.UNINITIALIZED) + suite.createConnection(connectionexported.UNINITIALIZED) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.TRYOPEN) + suite.createConnection(connectionexported.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.TRYOPEN) suite.updateClient() proofAck, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) suite.Error(err) // channel membership verification failed due to invalid counterparty - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) suite.updateClient() proofAck, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) @@ -254,7 +251,7 @@ func (suite *KeeperTestSuite) TestChanOpenConfirm() { channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) suite.True(found) - suite.Equal(types.OPEN.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.OPEN.String(), channel.State.String(), "invalid channel state") } func (suite *KeeperTestSuite) TestChanCloseInit() { @@ -266,32 +263,32 @@ func (suite *KeeperTestSuite) TestChanCloseInit() { err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) suite.Error(err) // channel does not exist - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.CLOSED) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) suite.Error(err) // channel is already closed - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) suite.Error(err) // connection does not exist - suite.createConnection(connection.TRYOPEN) + suite.createConnection(connectionexported.TRYOPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) + suite.createConnection(connectionexported.OPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) suite.NoError(err) // successfully executed channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) suite.True(found) - suite.Equal(types.CLOSED.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.CLOSED.String(), channel.State.String(), "invalid channel state") } func (suite *KeeperTestSuite) TestChanCloseConfirm() { suite.bindPort(testPort2) - channelKey := types.KeyChannel(testPort1, testChannel1) + channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.CLOSED) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) suite.updateClient() proofInit, proofHeight := suite.queryProof(channelKey) err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort1, testChannel2, proofInit, uint64(proofHeight)) @@ -300,26 +297,26 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) suite.Error(err) // channel does not exist - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.CLOSED) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.CLOSED) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) suite.Error(err) // channel is already closed - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, types.OPEN) + suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) suite.Error(err) // connection does not exist - suite.createConnection(connection.TRYOPEN) + suite.createConnection(connectionexported.TRYOPEN) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) suite.Error(err) // invalid connection state - suite.createConnection(connection.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.OPEN) + suite.createConnection(connectionexported.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) suite.updateClient() proofInit, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) suite.Error(err) // channel membership verification failed due to invalid counterparty - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, types.CLOSED) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) suite.updateClient() proofInit, proofHeight = suite.queryProof(channelKey) err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) @@ -327,5 +324,5 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) suite.True(found) - suite.Equal(types.CLOSED.String(), channel.State.String(), "invalid channel state") + suite.Equal(exported.CLOSED.String(), channel.State.String(), "invalid channel state") } diff --git a/x/ibc/04-channel/keeper/keeper_test.go b/x/ibc/04-channel/keeper/keeper_test.go index b8159d760bc4..d5140e34cd72 100644 --- a/x/ibc/04-channel/keeper/keeper_test.go +++ b/x/ibc/04-channel/keeper/keeper_test.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) @@ -30,7 +31,7 @@ const ( testChannel2 = "secondchannel" testChannel3 = "thirdchannel" - testChannelOrder = types.ORDERED + testChannelOrder = exported.ORDERED testChannelVersion = "1.0" ) @@ -64,7 +65,7 @@ func (suite *KeeperTestSuite) TestSetChannel() { suite.False(found) channel := types.Channel{ - State: types.OPEN, + State: exported.OPEN, Ordering: testChannelOrder, Counterparty: types.Counterparty{ PortID: testPort2, @@ -87,7 +88,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { counterparty3 := types.NewCounterparty(testPort3, testChannel3) channel1 := types.Channel{ - State: types.INIT, + State: exported.INIT, Ordering: testChannelOrder, Counterparty: counterparty3, ConnectionHops: []string{testConnection}, @@ -95,7 +96,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { } channel2 := types.Channel{ - State: types.INIT, + State: exported.INIT, Ordering: testChannelOrder, Counterparty: counterparty1, ConnectionHops: []string{testConnection}, @@ -103,7 +104,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { } channel3 := types.Channel{ - State: types.CLOSED, + State: exported.CLOSED, Ordering: testChannelOrder, Counterparty: counterparty2, ConnectionHops: []string{testConnection}, diff --git a/x/ibc/04-channel/types/msgs_test.go b/x/ibc/04-channel/types/msgs_test.go index a6d324670200..731ebe42c01d 100644 --- a/x/ibc/04-channel/types/msgs_test.go +++ b/x/ibc/04-channel/types/msgs_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto/merkle" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/store/iavl" @@ -81,21 +81,21 @@ func TestMsgTestSuite(t *testing.T) { // TestMsgChannelOpenInit tests ValidateBasic for MsgChannelOpenInit func (suite *MsgTestSuite) TestMsgChannelOpenInit() { testMsgs := []MsgChannelOpenInit{ - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // valid msg - NewMsgChannelOpenInit(invalidShortPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short port id - NewMsgChannelOpenInit(invalidLongPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long port id - NewMsgChannelOpenInit(invalidPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // port id contains non-alpha - NewMsgChannelOpenInit("testportid", invalidShortChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short channel id - NewMsgChannelOpenInit("testportid", invalidLongChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long channel id - NewMsgChannelOpenInit("testportid", invalidChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", addr), // channel id contains non-alpha - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", Order(3), connHops, "testcpport", "testcpchannel", addr), // invalid channel order - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", ORDERED, invalidConnHops, "testcpport", "testcpchannel", addr), // connection hops more than 1 - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", addr), // too short connection id - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", addr), // too long connection id - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", addr), // connection id contains non-alpha - NewMsgChannelOpenInit("testportid", "testchannel", "", UNORDERED, connHops, "testcpport", "testcpchannel", addr), // empty channel version - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", UNORDERED, connHops, invalidPort, "testcpchannel", addr), // invalid counterparty port id - NewMsgChannelOpenInit("testportid", "testchannel", "1.0", UNORDERED, connHops, "testcpport", invalidChannel, addr), // invalid counterparty channel id + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // valid msg + NewMsgChannelOpenInit(invalidShortPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short port id + NewMsgChannelOpenInit(invalidLongPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long port id + NewMsgChannelOpenInit(invalidPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // port id contains non-alpha + NewMsgChannelOpenInit("testportid", invalidShortChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short channel id + NewMsgChannelOpenInit("testportid", invalidLongChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long channel id + NewMsgChannelOpenInit("testportid", invalidChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", addr), // channel id contains non-alpha + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.Order(3), connHops, "testcpport", "testcpchannel", addr), // invalid channel order + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.ORDERED, invalidConnHops, "testcpport", "testcpchannel", addr), // connection hops more than 1 + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", addr), // too short connection id + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", addr), // too long connection id + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", addr), // connection id contains non-alpha + NewMsgChannelOpenInit("testportid", "testchannel", "", exported.UNORDERED, connHops, "testcpport", "testcpchannel", addr), // empty channel version + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.UNORDERED, connHops, invalidPort, "testcpchannel", addr), // invalid counterparty port id + NewMsgChannelOpenInit("testportid", "testchannel", "1.0", exported.UNORDERED, connHops, "testcpport", invalidChannel, addr), // invalid counterparty channel id } testCases := []struct { @@ -133,24 +133,24 @@ func (suite *MsgTestSuite) TestMsgChannelOpenInit() { // TestMsgChannelOpenTry tests ValidateBasic for MsgChannelOpenTry func (suite *MsgTestSuite) TestMsgChannelOpenTry() { testMsgs := []MsgChannelOpenTry{ - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // valid msg - NewMsgChannelOpenTry(invalidShortPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short port id - NewMsgChannelOpenTry(invalidLongPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long port id - NewMsgChannelOpenTry(invalidPort, "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // port id contains non-alpha - NewMsgChannelOpenTry("testportid", invalidShortChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short channel id - NewMsgChannelOpenTry("testportid", invalidLongChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long channel id - NewMsgChannelOpenTry("testportid", invalidChannel, "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // channel id contains non-alpha - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "", suite.proof, 1, addr), // empty counterparty version - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", nil, 1, addr), // empty suite.proof - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 0, addr), // suite.proof height is zero - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", Order(4), connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // invalid channel order - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, invalidConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // connection hops more than 1 - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short connection id - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long connection id - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // connection id contains non-alpha - NewMsgChannelOpenTry("testportid", "testchannel", "", UNORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // empty channel version - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, connHops, invalidPort, "testcpchannel", "1.0", suite.proof, 1, addr), // invalid counterparty port id - NewMsgChannelOpenTry("testportid", "testchannel", "1.0", UNORDERED, connHops, "testcpport", invalidChannel, "1.0", suite.proof, 1, addr), // invalid counterparty channel id + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // valid msg + NewMsgChannelOpenTry(invalidShortPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short port id + NewMsgChannelOpenTry(invalidLongPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long port id + NewMsgChannelOpenTry(invalidPort, "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // port id contains non-alpha + NewMsgChannelOpenTry("testportid", invalidShortChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short channel id + NewMsgChannelOpenTry("testportid", invalidLongChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long channel id + NewMsgChannelOpenTry("testportid", invalidChannel, "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // channel id contains non-alpha + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "", suite.proof, 1, addr), // empty counterparty version + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", nil, 1, addr), // empty suite.proof + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 0, addr), // suite.proof height is zero + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.Order(4), connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // invalid channel order + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, invalidConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // connection hops more than 1 + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too short connection id + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // too long connection id + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // connection id contains non-alpha + NewMsgChannelOpenTry("testportid", "testchannel", "", exported.UNORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, 1, addr), // empty channel version + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, connHops, invalidPort, "testcpchannel", "1.0", suite.proof, 1, addr), // invalid counterparty port id + NewMsgChannelOpenTry("testportid", "testchannel", "1.0", exported.UNORDERED, connHops, "testcpport", invalidChannel, "1.0", suite.proof, 1, addr), // invalid counterparty channel id } testCases := []struct { diff --git a/x/ibc/ante/ante_test.go b/x/ibc/ante/ante_test.go index b23f9fbeefcf..60acb3873d01 100644 --- a/x/ibc/ante/ante_test.go +++ b/x/ibc/ante/ante_test.go @@ -13,11 +13,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - clienttypestm "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/ante" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -58,7 +59,7 @@ func (suite *HandlerTestSuite) SetupTest() { suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.createClient() - suite.createConnection(connection.OPEN) + suite.createConnection(connectionexported.OPEN) } func (suite *HandlerTestSuite) createClient() { @@ -68,12 +69,9 @@ func (suite *HandlerTestSuite) createClient() { suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - consensusState := clienttypestm.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), + consensusState := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ValidatorSetHash: suite.valSet.Hash(), } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) @@ -85,20 +83,18 @@ func (suite *HandlerTestSuite) updateClient() { suite.app.Commit() commitID := suite.app.LastCommitID() - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) + height := suite.app.LastBlockHeight() + 1 + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - state := clienttypestm.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), + state := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) - suite.app.IBCKeeper.ClientKeeper.SetVerifiedRoot(suite.ctx, testClient, state.GetHeight(), state.GetRoot()) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height), state) } -func (suite *HandlerTestSuite) createConnection(state connection.State) { +func (suite *HandlerTestSuite) createConnection(state connectionexported.State) { connection := connection.ConnectionEnd{ State: state, ClientID: testClient, @@ -113,7 +109,7 @@ func (suite *HandlerTestSuite) createConnection(state connection.State) { suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnection, connection) } -func (suite *HandlerTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state channel.State, order channel.Order) { +func (suite *HandlerTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state channelexported.State, order channelexported.Order) { ch := channel.Channel{ State: state, Ordering: order, @@ -165,8 +161,8 @@ func (suite *HandlerTestSuite) TestHandleMsgPacketOrdered() { suite.Error(err, "%+v", err) // channel does not exist cctx, _ = suite.ctx.CacheContext() - suite.createChannel(cpportid, cpchanid, testConnection, portid, chanid, channel.OPEN, channel.ORDERED) - packetCommitmentPath := channel.PacketCommitmentPath(packet.SourcePort, packet.SourceChannel, packet.Sequence) + suite.createChannel(cpportid, cpchanid, testConnection, portid, chanid, channelexported.OPEN, channelexported.ORDERED) + packetCommitmentPath := ibctypes.PacketCommitmentPath(packet.SourcePort, packet.SourceChannel, packet.Sequence) proof, proofHeight := suite.queryProof(packetCommitmentPath) msg = channel.NewMsgPacket(packet, proof, uint64(proofHeight), addr1) _, err = handler(cctx, suite.newTx(msg), false) @@ -212,14 +208,14 @@ func (suite *HandlerTestSuite) TestHandleMsgPacketUnordered() { suite.app.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.ctx, packet.SourcePort, packet.SourceChannel, uint64(10)) - suite.createChannel(cpportid, cpchanid, testConnection, portid, chanid, channel.OPEN, channel.UNORDERED) + suite.createChannel(cpportid, cpchanid, testConnection, portid, chanid, channelexported.OPEN, channelexported.UNORDERED) suite.updateClient() for i := 10; i >= 0; i-- { cctx, write := suite.ctx.CacheContext() packet = channel.NewPacket(newPacket(uint64(i)), uint64(i), portid, chanid, cpportid, cpchanid) - packetCommitmentPath := channel.PacketCommitmentPath(packet.SourcePort, packet.SourceChannel, uint64(i)) + packetCommitmentPath := ibctypes.PacketCommitmentPath(packet.SourcePort, packet.SourceChannel, uint64(i)) proof, proofHeight := suite.queryProof(packetCommitmentPath) msg := channel.NewMsgPacket(packet, proof, uint64(proofHeight), addr1) _, err := handler(cctx, suite.newTx(msg), false) From c1de08b273ee59ed7415f9674d406c16251d8864 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 17 Jan 2020 18:28:19 +0100 Subject: [PATCH 15/35] update tests; fix codec registration --- x/ibc/02-client/keeper/client_test.go | 3 -- x/ibc/02-client/types/errors.go | 2 +- x/ibc/03-connection/keeper/handshake_test.go | 41 +++++++++++++------- x/ibc/04-channel/types/codec.go | 5 +++ x/ibc/07-tendermint/codec.go | 6 ++- x/ibc/20-transfer/handler_test.go | 31 +++++++-------- x/ibc/20-transfer/keeper/keeper_test.go | 8 ++-- x/ibc/20-transfer/keeper/relay_test.go | 18 ++++----- x/ibc/20-transfer/types/codec.go | 6 ++- x/ibc/keeper/querier_test.go | 6 --- x/ibc/module.go | 2 + 11 files changed, 69 insertions(+), 59 deletions(-) diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index ebdc5a5c3870..51ec4765ac96 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -60,9 +60,6 @@ func (suite *KeeperTestSuite) TestUpdateClient() { {"validatorHash incorrect", func() { suite.header = tendermint.MakeHeader("gaia", 4, altValSet, suite.valSet, altSigners) }, true}, - {"nextHash incorrect", func() { - suite.header.NextValidatorSet = altValSet - }, true}, {"header fails validateBasic", func() { suite.header.ChainID = "test" }, true}, diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 0cdf4b61c5d0..ceba41f85988 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -22,5 +22,5 @@ var ( ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 16, "packet commitment verification failed") ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 17, "packet acknowledgement verification failed") ErrFailedPacketAckAbsenceVerification = sdkerrors.Register(SubModuleName, 18, "packet acknowledgement absence verification failed") - ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 18, "next sequence receive verification failed") + ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 19, "next sequence receive verification failed") ) diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index f2953314514f..97da665b3c5a 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -38,13 +38,17 @@ func (suite *KeeperTestSuite) TestConnOpenInit() { return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) } - var testCases = []TestCase{ + var testCases = []testCase{ {success, true, "success"}, {connectionExists, false, "connection already exists"}, } for _, tc := range testCases { - suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) + if tc.expectPass { + suite.Require().NoError(tc.fun(), tc.msg) + } else { + suite.Require().Error(tc.fun(), tc.msg) + } } } @@ -96,14 +100,18 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { uint64(proofHeight), uint64(consensusHeight)) } - var testCases = []TestCase{ + var testCases = []testCase{ {invalidProof, false, "invalid proof"}, {connectionExists, false, "connection already exists"}, {success, true, "success"}, } for _, tc := range testCases { - suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) + if tc.expectPass { + suite.Require().NoError(tc.fun(), tc.msg) + } else { + suite.Require().Error(tc.fun(), tc.msg) + } } } @@ -151,7 +159,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { return nil } - var testCases = []TestCase{ + var testCases = []testCase{ {connectionNotFound, false, "connection not exists"}, {invalidConnectionState, false, "invalid connection state"}, {invalidVersion, false, "invalid version"}, @@ -160,9 +168,12 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { } for _, tc := range testCases { - suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) + if tc.expectPass { + suite.Require().NoError(tc.fun(), tc.msg) + } else { + suite.Require().Error(tc.fun(), tc.msg) + } } - } func (suite *KeeperTestSuite) TestConnOpenConfirm() { @@ -200,7 +211,7 @@ func (suite *KeeperTestSuite) TestConnOpenConfirm() { return nil } - var testCases = []TestCase{ + var testCases = []testCase{ {connectionNotFound, false, "connection not exists"}, {invalidConnectionState, false, "invalid connection state"}, {invalidProof, false, "invalid proof"}, @@ -208,7 +219,11 @@ func (suite *KeeperTestSuite) TestConnOpenConfirm() { } for _, tc := range testCases { - suite.Require().Nil(tc.expected, tc.fun(), tc.errMsg) + if tc.expectPass { + suite.Require().NoError(tc.fun(), tc.msg) + } else { + suite.Require().Error(tc.fun(), tc.msg) + } } } @@ -270,8 +285,8 @@ func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) } -type TestCase = struct { - fun func() error - expected bool - errMsg string +type testCase = struct { + fun func() error + expectPass bool + msg string } diff --git a/x/ibc/04-channel/types/codec.go b/x/ibc/04-channel/types/codec.go index 04dcbc07ca4f..2bb785418b67 100644 --- a/x/ibc/04-channel/types/codec.go +++ b/x/ibc/04-channel/types/codec.go @@ -2,7 +2,9 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // SubModuleCdc defines the IBC channel codec. @@ -10,6 +12,8 @@ var SubModuleCdc *codec.Codec func init() { SubModuleCdc = codec.New() + commitment.RegisterCodec(SubModuleCdc) + client.RegisterCodec(SubModuleCdc) RegisterCodec(SubModuleCdc) } @@ -31,6 +35,7 @@ func RegisterCodec(cdc *codec.Codec) { SetSubModuleCodec(cdc) } +// SetSubModuleCodec sets the ibc channel codec func SetSubModuleCodec(cdc *codec.Codec) { SubModuleCdc = cdc } diff --git a/x/ibc/07-tendermint/codec.go b/x/ibc/07-tendermint/codec.go index 03a5b8bc31e4..f8f3c9ff79e0 100644 --- a/x/ibc/07-tendermint/codec.go +++ b/x/ibc/07-tendermint/codec.go @@ -12,8 +12,10 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) cdc.RegisterConcrete(Evidence{}, "ibc/client/tendermint/Evidence", nil) + + SetSubModuleCodec(cdc) } -func init() { - RegisterCodec(SubModuleCdc) +func SetSubModuleCodec(cdc *codec.Codec) { + SubModuleCdc = cdc } diff --git a/x/ibc/20-transfer/handler_test.go b/x/ibc/20-transfer/handler_test.go index f1c1ecf4ed11..f369d0fd5595 100644 --- a/x/ibc/20-transfer/handler_test.go +++ b/x/ibc/20-transfer/handler_test.go @@ -13,7 +13,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" @@ -34,7 +36,7 @@ const ( testChannel1 = "firstchannel" testChannel2 = "secondchannel" - testChannelOrder = channel.UNORDERED + testChannelOrder = channelexported.UNORDERED testChannelVersion = "1.0" ) @@ -71,7 +73,7 @@ func (suite *HandlerTestSuite) SetupTest() { suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.createClient() - suite.createConnection(connection.OPEN) + suite.createConnection(connectionexported.OPEN) } func (suite *HandlerTestSuite) createClient() { @@ -82,11 +84,8 @@ func (suite *HandlerTestSuite) createClient() { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) consensusState := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ValidatorSetHash: suite.valSet.Hash(), } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) @@ -102,16 +101,13 @@ func (suite *HandlerTestSuite) updateClient() { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) state := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), + Root: commitment.NewRoot(commitID.Hash), } suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) - suite.app.IBCKeeper.ClientKeeper.SetVerifiedRoot(suite.ctx, testClient, state.GetHeight(), state.GetRoot()) } -func (suite *HandlerTestSuite) createConnection(state connection.State) { +func (suite *HandlerTestSuite) createConnection(state connectionexported.State) { connection := connection.ConnectionEnd{ State: state, ClientID: testClient, @@ -126,19 +122,20 @@ func (suite *HandlerTestSuite) createConnection(state connection.State) { suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnection, connection) } -func (suite *HandlerTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state channel.State) { +func (suite *HandlerTestSuite) createChannel( + portID, channnelID, connnnectionID, counterpartyPortID, counterpartyChannelID string, state channelexported.State) { ch := channel.Channel{ State: state, Ordering: testChannelOrder, Counterparty: channel.Counterparty{ - PortID: counterpartyPort, - ChannelID: counterpartyChan, + PortID: counterpartyPortID, + ChannelID: counterpartyChannelID, }, - ConnectionHops: []string{connID}, + ConnectionHops: []string{connnnectionID}, Version: testChannelVersion, } - suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, portID, chanID, ch) + suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, portID, channnelID, ch) } func (suite *HandlerTestSuite) queryProof(key []byte) (proof commitment.Proof, height int64) { @@ -166,7 +163,7 @@ func (suite *HandlerTestSuite) TestHandleMsgTransfer() { suite.Require().Error(err) suite.Require().Nil(res, "%+v", res) // channel does not exist - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, channel.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, channelexported.OPEN) res, err = handler(suite.ctx, msg) suite.Require().Error(err) suite.Require().Nil(res, "%+v", res) // next send sequence not found diff --git a/x/ibc/20-transfer/keeper/keeper_test.go b/x/ibc/20-transfer/keeper/keeper_test.go index b4d4132c7109..af4ea22a8e64 100644 --- a/x/ibc/20-transfer/keeper/keeper_test.go +++ b/x/ibc/20-transfer/keeper/keeper_test.go @@ -13,8 +13,8 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" - channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" ) @@ -30,7 +30,7 @@ const ( testChannel1 = "firstchannel" testChannel2 = "secondchannel" - testChannelOrder = channel.UNORDERED + testChannelOrder = channelexported.UNORDERED testChannelVersion = "1.0" ) @@ -67,7 +67,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.createClient() - suite.createConnection(connection.OPEN) + suite.createConnection(connectionexported.OPEN) } func (suite *KeeperTestSuite) TestGetTransferAccount() { diff --git a/x/ibc/20-transfer/keeper/relay_test.go b/x/ibc/20-transfer/keeper/relay_test.go index 2a3da8ddb105..4bf615931571 100644 --- a/x/ibc/20-transfer/keeper/relay_test.go +++ b/x/ibc/20-transfer/keeper/relay_test.go @@ -7,7 +7,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -23,11 +25,8 @@ func (suite *KeeperTestSuite) createClient() { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) consensusState := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), Root: commitment.NewRoot(commitID.Hash), - ValidatorSet: suite.valSet, - NextValidatorSet: suite.valSet, + ValidatorSetHash: suite.valSet.Hash(), } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) @@ -43,16 +42,13 @@ func (suite *KeeperTestSuite) updateClient() { suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) state := tendermint.ConsensusState{ - ChainID: testChainID, - Height: uint64(commitID.Version), - Root: commitment.NewRoot(commitID.Hash), + Root: commitment.NewRoot(commitID.Hash), } suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) - suite.app.IBCKeeper.ClientKeeper.SetVerifiedRoot(suite.ctx, testClient, state.GetHeight(), state.GetRoot()) } -func (suite *KeeperTestSuite) createConnection(state connection.State) { +func (suite *KeeperTestSuite) createConnection(state connectionexported.State) { connection := connection.ConnectionEnd{ State: state, ClientID: testClient, @@ -67,7 +63,7 @@ func (suite *KeeperTestSuite) createConnection(state connection.State) { suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnection, connection) } -func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state channel.State) { +func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state channelexported.State) { ch := channel.Channel{ State: state, Ordering: testChannelOrder, @@ -104,7 +100,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { err := suite.app.TransferKeeper.SendTransfer(suite.ctx, testPort1, testChannel1, testCoins, testAddr1, testAddr2, isSourceChain) suite.Error(err) // channel does not exist - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, channel.OPEN) + suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, channelexported.OPEN) err = suite.app.TransferKeeper.SendTransfer(suite.ctx, testPort1, testChannel1, testCoins, testAddr1, testAddr2, isSourceChain) suite.Error(err) // next send sequence not found diff --git a/x/ibc/20-transfer/types/codec.go b/x/ibc/20-transfer/types/codec.go index 1b9680c8da3d..ff87c7fcfeaf 100644 --- a/x/ibc/20-transfer/types/codec.go +++ b/x/ibc/20-transfer/types/codec.go @@ -6,13 +6,15 @@ import ( commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +// ModuleCdc defines the IBC transfer codec. +var ModuleCdc = codec.New() + +// RegisterCodec registers the IBC transfer types func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgTransfer{}, "ibc/transfer/MsgTransfer", nil) cdc.RegisterConcrete(PacketDataTransfer{}, "ibc/transfer/PacketDataTransfer", nil) } -var ModuleCdc = codec.New() - func init() { RegisterCodec(ModuleCdc) channel.RegisterCodec(ModuleCdc) diff --git a/x/ibc/keeper/querier_test.go b/x/ibc/keeper/querier_test.go index f15920608b6c..b8093bf9dd9d 100644 --- a/x/ibc/keeper/querier_test.go +++ b/x/ibc/keeper/querier_test.go @@ -43,12 +43,6 @@ func (suite *KeeperTestSuite) TestNewQuerier() { false, "", }, - { - "client - QuerierVerifiedRoot", - []string{client.SubModuleName, client.QueryVerifiedRoot}, - false, - "", - }, { "client - invalid query", []string{client.SubModuleName, "foo"}, diff --git a/x/ibc/module.go b/x/ibc/module.go index 7c77a865796d..65f03954cd1d 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -16,6 +16,7 @@ import ( connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -43,6 +44,7 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { connection.RegisterCodec(cdc) channel.RegisterCodec(cdc) tendermint.RegisterCodec(cdc) + commitment.RegisterCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the ibc From 959a4f5d4d80441ccb882104893f96a28b3d7efd Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 20 Jan 2020 14:47:59 +0100 Subject: [PATCH 16/35] minor changes from code review --- x/ibc/02-client/client/cli/tx.go | 2 -- x/ibc/02-client/keeper/client.go | 1 - x/ibc/03-connection/keeper/handshake.go | 30 +++++++++++----------- x/ibc/03-connection/types/connection.go | 17 ++++++++++++- x/ibc/03-connection/types/errors.go | 1 + x/ibc/04-channel/keeper/handshake.go | 23 ++++++----------- x/ibc/04-channel/keeper/packet.go | 27 ++++++++++---------- x/ibc/04-channel/keeper/timeout.go | 33 +++++++++++++------------ x/ibc/04-channel/types/channel.go | 1 + x/ibc/07-tendermint/codec.go | 4 ++- 10 files changed, 75 insertions(+), 64 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 1e86de879a91..5dd8cd1b9c30 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "io/ioutil" - "os" "strings" "github.com/pkg/errors" @@ -135,7 +134,6 @@ $ %s tx ibc client misbehaviour [path/to/evidence.json] --from node0 --home ../n var ev evidenceexported.Evidence if err := cdc.UnmarshalJSON([]byte(args[0]), &ev); err != nil { - fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...") // check for file path if JSON input is not provided contents, err := ioutil.ReadFile(args[0]) if err != nil { diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 903bee326f24..1e4de2ee3879 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -143,5 +143,4 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex ) return nil - } diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 12fad65fa54e..9cab202cb410 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -30,8 +30,7 @@ func (k Keeper) ConnOpenInit( connection := types.NewConnectionEnd(exported.INIT, clientID, counterparty, types.GetCompatibleVersions()) k.SetConnection(ctx, connectionID, connection) - err := k.addConnectionToClient(ctx, clientID, connectionID) - if err != nil { + if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { return sdkerrors.Wrap(err, "cannot initialize connection") } @@ -79,11 +78,10 @@ func (k Keeper) ConnOpenTry( // connection defines chain B's ConnectionEnd connection := types.NewConnectionEnd(exported.UNINITIALIZED, clientID, counterparty, []string{version}) - err := k.VerifyConnectionState( + if err := k.VerifyConnectionState( ctx, proofHeight, proofInit, counterparty.ConnectionID, expectedConnection, expectedConsensusState, - ) - if err != nil { + ); err != nil { return err } @@ -95,14 +93,20 @@ func (k Keeper) ConnOpenTry( // return err // } - _, found = k.GetConnection(ctx, connectionID) + previousConnection, found := k.GetConnection(ctx, connectionID) if found { return sdkerrors.Wrap(types.ErrConnectionExists, "cannot relay connection attempt") + } else if !(previousConnection.State == exported.INIT && + previousConnection.Counterparty.ConnectionID == counterparty.ConnectionID && + previousConnection.Counterparty.Prefix == counterparty.Prefix && + previousConnection.ClientID == clientID && + previousConnection.Counterparty.ClientID == counterparty.ClientID && + previousConnection.Versions[0] == version) { + return sdkerrors.Wrap(types.ErrInvalidConnection, "cannot relay connection attempt") } connection.State = exported.TRYOPEN - err = k.addConnectionToClient(ctx, clientID, connectionID) - if err != nil { + if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { return sdkerrors.Wrap(err, "cannot relay connection attempt") } @@ -157,11 +161,10 @@ func (k Keeper) ConnOpenAck( expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) expectedConnection := types.NewConnectionEnd(exported.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version}) - err := k.VerifyConnectionState( + if err := k.VerifyConnectionState( ctx, proofHeight, proofTry, connection.Counterparty.ConnectionID, expectedConnection, expectedConsensusState, - ) - if err != nil { + ); err != nil { return err } @@ -213,11 +216,10 @@ func (k Keeper) ConnOpenConfirm( expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix) expectedConnection := types.NewConnectionEnd(exported.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions) - err := k.VerifyConnectionState( + if err := k.VerifyConnectionState( ctx, proofHeight, proofAck, connection.Counterparty.ConnectionID, expectedConnection, expectedConsensusState, - ) - if err != nil { + ); err != nil { return err } diff --git a/x/ibc/03-connection/types/connection.go b/x/ibc/03-connection/types/connection.go index 880f43f45bae..6f5104427500 100644 --- a/x/ibc/03-connection/types/connection.go +++ b/x/ibc/03-connection/types/connection.go @@ -1,10 +1,13 @@ package types import ( + "strings" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) var _ exported.ConnectionI = ConnectionEnd{} @@ -58,7 +61,19 @@ func (c ConnectionEnd) GetVersions() []string { // ValidateBasic implements the Connection interface func (c ConnectionEnd) ValidateBasic() error { - // TODO: move validations from msg.ValidateBasic() + // NOTE: invalid state is considered "UNINITIALIZED" + + if err := host.DefaultClientIdentifierValidator(c.ClientID); err != nil { + return sdkerrors.Wrapf(err, "invalid client ID: %s", c.ClientID) + } + if len(c.Versions) == 0 { + return sdkerrors.Wrap(ibctypes.ErrInvalidVersion, "missing connection versions") + } + for _, version := range c.Versions { + if strings.TrimSpace(version) == "" { + return sdkerrors.Wrap(ibctypes.ErrInvalidVersion, "version can't be blank") + } + } return c.Counterparty.ValidateBasic() } diff --git a/x/ibc/03-connection/types/errors.go b/x/ibc/03-connection/types/errors.go index 41199efc4103..a738f9956762 100644 --- a/x/ibc/03-connection/types/errors.go +++ b/x/ibc/03-connection/types/errors.go @@ -12,4 +12,5 @@ var ( ErrConnectionPath = sdkerrors.Register(SubModuleName, 4, "connection path is not associated to the given light client") ErrInvalidConnectionState = sdkerrors.Register(SubModuleName, 5, "invalid connection state") ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 6, "invalid counterparty connection") + ErrInvalidConnection = sdkerrors.Register(SubModuleName, 7, "invalid connection") ) diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index 4c1487a47f36..4fa918a984b8 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -139,11 +139,10 @@ func (k Keeper) ChanOpenTry( return clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyChannelState( + if err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofInit, counterparty.PortID, counterparty.ChannelID, expectedChannel, consensusState, - ) - if err != nil { + ); err != nil { return sdkerrors.Wrap(err, "channel membership verification failed") } @@ -218,13 +217,11 @@ func (k Keeper) ChanOpenAck( return clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyChannelState( + if err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofTry, channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, - ) - - if err != nil { + ); err != nil { return sdkerrors.Wrap(err, "channel membership verification failed") } @@ -298,13 +295,11 @@ func (k Keeper) ChanOpenConfirm( return clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyChannelState( + if err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofAck, channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, - ) - - if err != nil { + ); err != nil { return sdkerrors.Wrap(err, "channel membership verification failed") } @@ -424,13 +419,11 @@ func (k Keeper) ChanCloseConfirm( return clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyChannelState( + if err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofInit, channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, - ) - - if err != nil { + ); err != nil { return sdkerrors.Wrap(err, "channel membership verification failed") } diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index 497fefb450e0..091038cc51ca 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -39,16 +39,17 @@ func (k Keeper) SendPacket( ) } - capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return types.ErrChannelCapabilityNotFound - } + // TODO: uncomment when channel capability key is set to store + // capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + // if !found { + // return types.ErrChannelCapabilityNotFound + // } - portCapabilityKey := sdk.NewKVStoreKey(capKey) + // portCapabilityKey := sdk.NewKVStoreKey(capKey) - if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { - return sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) - } + // if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { + // return sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) + // } if packet.GetDestPort() != channel.Counterparty.PortID { return sdkerrors.Wrapf( @@ -169,12 +170,11 @@ func (k Keeper) RecvPacket( return nil, clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyPacketCommitment( + if err := k.connectionKeeper.VerifyPacketCommitment( ctx, connectionEnd, proofHeight, proof, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), types.CommitPacket(packet.GetData()), consensusState, - ) - if err != nil { + ); err != nil { return nil, sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") } @@ -300,11 +300,10 @@ func (k Keeper) AcknowledgePacket( return nil, clienttypes.ErrConsensusStateNotFound } - err := k.connectionKeeper.VerifyPacketAcknowledgement( + if err := k.connectionKeeper.VerifyPacketAcknowledgement( ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), acknowledgement.GetBytes(), consensusState, - ) - if err != nil { + ); err != nil { return nil, sdkerrors.Wrap(err, "invalid acknowledgement on counterparty chain") } diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index 927f40eb5f02..1c1e47905ba7 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -9,7 +9,6 @@ import ( connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -124,10 +123,11 @@ func (k Keeper) TimeoutExecuted(ctx sdk.Context, packet exported.PacketI) error } // check if the packet is linked to a capability key - _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return types.ErrChannelCapabilityNotFound - } + // TODO: uncomment + // _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + // if !found { + // return types.ErrChannelCapabilityNotFound + // } k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) @@ -155,16 +155,17 @@ func (k Keeper) TimeoutOnClose( return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) } - capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return nil, types.ErrChannelCapabilityNotFound - } + // TODO: uncomment + // capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + // if !found { + // return nil, types.ErrChannelCapabilityNotFound + // } - portCapabilityKey := sdk.NewKVStoreKey(capKey) + // portCapabilityKey := sdk.NewKVStoreKey(capKey) - if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { - return nil, sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) - } + // if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { + // return nil, sdkerrors.Wrap(port.ErrInvalidPort, packet.GetSourcePort()) + // } if packet.GetDestPort() != channel.Counterparty.PortID { return nil, sdkerrors.Wrapf( @@ -211,15 +212,15 @@ func (k Keeper) TimeoutOnClose( } // check that the opposing channel end has closed - err := k.connectionKeeper.VerifyChannelState( + if err := k.connectionKeeper.VerifyChannelState( ctx, connectionEnd, proofHeight, proofClosed, channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, - ) - if err != nil { + ); err != nil { return nil, sdkerrors.Wrap(err, "channel membership verification failed") } + var err error switch channel.Ordering { case exported.ORDERED: // check that the recv sequence is as claimed diff --git a/x/ibc/04-channel/types/channel.go b/x/ibc/04-channel/types/channel.go index ebb026bc86ef..c82456aa5063 100644 --- a/x/ibc/04-channel/types/channel.go +++ b/x/ibc/04-channel/types/channel.go @@ -59,6 +59,7 @@ func (ch Channel) GetVersion() string { // ValidateBasic performs a basic validation of the channel fields func (ch Channel) ValidateBasic() error { + // TODO: update channel and ordering validation if ch.State.String() == "" { return sdkerrors.Wrap(ErrInvalidChannel, ErrInvalidChannelState.Error()) } diff --git a/x/ibc/07-tendermint/codec.go b/x/ibc/07-tendermint/codec.go index f8f3c9ff79e0..22c6d508ed6a 100644 --- a/x/ibc/07-tendermint/codec.go +++ b/x/ibc/07-tendermint/codec.go @@ -4,7 +4,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -var SubModuleCdc = codec.New() +// SubModuleCdc defines the IBC tendermint client codec. +var SubModuleCdc *codec.Codec // RegisterCodec registers the Tendermint types func RegisterCodec(cdc *codec.Codec) { @@ -16,6 +17,7 @@ func RegisterCodec(cdc *codec.Codec) { SetSubModuleCodec(cdc) } +// SetSubModuleCodec sets the ibc tendermint client codec func SetSubModuleCodec(cdc *codec.Codec) { SubModuleCdc = cdc } From dd899bc3f93cb57c83577a97f340e884367e267c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 20 Jan 2020 15:19:22 +0100 Subject: [PATCH 17/35] ibc/client/types: fix tests --- x/ibc/02-client/alias.go | 1 - x/ibc/02-client/types/keys.go | 3 --- x/ibc/02-client/types/msgs_test.go | 38 ++++++++++++------------------ 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index 3b76fb13c58c..20b946cf4a4f 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -15,7 +15,6 @@ const ( AttributeKeyClientID = types.AttributeKeyClientID AttrbuteKeyClientType = types.AttributeKeyClientType SubModuleName = types.SubModuleName - StoreKey = types.StoreKey RouterKey = types.RouterKey QuerierRoute = types.QuerierRoute QueryAllClients = types.QueryAllClients diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index 850e26a6f340..f360393335d5 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -4,9 +4,6 @@ const ( // SubModuleName defines the IBC client name SubModuleName string = "client" - // StoreKey is the store key string for IBC client - StoreKey string = SubModuleName - // RouterKey is the message route for IBC client RouterKey string = SubModuleName diff --git a/x/ibc/02-client/types/msgs_test.go b/x/ibc/02-client/types/msgs_test.go index 09117a5f68e0..9c60553b14b4 100644 --- a/x/ibc/02-client/types/msgs_test.go +++ b/x/ibc/02-client/types/msgs_test.go @@ -11,30 +11,28 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func TestMsgCreateClientValidateBasic(t *testing.T) { - cs := tendermint.ConsensusState{} + cs := tendermint.ConsensusState{ + Root: commitment.NewRoot([]byte("root")), + ValidatorSetHash: []byte("hash"), + } privKey := secp256k1.GenPrivKey() signer := sdk.AccAddress(privKey.PubKey().Address()) - testMsgs := []types.MsgCreateClient{ - types.NewMsgCreateClient(exported.ClientTypeTendermint, exported.ClientTypeTendermint, cs, signer), // valid msg - types.NewMsgCreateClient("badClient", exported.ClientTypeTendermint, cs, signer), // invalid client id - types.NewMsgCreateClient("goodChain", "bad_type", cs, signer), // invalid client type - types.NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, nil, signer), // nil Consensus State - types.NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, cs, sdk.AccAddress{}), // empty signer - } cases := []struct { msg types.MsgCreateClient expPass bool errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "invalid client id passed"}, - {testMsgs[2], false, "unregistered client type passed"}, - {testMsgs[3], false, "Nil Consensus State in msg passed"}, - {testMsgs[4], false, "Empty address passed"}, + {types.NewMsgCreateClient(exported.ClientTypeTendermint, exported.ClientTypeTendermint, cs, signer), true, "success msg should pass"}, + {types.NewMsgCreateClient("BADCHAIN", exported.ClientTypeTendermint, cs, signer), false, "invalid client id passed"}, + {types.NewMsgCreateClient("goodchain", "invalid_client_type", cs, signer), false, "unregistered client type passed"}, + {types.NewMsgCreateClient("goodchain", exported.ClientTypeTendermint, nil, signer), false, "nil Consensus State in msg passed"}, + {types.NewMsgCreateClient("goodchain", exported.ClientTypeTendermint, tendermint.ConsensusState{}, signer), false, "invalid Consensus State in msg passed"}, + {types.NewMsgCreateClient("goodchain", exported.ClientTypeTendermint, cs, nil), false, "Empty address passed"}, } for i, tc := range cases { @@ -50,22 +48,16 @@ func TestMsgCreateClientValidateBasic(t *testing.T) { func TestMsgUpdateClient(t *testing.T) { privKey := secp256k1.GenPrivKey() signer := sdk.AccAddress(privKey.PubKey().Address()) - testMsgs := []types.MsgUpdateClient{ - types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, signer), // valid msg - types.NewMsgUpdateClient("badClient", tendermint.Header{}, signer), // bad client id - types.NewMsgUpdateClient(exported.ClientTypeTendermint, nil, signer), // nil Header - types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, sdk.AccAddress{}), // empty address - } cases := []struct { msg types.MsgUpdateClient expPass bool errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "invalid client id passed"}, - {testMsgs[2], false, "Nil Header passed"}, - {testMsgs[3], false, "Empty address passed"}, + {types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, signer), true, "success msg should pass"}, + {types.NewMsgUpdateClient("badClient", tendermint.Header{}, signer), false, "invalid client id passed"}, + {types.NewMsgUpdateClient(exported.ClientTypeTendermint, nil, signer), false, "Nil Header passed"}, + {types.NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, nil), false, "Empty address passed"}, } for i, tc := range cases { From f285fa1b79c985a963c2eec487b62b5c2098039b Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 20 Jan 2020 18:18:30 +0100 Subject: [PATCH 18/35] ibc/02-client/keeper: fix tests --- x/ibc/02-client/keeper/client.go | 6 +- x/ibc/02-client/keeper/client_test.go | 266 +++++++++++++---------- x/ibc/02-client/keeper/keeper_test.go | 35 ++- x/ibc/02-client/types/msgs_test.go | 8 +- x/ibc/07-tendermint/evidence_test.go | 16 +- x/ibc/07-tendermint/misbehaviour.go | 11 +- x/ibc/07-tendermint/misbehaviour_test.go | 16 +- x/ibc/07-tendermint/tendermint_test.go | 2 +- x/ibc/07-tendermint/test_utils.go | 4 +- x/ibc/07-tendermint/update.go | 6 +- 10 files changed, 209 insertions(+), 161 deletions(-) diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 1e4de2ee3879..3e21a9c2543e 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -23,7 +23,7 @@ func (k Keeper) CreateClient( _, found = k.GetClientType(ctx, clientID) if found { - panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) + panic(fmt.Sprintf("client type is already defined for client %s", clientID)) } clientState, err := k.initialize(ctx, clientID, clientType, consensusState) @@ -121,7 +121,9 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex var err error switch e := misbehaviour.(type) { case tendermint.Evidence: - clientState, err = tendermint.CheckMisbehaviourAndUpdateState(clientState, consensusState, misbehaviour, uint64(misbehaviour.GetHeight())) + clientState, err = tendermint.CheckMisbehaviourAndUpdateState( + clientState, consensusState, misbehaviour, uint64(misbehaviour.GetHeight()), + ) default: err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC client evidence type: %T", e) diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index 51ec4765ac96..421a1781cc90 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -4,103 +4,122 @@ import ( "bytes" "fmt" - "github.com/stretchr/testify/require" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + tmtypes "github.com/tendermint/tendermint/types" ) const ( - testClientType exported.ClientType = iota + 2 + invalidClientType exported.ClientType = iota + 5 ) func (suite *KeeperTestSuite) TestCreateClient() { - // Test Valid CreateClient - state, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) - suite.NoError(err, "CreateClient failed") + type params struct { + clientID string + clientType exported.ClientType + } - // Test ClientState stored correctly - expectedState := tendermint.NewClientState(testClientID) - require.Equal(suite.T(), expectedState, state, "Incorrect ClientState returned") + suite.keeper.SetClientType(suite.ctx, testClientID2, exported.Tendermint) - // Test ClientType and VerifiedRoot stored correctly - clientType, _ := suite.keeper.GetClientType(suite.ctx, testClientID) - require.Equal(suite.T(), exported.Tendermint, clientType, "Incorrect ClientType stored") + cases := []struct { + msg string + params params + expPass bool + expPanic bool + }{ + {"sucess", params{testClientID, exported.Tendermint}, true, false}, + {"client ID exists", params{testClientID, exported.Tendermint}, false, false}, + {"client type exists", params{testClientID2, exported.Tendermint}, false, true}, + {"invalid client type", params{testClientID3, invalidClientType}, false, false}, + } + + for i, tc := range cases { + tc := tc + + if tc.expPanic { + suite.Require().Panics(func() { + suite.keeper.CreateClient(suite.ctx, tc.params.clientID, tc.params.clientType, suite.consensusState) + }, "Msg %d didn't panic: %s", i, tc.msg) + } else { + clientState, err := suite.keeper.CreateClient(suite.ctx, tc.params.clientID, tc.params.clientType, suite.consensusState) - // Test that trying to CreateClient on existing client fails - _, err = suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) - suite.Error(err, "CreateClient on existing client: %s passed", testClientID) + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.msg) + } + } + } } func (suite *KeeperTestSuite) TestUpdateClient() { - privVal := tmtypes.NewMockPV() - validator := tmtypes.NewValidator(privVal.GetPubKey(), 1) - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - altSigners := []tmtypes.PrivValidator{privVal} - - // Test invalid cases all fail and do not update state cases := []struct { name string - malleate func() - expErr bool + malleate func() error + expPass bool }{ - {"valid update", func() {}, false}, - {"wrong client type", func() { - suite.keeper.SetClientType(suite.ctx, testClientID, testClientType) + {"valid update", func() error { + _, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) + return err }, true}, - {"frozen client", func() { - clientState, _ := suite.keeper.GetClientState(suite.ctx, testClientID) + {"client type not found", func() error { + return nil + }, false}, + {"client type and header type mismatch", func() error { + suite.keeper.SetClientType(suite.ctx, testClientID, invalidClientType) + return nil + }, false}, + {"client state not found", func() error { + suite.keeper.SetClientType(suite.ctx, testClientID, exported.Tendermint) + return nil + }, false}, + {"frozen client", func() error { + clientState := tendermint.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10} suite.keeper.SetClientState(suite.ctx, clientState) - }, true}, - {"past height", func() { - suite.header = tendermint.MakeHeader("gaia", 2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) - }, true}, - {"validatorHash incorrect", func() { - suite.header = tendermint.MakeHeader("gaia", 4, altValSet, suite.valSet, altSigners) - }, true}, - {"header fails validateBasic", func() { - suite.header.ChainID = "test" - }, true}, - {"verify future commit fails", func() { - suite.consensusState.NextValidatorSet = altValSet - suite.keeper.SetConsensusState(suite.ctx, testClientID, suite.consensusState) - }, true}, + suite.keeper.SetClientType(suite.ctx, testClientID, exported.Tendermint) + return nil + }, false}, + {"invalid header", func() error { + _, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) + if err != nil { + return err + } + suite.header.Height = suite.ctx.BlockHeight() - 1 + return nil + }, false}, } - for _, tc := range cases { - tc := tc // pin for scopelint + for i, tc := range cases { + tc := tc suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - // Reset suite on each subtest suite.SetupTest() - _, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) - suite.NoError(err, "CreateClient failed") + err := tc.malleate() + suite.Require().NoError(err) - tc.malleate() err = suite.keeper.UpdateClient(suite.ctx, testClientID, suite.header) - retrievedConsState, _ := suite.keeper.GetConsensusState(suite.ctx, testClientID) - tmConsState, _ := retrievedConsState.(tendermint.ConsensusState) - tmConsState.ValidatorSet.TotalVotingPower() - tmConsState.NextValidatorSet.TotalVotingPower() - if tc.expErr { - suite.Error(err, "Invalid UpdateClient passed", tc.name) + if tc.expPass { + expConsensusState := tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.header.ValidatorSet.Hash(), + } - // require no state changes occurred - require.Equal(suite.T(), suite.consensusState, tmConsState, "Consensus state changed after invalid UpdateClient") - require.Nil(suite.T(), retrievedRoot, "Root added for new height after invalid UpdateClient") - } else { - suite.NoError(err, "Valid UpdateClient failed", tc.name) + clientState, found := suite.keeper.GetClientState(suite.ctx, testClientID) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) - // require state changes were performed correctly - require.Equal(suite.T(), suite.header.GetHeight(), retrievedConsState.GetHeight(), "height not updated correctly") - require.Equal(suite.T(), commitment.NewRoot(suite.header.AppHash), retrievedConsState.GetRoot(), "root not updated correctly") - require.Equal(suite.T(), suite.header.NextValidatorSet, tmConsState.NextValidatorSet, "NextValidatorSet not updated correctly") + consensusState, found := suite.keeper.GetConsensusState(suite.ctx, testClientID, suite.header.GetHeight()) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(suite.header.GetHeight(), clientState.GetSequence(), "client state height not updated correctly") + suite.Require().Equal(expConsensusState, consensusState, "consensus state should have been updated") + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) } - }) } } @@ -124,77 +143,100 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { altSigners := []tmtypes.PrivValidator{altPrivVal} - _, err := suite.keeper.CreateClient(suite.ctx, "gaiamainnet", exported.Tendermint, suite.consensusState) - suite.NoError(err, "CreateClient failed") - testCases := []struct { name string - evidence *tendermint.Evidence - clientID string - expErr bool + evidence tendermint.Evidence + malleate func() error + expPass bool }{ { "trusting period misbehavior should pass", - &tendermint.Evidence{ - Header1: tendermint.MakeHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), - Header2: tendermint.MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: testClientID, + ClientID: testClientID, + }, + func() error { + suite.consensusState.ValidatorSetHash = bothValSet.Hash() + _, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) + return err }, - "gaiamainnet", + true, + }, + { + "client state not found", + tendermint.Evidence{}, + func() error { return nil }, false, }, { - "first valset has too much change", - &tendermint.Evidence{ - Header1: tendermint.MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - Header2: tendermint.MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", + "consensus state not found", + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners), + ChainID: testClientID, + ClientID: testClientID, }, - "gaiamainnet", - true, + func() error { + clientState := tendermint.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10} + suite.keeper.SetClientState(suite.ctx, clientState) + return nil + }, + false, }, { - "second valset has too much change", - &tendermint.Evidence{ - Header1: tendermint.MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - Header2: tendermint.MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", + "consensus state not found", + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners), + ChainID: testClientID, + ClientID: testClientID, }, - "gaiamainnet", - true, + func() error { + clientState := tendermint.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10} + suite.keeper.SetClientState(suite.ctx, clientState) + return nil + }, + false, }, { - "both valsets have too much change", - &tendermint.Evidence{ - Header1: tendermint.MakeHeader("gaia", 5, altValSet, altValSet, altSigners), - Header2: tendermint.MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", + "misbehaviour check failed", + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners), + Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, altValSet, bothValSet, altSigners), + FromValidatorSet: bothValSet, + ChainID: testClientID, + ClientID: testClientID, }, - "gaiamainnet", - true, + func() error { + _, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState) + return err + }, + false, }, } - for _, tc := range testCases { - tc := tc // pin for scopelint + for i, tc := range testCases { + tc := tc suite.Run(tc.name, func() { - misbehaviour := tendermint.Evidence{ - tc.evidence, - ClientID: tc.clientID, - } + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, tc.evidence) - err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, misbehaviour) + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - if tc.expErr { - suite.Error(err, "CheckMisbehaviour passed unexpectedly") + clientState, found := suite.keeper.GetClientState(suite.ctx, testClientID) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + suite.Require().True(clientState.IsFrozen(), "valid test case %d failed: %s", i, tc.name) } else { - suite.NoError(err, "CheckMisbehaviour failed unexpectedly: %v", err) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) } - - // reset Frozen flag to false - clientState, _ := suite.keeper.GetClientState(suite.ctx, tc.clientID) - clientState.Frozen = false - suite.keeper.SetClientState(suite.ctx, clientState) }) } } diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 870ef0ba55fc..9a2dc723b4f9 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -22,6 +22,8 @@ const ( testClientID = "gaia" testClientID2 = "ethbridge" testClientID3 = "ethermint" + + testClientHeight = 5 ) type KeeperTestSuite struct { @@ -41,7 +43,7 @@ func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(isCheckTx) suite.cdc = app.Codec() - suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{}) + suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{Height: testClientHeight, ChainID: testClientID}) suite.keeper = &app.IBCKeeper.ClientKeeper suite.privVal = tmtypes.NewMockPV() @@ -49,7 +51,7 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(suite.privVal.GetPubKey(), 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - suite.header = tendermint.MakeHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = tendermint.CreateTestHeader(testClientID, testClientHeight, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.consensusState = tendermint.ConsensusState{ Root: commitment.NewRoot([]byte("hash")), @@ -62,40 +64,37 @@ func TestKeeperTestSuite(t *testing.T) { } func (suite *KeeperTestSuite) TestSetClientState() { - clientState := tendermint.NewClientState(testClientID) + clientState := tendermint.NewClientState(testClientID, testClientHeight) suite.keeper.SetClientState(suite.ctx, clientState) - retrievedState, ok := suite.keeper.GetClientState(suite.ctx, testClientID) - require.True(suite.T(), ok, "GetClientState failed") - require.Equal(suite.T(), clientState, retrievedState, "Client states are not equal") + retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID) + suite.Require().True(found, "GetClientState failed") + suite.Require().Equal(clientState, retrievedState, "Client states are not equal") } func (suite *KeeperTestSuite) TestSetClientType() { suite.keeper.SetClientType(suite.ctx, testClientID, exported.Tendermint) - clientType, ok := suite.keeper.GetClientType(suite.ctx, testClientID) + clientType, found := suite.keeper.GetClientType(suite.ctx, testClientID) - require.True(suite.T(), ok, "GetClientType failed") - require.Equal(suite.T(), exported.Tendermint, clientType, "ClientTypes not stored correctly") + suite.Require().True(found, "GetClientType failed") + suite.Require().Equal(exported.Tendermint, clientType, "ClientTypes not stored correctly") } func (suite *KeeperTestSuite) TestSetConsensusState() { - suite.keeper.SetConsensusState(suite.ctx, testClientID, suite.consensusState) + suite.keeper.SetConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) - retrievedConsState, ok := suite.keeper.GetConsensusState(suite.ctx, testClientID) + retrievedConsState, found := suite.keeper.GetConsensusState(suite.ctx, testClientID, testClientHeight) - require.True(suite.T(), ok, "GetConsensusState failed") + suite.Require().True(found, "GetConsensusState failed") tmConsState, _ := retrievedConsState.(tendermint.ConsensusState) - // force recalculation of unexported totalVotingPower so we can compare consensusState - tmConsState.ValidatorSet.TotalVotingPower() - tmConsState.NextValidatorSet.TotalVotingPower() require.Equal(suite.T(), suite.consensusState, tmConsState, "ConsensusState not stored correctly") } func (suite KeeperTestSuite) TestGetAllClients() { expClients := []exported.ClientState{ - tendermint.NewClientState(testClientID2), - tendermint.NewClientState(testClientID3), - tendermint.NewClientState(testClientID), + tendermint.NewClientState(testClientID2, testClientHeight), + tendermint.NewClientState(testClientID3, testClientHeight), + tendermint.NewClientState(testClientID, testClientHeight), } for i := range expClients { diff --git a/x/ibc/02-client/types/msgs_test.go b/x/ibc/02-client/types/msgs_test.go index 9c60553b14b4..c1c177a3db0b 100644 --- a/x/ibc/02-client/types/msgs_test.go +++ b/x/ibc/02-client/types/msgs_test.go @@ -38,9 +38,9 @@ func TestMsgCreateClientValidateBasic(t *testing.T) { for i, tc := range cases { err := tc.msg.ValidateBasic() if tc.expPass { - require.Nil(t, err, "Msg %d failed: %v", i, err) + require.NoError(t, err, "Msg %d failed: %v", i, err) } else { - require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + require.Error(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) } } } @@ -63,9 +63,9 @@ func TestMsgUpdateClient(t *testing.T) { for i, tc := range cases { err := tc.msg.ValidateBasic() if tc.expPass { - require.Nil(t, err, "Msg %d failed: %v", i, err) + require.NoError(t, err, "Msg %d failed: %v", i, err) } else { - require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + require.Error(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) } } } diff --git a/x/ibc/07-tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go index 68ae61c59b25..b7811847fd4a 100644 --- a/x/ibc/07-tendermint/evidence_test.go +++ b/x/ibc/07-tendermint/evidence_test.go @@ -37,7 +37,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "valid evidence", Evidence{ Header1: suite.header, - Header2: MakeHeader("gaia", 4, suite.valSet, bothValSet, signers), + Header2: CreateTestHeader("gaia", 4, suite.valSet, bothValSet, signers), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -48,7 +48,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "wrong chainID on header1", Evidence{ Header1: suite.header, - Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), + Header2: CreateTestHeader("ethermint", 4, suite.valSet, bothValSet, signers), ChainID: "ethermint", ClientID: "gaiamainnet", }, @@ -59,7 +59,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "wrong chainID on header2", Evidence{ Header1: suite.header, - Header2: MakeHeader("ethermint", 4, suite.valSet, bothValSet, signers), + Header2: CreateTestHeader("ethermint", 4, suite.valSet, bothValSet, signers), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -70,7 +70,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "mismatched heights", Evidence{ Header1: suite.header, - Header2: MakeHeader("gaia", 6, suite.valSet, bothValSet, signers), + Header2: CreateTestHeader("gaia", 6, suite.valSet, bothValSet, signers), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -92,7 +92,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "header doesn't have 2/3 majority", Evidence{ Header1: suite.header, - Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), + Header2: CreateTestHeader("gaia", 4, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -111,7 +111,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "validators sign off on wrong commit", Evidence{ Header1: suite.header, - Header2: MakeHeader("gaia", 4, bothValSet, bothValSet, bothSigners), + Header2: CreateTestHeader("gaia", 4, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -123,8 +123,8 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "invalid ClientID", Evidence{ - Header1: MakeHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), - Header2: MakeHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), + Header1: CreateTestHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), + Header2: CreateTestHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), ChainID: "gaia123?", ClientID: "gaia123?", }, diff --git a/x/ibc/07-tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go index f8a2ac7b2c16..15eb2b935c6a 100644 --- a/x/ibc/07-tendermint/misbehaviour.go +++ b/x/ibc/07-tendermint/misbehaviour.go @@ -2,6 +2,8 @@ package tendermint import ( "bytes" + "errors" + "fmt" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" @@ -43,7 +45,7 @@ func CheckMisbehaviourAndUpdateState( tmClientState.FrozenHeight = uint64(tmEvidence.GetHeight()) - return clientState, nil + return tmClientState, nil } // checkMisbehaviour checks if the evidence provided is a valid light client misbehaviour @@ -62,8 +64,7 @@ func checkMisbehaviour( } if !bytes.Equal(consensusState.ValidatorSetHash, evidence.FromValidatorSet.Hash()) { - return sdkerrors.Wrap( - clienttypes.ErrInvalidEvidence, + return errors.New( "the consensus state's validator set hash doesn't match the evidence's one", ) } @@ -75,14 +76,14 @@ func checkMisbehaviour( evidence.Header1.ValidatorSet, evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit, ); err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err) + return fmt.Errorf("validator set in header 1 has too much change from last known validator set: %v", err) } if err := evidence.FromValidatorSet.VerifyFutureCommit( evidence.Header2.ValidatorSet, evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit, ); err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err) + return fmt.Errorf("validator set in header 2 has too much change from last known validator set: %v", err) } return nil diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index fa5980065da1..7f929e805179 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -38,8 +38,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { ClientState{}, ConsensusState{}, Evidence{ - Header1: MakeHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), - Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + Header1: CreateTestHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), + Header2: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -51,8 +51,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { ClientState{}, ConsensusState{}, Evidence{ - Header1: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), - Header2: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + Header1: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), + Header2: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -64,8 +64,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { ClientState{}, ConsensusState{}, Evidence{ - Header1: MakeHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), + Header1: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), + Header2: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, @@ -77,8 +77,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { ClientState{}, ConsensusState{}, Evidence{ - Header1: MakeHeader("gaia", 5, altValSet, altValSet, altSigners), - Header2: MakeHeader("gaia", 5, altValSet, bothValSet, altSigners), + Header1: CreateTestHeader("gaia", 5, altValSet, altValSet, altSigners), + Header2: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), ChainID: "gaia", ClientID: "gaiamainnet", }, diff --git a/x/ibc/07-tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go index 64ff8fb1c69a..3b39b7c09d5c 100644 --- a/x/ibc/07-tendermint/tendermint_test.go +++ b/x/ibc/07-tendermint/tendermint_test.go @@ -20,7 +20,7 @@ func (suite *TendermintTestSuite) SetupTest() { suite.privVal = tmtypes.NewMockPV() val := tmtypes.NewValidator(suite.privVal.GetPubKey(), 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - suite.header = MakeHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = CreateTestHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } func TestTendermintTestSuite(t *testing.T) { diff --git a/x/ibc/07-tendermint/test_utils.go b/x/ibc/07-tendermint/test_utils.go index aeb96a69cf25..ce0df416a2a7 100644 --- a/x/ibc/07-tendermint/test_utils.go +++ b/x/ibc/07-tendermint/test_utils.go @@ -18,10 +18,10 @@ func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.Block Hash: partSetHash, }, } - } -func MakeHeader(chainID string, height int64, valSet *tmtypes.ValidatorSet, nextValSet *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) Header { +// CreateTestHeader creates a mock header for testing only. +func CreateTestHeader(chainID string, height int64, valSet *tmtypes.ValidatorSet, nextValSet *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) Header { vsetHash := valSet.Hash() nextHash := nextValSet.Hash() timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go index e69913305c5d..bf333d9694b9 100644 --- a/x/ibc/07-tendermint/update.go +++ b/x/ibc/07-tendermint/update.go @@ -8,7 +8,11 @@ import ( ) // CheckValidityAndUpdateState checks if the provided header is valid and updates -// the consensus state if appropriate +// the consensus state if appropriate. It returns an error if: +// - the client or header provided are not parseable to tendermint types +// - the header is invalid +// - header height is lower than the latest client height +// - header valset commit verification fails func CheckValidityAndUpdateState( clientState clientexported.ClientState, header clientexported.Header, chainID string, ) (clientexported.ClientState, clientexported.ConsensusState, error) { From 4561d8fedc812fae1c1b7d0429649452d5ba51bd Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 20 Jan 2020 22:00:18 +0100 Subject: [PATCH 19/35] ibc/03-connection/keeper: begin tests for verify.go --- x/ibc/02-client/keeper/keeper_test.go | 6 +- x/ibc/03-connection/keeper/handshake_test.go | 565 +++++++++---------- x/ibc/03-connection/keeper/keeper_test.go | 67 ++- x/ibc/03-connection/keeper/verify_test.go | 55 ++ x/ibc/07-tendermint/client_state.go | 6 + x/ibc/07-tendermint/consensus_state.go | 6 +- x/ibc/23-commitment/merkle.go | 5 + x/ibc/23-commitment/types.go | 1 + 8 files changed, 384 insertions(+), 327 deletions(-) create mode 100644 x/ibc/03-connection/keeper/verify_test.go diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 9a2dc723b4f9..eb0edc99d3e4 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -3,6 +3,9 @@ package keeper_test import ( "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" @@ -13,9 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" ) const ( diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index 97da665b3c5a..b716adc0960b 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -1,292 +1,277 @@ package keeper_test -import ( - "fmt" - - abci "github.com/tendermint/tendermint/abci/types" - - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" - "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" -) - -func (suite *KeeperTestSuite) TestConnOpenInit() { - suite.createClient(testClientID1) - counterparty := connection.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - - success := func() error { - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) - suite.NoError(err) - - conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) - suite.True(existed) - - expectConn := connection.ConnectionEnd{ - State: exported.INIT, - ClientID: testClientID1, - Counterparty: counterparty, - Versions: connection.GetCompatibleVersions(), - } - suite.EqualValues(expectConn, conn) - - return nil - } - - connectionExists := func() error { - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) - } - - var testCases = []testCase{ - {success, true, "success"}, - {connectionExists, false, "connection already exists"}, - } - - for _, tc := range testCases { - if tc.expectPass { - suite.Require().NoError(tc.fun(), tc.msg) - } else { - suite.Require().Error(tc.fun(), tc.msg) - } - } -} - -func (suite *KeeperTestSuite) TestConnOpenTry() { - suite.createClient(testClientID2) - suite.createClient(testClientID1) - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) - - connectionKey := ibctypes.KeyConnection(testConnectionID2) - - proofInit, proofHeight := suite.queryProof(connectionKey) - consensusKey := ibctypes.KeyConsensusState(testClientID2, uint64(proofHeight)) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - - invalidProof := func() error { - counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, - testConnectionID1, counterparty, testClientID1, - connection.GetCompatibleVersions(), - proofInit, proofConsensus, - uint64(proofHeight), uint64(consensusHeight)) - } - - success := func() error { - suite.updateClient(testClientID1) - - counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, - testConnectionID1, counterparty, testClientID1, - connection.GetCompatibleVersions(), - proofInit, proofConsensus, - uint64(proofHeight), uint64(consensusHeight)) - suite.NoError(err) - - //check connection state - conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) - suite.True(existed) - suite.Equal(exported.TRYOPEN.String(), conn.State.String(), "invalid connection state") - return nil - } - - connectionExists := func() error { - suite.updateClient(testClientID1) - counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, - testConnectionID1, counterparty, testClientID1, - connection.GetCompatibleVersions(), - proofInit, proofConsensus, - uint64(proofHeight), uint64(consensusHeight)) - } - - var testCases = []testCase{ - {invalidProof, false, "invalid proof"}, - {connectionExists, false, "connection already exists"}, - {success, true, "success"}, - } - - for _, tc := range testCases { - if tc.expectPass { - suite.Require().NoError(tc.fun(), tc.msg) - } else { - suite.Require().Error(tc.fun(), tc.msg) - } - } - -} - -func (suite *KeeperTestSuite) TestConnOpenAck() { - suite.createClient(testClientID2) - suite.createClient(testClientID1) - - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) - connectionKey := ibctypes.KeyConnection(testConnectionID1) - - proofTry, proofHeight := suite.queryProof(connectionKey) - consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(proofHeight)) - proofConsensus, consensusHeight := suite.queryProof(consensusKey) - - connectionNotFound := func() error { - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - } - - invalidConnectionState := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - } - - invalidVersion := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, "1.0.1", proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - } - - invalidProof := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - } - - success := func() error { - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) - suite.updateClient(testClientID2) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) - suite.NoError(err) - - //check connection state - conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID2) - suite.True(existed) - suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") - return nil - } - - var testCases = []testCase{ - {connectionNotFound, false, "connection not exists"}, - {invalidConnectionState, false, "invalid connection state"}, - {invalidVersion, false, "invalid version"}, - {invalidProof, false, "invalid proof"}, - {success, true, ""}, - } - - for _, tc := range testCases { - if tc.expectPass { - suite.Require().NoError(tc.fun(), tc.msg) - } else { - suite.Require().Error(tc.fun(), tc.msg) - } - } -} - -func (suite *KeeperTestSuite) TestConnOpenConfirm() { - suite.createClient(testClientID2) - suite.createClient(testClientID1) - suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.OPEN) - - connKey := ibctypes.KeyConnection(testConnectionID2) - proof, h := suite.queryProof(connKey) - - connectionNotFound := func() error { - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - } - - invalidConnectionState := func() error { - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - } - - invalidProof := func() error { - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) - return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - } - - success := func() error { - suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) - suite.updateClient(testClientID1) - proof, h = suite.queryProof(connKey) - err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) - suite.NoError(err) - - conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) - suite.True(existed) - suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") - return nil - } - - var testCases = []testCase{ - {connectionNotFound, false, "connection not exists"}, - {invalidConnectionState, false, "invalid connection state"}, - {invalidProof, false, "invalid proof"}, - {success, true, ""}, - } - - for _, tc := range testCases { - if tc.expectPass { - suite.Require().NoError(tc.fun(), tc.msg) - } else { - suite.Require().Error(tc.fun(), tc.msg) - } - } -} - -func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { - res := suite.app.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", storeKey), - Data: key, - Prove: true, - }) - - height := res.Height - proof := commitment.Proof{ - Proof: res.Proof, - } - - return proof, height -} - -func (suite *KeeperTestSuite) createClient(clientID string) { - suite.app.Commit() - commitID := suite.app.LastCommitID() - - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - - consensusState := tendermint.ConsensusState{ - Root: commitment.NewRoot(commitID.Hash), - ValidatorSetHash: suite.valSet.Hash(), - } - - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) - suite.NoError(err) -} - -func (suite *KeeperTestSuite) updateClient(clientID string) { - // always commit when updateClient and begin a new block - suite.app.Commit() - commitID := suite.app.LastCommitID() - - height := suite.app.LastBlockHeight() + 1 - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - - state := tendermint.ConsensusState{ - Root: commitment.NewRoot(commitID.Hash), - } - - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(height), state) -} - -func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { - counterparty := connection.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - connection := connection.ConnectionEnd{ - State: state, - ClientID: clientID, - Counterparty: counterparty, - Versions: connection.GetCompatibleVersions(), - } - suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) -} - -type testCase = struct { - fun func() error - expectPass bool - msg string -} +// import ( +// "fmt" + +// abci "github.com/tendermint/tendermint/abci/types" + +// connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" +// "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" +// tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" +// commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +// ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +// ) + +// func (suite *KeeperTestSuite) TestConnOpenInit() { +// suite.createClient(testClientID1) +// counterparty := connection.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) + +// success := func() error { +// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) +// suite.NoError(err) + +// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) +// suite.True(existed) + +// expectConn := connection.ConnectionEnd{ +// State: exported.INIT, +// ClientID: testClientID1, +// Counterparty: counterparty, +// Versions: connection.GetCompatibleVersions(), +// } +// suite.EqualValues(expectConn, conn) + +// return nil +// } + +// connectionExists := func() error { +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) +// } + +// var testCases = []testCase{ +// {success, true, "success"}, +// {connectionExists, false, "connection already exists"}, +// } + +// for _, tc := range testCases { +// if tc.expectPass { +// suite.Require().NoError(tc.fun(), tc.msg) +// } else { +// suite.Require().Error(tc.fun(), tc.msg) +// } +// } +// } + +// func (suite *KeeperTestSuite) TestConnOpenTry() { +// suite.createClient(testClientID2) +// suite.createClient(testClientID1) +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) + +// connectionKey := ibctypes.KeyConnection(testConnectionID2) + +// proofInit, proofHeight := suite.queryProof(connectionKey) +// consensusKey := ibctypes.KeyConsensusState(testClientID2, uint64(proofHeight)) +// proofConsensus, consensusHeight := suite.queryProof(consensusKey) + +// invalidProof := func() error { +// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, +// testConnectionID1, counterparty, testClientID1, +// connection.GetCompatibleVersions(), +// proofInit, proofConsensus, +// uint64(proofHeight), uint64(consensusHeight)) +// } + +// success := func() error { +// suite.updateClient(testClientID1) + +// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) +// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, +// testConnectionID1, counterparty, testClientID1, +// connection.GetCompatibleVersions(), +// proofInit, proofConsensus, +// uint64(proofHeight), uint64(consensusHeight)) +// suite.NoError(err) + +// //check connection state +// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) +// suite.True(existed) +// suite.Equal(exported.TRYOPEN.String(), conn.State.String(), "invalid connection state") +// return nil +// } + +// connectionExists := func() error { +// suite.updateClient(testClientID1) +// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, +// testConnectionID1, counterparty, testClientID1, +// connection.GetCompatibleVersions(), +// proofInit, proofConsensus, +// uint64(proofHeight), uint64(consensusHeight)) +// } + +// var testCases = []testCase{ +// {invalidProof, false, "invalid proof"}, +// {connectionExists, false, "connection already exists"}, +// {success, true, "success"}, +// } + +// for _, tc := range testCases { +// if tc.expectPass { +// suite.Require().NoError(tc.fun(), tc.msg) +// } else { +// suite.Require().Error(tc.fun(), tc.msg) +// } +// } + +// } + +// func (suite *KeeperTestSuite) TestConnOpenAck() { +// suite.createClient(testClientID2) +// suite.createClient(testClientID1) + +// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) +// connectionKey := ibctypes.KeyConnection(testConnectionID1) + +// proofTry, proofHeight := suite.queryProof(connectionKey) +// consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(proofHeight)) +// proofConsensus, consensusHeight := suite.queryProof(consensusKey) + +// connectionNotFound := func() error { +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) +// } + +// invalidConnectionState := func() error { +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) +// } + +// invalidVersion := func() error { +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, "1.0.1", proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) +// } + +// invalidProof := func() error { +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) +// } + +// success := func() error { +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) +// suite.updateClient(testClientID2) +// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) +// suite.NoError(err) + +// //check connection state +// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID2) +// suite.True(existed) +// suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") +// return nil +// } + +// var testCases = []testCase{ +// {connectionNotFound, false, "connection not exists"}, +// {invalidConnectionState, false, "invalid connection state"}, +// {invalidVersion, false, "invalid version"}, +// {invalidProof, false, "invalid proof"}, +// {success, true, ""}, +// } + +// for _, tc := range testCases { +// if tc.expectPass { +// suite.Require().NoError(tc.fun(), tc.msg) +// } else { +// suite.Require().Error(tc.fun(), tc.msg) +// } +// } +// } + +// func (suite *KeeperTestSuite) TestConnOpenConfirm() { +// suite.createClient(testClientID2) +// suite.createClient(testClientID1) +// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.OPEN) + +// connKey := ibctypes.KeyConnection(testConnectionID2) +// proof, h := suite.queryProof(connKey) + +// connectionNotFound := func() error { +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) +// } + +// invalidConnectionState := func() error { +// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) +// } + +// invalidProof := func() error { +// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) +// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) +// } + +// success := func() error { +// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) +// suite.updateClient(testClientID1) +// proof, h = suite.queryProof(connKey) +// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) +// suite.NoError(err) + +// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) +// suite.True(existed) +// suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") +// return nil +// } + +// var testCases = []testCase{ +// {connectionNotFound, false, "connection not exists"}, +// {invalidConnectionState, false, "invalid connection state"}, +// {invalidProof, false, "invalid proof"}, +// {success, true, ""}, +// } + +// for _, tc := range testCases { +// if tc.expectPass { +// suite.Require().NoError(tc.fun(), tc.msg) +// } else { +// suite.Require().Error(tc.fun(), tc.msg) +// } +// } +// } + +// func (suite *KeeperTestSuite) createClient(clientID string) { +// suite.app.Commit() +// commitID := suite.app.LastCommitID() + +// suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) +// suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) + +// consensusState := tendermint.ConsensusState{ +// Root: commitment.NewRoot(commitID.Hash), +// ValidatorSetHash: suite.valSet.Hash(), +// } + +// _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) +// suite.NoError(err) +// } + +// func (suite *KeeperTestSuite) updateClient(clientID string) { +// // always commit when updateClient and begin a new block +// suite.app.Commit() +// commitID := suite.app.LastCommitID() + +// height := suite.app.LastBlockHeight() + 1 +// suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) +// suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) + +// state := tendermint.ConsensusState{ +// Root: commitment.NewRoot(commitID.Hash), +// } + +// suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(height), state) +// } + +// func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { +// counterparty := connection.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) +// connection := connection.ConnectionEnd{ +// State: state, +// ClientID: clientID, +// Counterparty: counterparty, +// Versions: connection.GetCompatibleVersions(), +// } +// suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) +// } + +// type testCase = struct { +// fun func() error +// expectPass bool +// msg string +// } diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index cacafd4a35c2..003a3f064e7f 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -1,10 +1,12 @@ package keeper_test import ( + "fmt" "testing" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -13,6 +15,8 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -20,6 +24,7 @@ const ( clientType = clientexported.Tendermint storeKey = ibctypes.StoreKey chainID = "test" + testHeight = 10 testClientID1 = "testclientid1" testConnectionID1 = "connectionid1" @@ -34,10 +39,12 @@ const ( type KeeperTestSuite struct { suite.Suite - cdc *codec.Codec - ctx sdk.Context - app *simapp.SimApp - valSet *tmtypes.ValidatorSet + cdc *codec.Codec + ctx sdk.Context + app *simapp.SimApp + valSet *tmtypes.ValidatorSet + consensusState clientexported.ConsensusState + header tendermint.Header } func (suite *KeeperTestSuite) SetupTest() { @@ -45,13 +52,33 @@ func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(isCheckTx) suite.cdc = app.Codec() - suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{}) + suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{ChainID: chainID, Height: testHeight}) suite.app = app privVal := tmtypes.NewMockPV() validator := tmtypes.NewValidator(privVal.GetPubKey(), 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + suite.header = tendermint.CreateTestHeader(chainID, testHeight, suite.valSet, suite.valSet, []tmtypes.PrivValidator{privVal}) + suite.consensusState = tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + } +} + +func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { + res := suite.app.Query(abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", storeKey), + Data: key, + Prove: true, + }) + + height := res.Height + proof := commitment.Proof{ + Proof: res.Proof, + } + + return proof, height } func TestKeeperTestSuite(t *testing.T) { @@ -63,12 +90,7 @@ func (suite *KeeperTestSuite) TestSetAndGetConnection() { suite.False(existed) counterparty := types.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - expConn := types.ConnectionEnd{ - State: exported.INIT, - ClientID: testClientID1, - Counterparty: counterparty, - Versions: types.GetCompatibleVersions(), - } + expConn := types.NewConnectionEnd(exported.INIT, testClientID1, counterparty, types.GetCompatibleVersions()) suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnectionID1, expConn) conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) suite.True(existed) @@ -91,26 +113,9 @@ func (suite KeeperTestSuite) TestGetAllConnections() { counterparty2 := types.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) counterparty3 := types.NewCounterparty(testClientID3, testConnectionID3, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - conn1 := types.ConnectionEnd{ - State: exported.INIT, - ClientID: testClientID1, - Counterparty: counterparty3, - Versions: types.GetCompatibleVersions(), - } - - conn2 := types.ConnectionEnd{ - State: exported.INIT, - ClientID: testClientID2, - Counterparty: counterparty1, - Versions: types.GetCompatibleVersions(), - } - - conn3 := types.ConnectionEnd{ - State: exported.UNINITIALIZED, - ClientID: testClientID3, - Counterparty: counterparty2, - Versions: types.GetCompatibleVersions(), - } + conn1 := types.NewConnectionEnd(exported.INIT, testClientID1, counterparty3, types.GetCompatibleVersions()) + conn2 := types.NewConnectionEnd(exported.INIT, testClientID2, counterparty1, types.GetCompatibleVersions()) + conn3 := types.NewConnectionEnd(exported.UNINITIALIZED, testClientID3, counterparty2, types.GetCompatibleVersions()) expConnections := []types.ConnectionEnd{conn1, conn2, conn3} diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go new file mode 100644 index 000000000000..b2193e5d0270 --- /dev/null +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -0,0 +1,55 @@ +package keeper_test + +import ( + "fmt" + + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { + counterparty := types.Counterparty{Prefix: suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()} + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} + + connectionKey := ibctypes.KeyConsensusState(testClientID1, testHeight) + + cases := []struct { + msg string + connection types.ConnectionEnd + malleate func() error + expPass bool + }{ + // {"client state not found", connection1, func() error { return nil }, false}, + // {"verification failed", connection1, func() error { + // _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + // return err + // }, false}, + {"verification success", connection1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(connectionKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + suite.ctx, tc.connection, uint64(proofHeight), proof, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 8a4f0243741a..99478e958cb1 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,6 +1,8 @@ package tendermint import ( + "errors" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" @@ -79,6 +81,10 @@ func (cs ClientState) VerifyClientConsensusState( return err } + if consensusState.GetRoot() == nil { + return errors.New("root cannot be empty") + } + if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { return clienttypes.ErrFailedClientConsensusStateVerification } diff --git a/x/ibc/07-tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go index a99118e08dd1..fc03f95454d7 100644 --- a/x/ibc/07-tendermint/consensus_state.go +++ b/x/ibc/07-tendermint/consensus_state.go @@ -23,10 +23,10 @@ func (cs ConsensusState) GetRoot() commitment.RootI { return cs.Root } -// ValidateBasic +// ValidateBasic defines a basic validation for the tendermint consensus state. func (cs ConsensusState) ValidateBasic() error { - if cs.Root == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be nil") + if cs.Root.IsEmpty() { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") } if len(cs.ValidatorSetHash) == 0 { return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "validator set hash cannot be empty") diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 39a355fc9ce8..7e46bdbd09ef 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -41,6 +41,11 @@ func (r Root) GetHash() []byte { return r.Hash } +// IsEmpty returns true if the root is empty +func (r Root) IsEmpty() bool { + return &r == nil || len(r.GetHash()) == 0 +} + var _ PrefixI = Prefix{} // Prefix is merkle path prefixed to the key. diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index bd2968da88dc..b94b98ea61c2 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -14,6 +14,7 @@ package commitment type RootI interface { GetCommitmentType() Type GetHash() []byte + IsEmpty() bool } // PrefixI implements spec:CommitmentPrefix. From 613d522de99032c6e5814fbffea7a0b1d08f3562 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jan 2020 11:54:05 +0100 Subject: [PATCH 20/35] ibc/23-commitment: add IsEmpty() to Prefix, Path and Proof --- x/ibc/03-connection/keeper/verify_test.go | 10 +++++----- x/ibc/07-tendermint/consensus_state.go | 2 +- x/ibc/23-commitment/merkle.go | 21 ++++++++++++++++++--- x/ibc/23-commitment/types.go | 3 +++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index b2193e5d0270..5fa987c24768 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -20,11 +20,11 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { malleate func() error expPass bool }{ - // {"client state not found", connection1, func() error { return nil }, false}, - // {"verification failed", connection1, func() error { - // _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - // return err - // }, false}, + {"client state not found", connection1, func() error { return nil }, false}, + {"verification failed", connection1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, {"verification success", connection1, func() error { _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) return err diff --git a/x/ibc/07-tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go index fc03f95454d7..1af790da0960 100644 --- a/x/ibc/07-tendermint/consensus_state.go +++ b/x/ibc/07-tendermint/consensus_state.go @@ -25,7 +25,7 @@ func (cs ConsensusState) GetRoot() commitment.RootI { // ValidateBasic defines a basic validation for the tendermint consensus state. func (cs ConsensusState) ValidateBasic() error { - if cs.Root.IsEmpty() { + if cs.Root == nil || cs.Root.IsEmpty() { return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") } if len(cs.ValidatorSetHash) == 0 { diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 7e46bdbd09ef..febf9937109b 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -43,7 +43,7 @@ func (r Root) GetHash() []byte { // IsEmpty returns true if the root is empty func (r Root) IsEmpty() bool { - return &r == nil || len(r.GetHash()) == 0 + return len(r.GetHash()) == 0 } var _ PrefixI = Prefix{} @@ -71,6 +71,11 @@ func (p Prefix) Bytes() []byte { return p.KeyPrefix } +// IsEmpty returns true if the prefix is empty +func (p Prefix) IsEmpty() bool { + return len(p.Bytes()) == 0 +} + var _ PathI = Path{} // Path is the path used to verify commitment proofs, which can be an arbitrary @@ -110,6 +115,11 @@ func (p Path) Pretty() string { return path } +// IsEmpty returns true if the path is empty +func (p Path) IsEmpty() bool { + return len(p.KeyPath) == 0 +} + // ApplyPrefix constructs a new commitment path from the arguments. It interprets // the path argument in the context of the prefix argument. // @@ -121,7 +131,7 @@ func ApplyPrefix(prefix PrefixI, path string) (Path, error) { return Path{}, err } - if prefix == nil || len(prefix.Bytes()) == 0 { + if prefix == nil || prefix.IsEmpty() { return Path{}, errors.New("prefix can't be empty") } @@ -157,9 +167,14 @@ func (proof Proof) VerifyNonMembership(root RootI, path PathI) bool { return err == nil } +// IsEmpty returns true if the root is empty +func (proof Proof) IsEmpty() bool { + return (proof == Proof{}) || proof.Proof == nil +} + // ValidateBasic checks if the proof is empty. func (proof Proof) ValidateBasic() error { - if (proof == Proof{}) || proof.Proof == nil { + if proof.IsEmpty() { return ErrInvalidProof } return nil diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index b94b98ea61c2..f7556a54260b 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -22,6 +22,7 @@ type RootI interface { type PrefixI interface { GetCommitmentType() Type Bytes() []byte + IsEmpty() bool } // PathI implements spec:CommitmentPath. @@ -29,6 +30,7 @@ type PrefixI interface { type PathI interface { GetCommitmentType() Type String() string + IsEmpty() bool } // ProofI implements spec:CommitmentProof. @@ -39,6 +41,7 @@ type ProofI interface { GetCommitmentType() Type VerifyMembership(RootI, PathI, []byte) bool VerifyNonMembership(RootI, PathI) bool + IsEmpty() bool ValidateBasic() error } From b7e1dce03336083c51caab97f4e8fb9a61d6ef8b Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jan 2020 15:17:18 +0100 Subject: [PATCH 21/35] address comments from review --- x/ibc/02-client/exported/exported.go | 2 +- x/ibc/02-client/keeper/client.go | 2 +- x/ibc/02-client/keeper/client_test.go | 4 ++-- x/ibc/03-connection/keeper/handshake.go | 9 ++++----- x/ibc/04-channel/keeper/packet.go | 2 +- x/ibc/07-tendermint/client_state.go | 4 ++-- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index ab7f9843030a..b8ca0e6adad7 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -15,7 +15,7 @@ import ( type ClientState interface { GetID() string ClientType() ClientType - GetSequence() uint64 + GetLatestHeight() uint64 IsFrozen() bool // State verification functions diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 3e21a9c2543e..b7c8aa272809 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -33,7 +33,7 @@ func (k Keeper) CreateClient( k.SetClientState(ctx, clientState) k.SetClientType(ctx, clientID, clientType) - k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, clientState.GetSequence())) + k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, clientState.GetLatestHeight())) ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index 421a1781cc90..625ef800cdfc 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -11,7 +11,7 @@ import ( ) const ( - invalidClientType exported.ClientType = iota + 5 + invalidClientType exported.ClientType = 0 ) func (suite *KeeperTestSuite) TestCreateClient() { @@ -115,7 +115,7 @@ func (suite *KeeperTestSuite) TestUpdateClient() { suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(suite.header.GetHeight(), clientState.GetSequence(), "client state height not updated correctly") + suite.Require().Equal(suite.header.GetHeight(), clientState.GetLatestHeight(), "client state height not updated correctly") suite.Require().Equal(expConsensusState, consensusState, "consensus state should have been updated") } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index 9cab202cb410..f2826ba0a548 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -55,7 +55,7 @@ func (k Keeper) ConnOpenTry( proofHeight uint64, consensusHeight uint64, ) error { - // XXX: blocked by #5078 + // XXX: blocked by #5475 // if consensusHeight > uint64(ctx.BlockHeight()) { // return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") // } @@ -85,7 +85,7 @@ func (k Keeper) ConnOpenTry( return err } - // XXX: blocked by #5078 + // XXX: blocked by #5475 // err = k.VerifyClientConsensusState( // ctx, proofHeight, proofInit, expectedConsensusState, // ) @@ -128,7 +128,7 @@ func (k Keeper) ConnOpenAck( proofHeight uint64, consensusHeight uint64, ) error { - // XXX: blocked by #5078 + // XXX: blocked by #5475 // if consensusHeight > uint64(ctx.BlockHeight()) { // return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height") // } @@ -168,7 +168,7 @@ func (k Keeper) ConnOpenAck( return err } - // XXX: blocked by #5078 + // XXX: blocked by #5475 // err = k.VerifyClientConsensusState( // ctx, connection, proofHeight, proofInit, expectedConsensusState, // ) @@ -206,7 +206,6 @@ func (k Keeper) ConnOpenConfirm( } // NOTE: should be safe to use proofHeight here - // TODO: Update spec expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, proofHeight) if !found { return clienttypes.ErrConsensusStateNotFound diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index 091038cc51ca..3486fd7cec98 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -84,7 +84,7 @@ func (k Keeper) SendPacket( } // check if packet timeouted on the receiving chain - if clientState.GetSequence() >= packet.GetTimeoutHeight() { + if clientState.GetLatestHeight() >= packet.GetTimeoutHeight() { return sdkerrors.Wrap(types.ErrPacketTimeout, "timeout already passed ond the receiving chain") } diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 99478e958cb1..1838da6414f7 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -45,8 +45,8 @@ func (cs ClientState) ClientType() clientexported.ClientType { return clientexported.Tendermint } -// GetSequence returns latest block height. -func (cs ClientState) GetSequence() uint64 { +// GetLatestHeight returns latest block height. +func (cs ClientState) GetLatestHeight() uint64 { return cs.LatestHeight } From 00730605e8c3bc323562cf82df902f99d6d80a8c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jan 2020 15:50:52 +0100 Subject: [PATCH 22/35] add tests for evidence --- x/ibc/07-tendermint/consensus_state_test.go | 58 +++++- x/ibc/07-tendermint/evidence.go | 5 +- x/ibc/07-tendermint/evidence_test.go | 209 ++++++++++++-------- x/ibc/07-tendermint/header.go | 31 +++ x/ibc/07-tendermint/misbehaviour_test.go | 141 ++++++++----- x/ibc/07-tendermint/tendermint_test.go | 13 +- x/ibc/07-tendermint/test_utils.go | 6 +- x/ibc/07-tendermint/update_test.go | 2 +- 8 files changed, 320 insertions(+), 145 deletions(-) diff --git a/x/ibc/07-tendermint/consensus_state_test.go b/x/ibc/07-tendermint/consensus_state_test.go index 1b7ad5eda026..08365d13cd3b 100644 --- a/x/ibc/07-tendermint/consensus_state_test.go +++ b/x/ibc/07-tendermint/consensus_state_test.go @@ -1 +1,57 @@ -package tendermint +package tendermint_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +func TestConsensusStateValidateBasic(t *testing.T) { + testCases := []struct { + msg string + consensusState tendermint.ConsensusState + expectPass bool + }{ + {"sucess", + tendermint.ConsensusState{ + Root: commitment.NewRoot([]byte("app_hash")), + ValidatorSetHash: []byte("valset_hash"), + }, + true}, + {"root is nil", + tendermint.ConsensusState{ + Root: nil, + ValidatorSetHash: []byte("valset_hash"), + }, + false}, + {"root is empty", + tendermint.ConsensusState{ + Root: commitment.Root{}, + ValidatorSetHash: []byte("valset_hash"), + }, + false}, + {"invalid client type", + tendermint.ConsensusState{ + Root: commitment.NewRoot([]byte("app_hash")), + ValidatorSetHash: []byte{}, + }, + false}, + } + + for i, tc := range testCases { + tc := tc + + require.Equal(t, tc.consensusState.ClientType(), clientexported.Tendermint) + require.Equal(t, tc.consensusState.GetRoot(), tc.consensusState.Root) + + if tc.expectPass { + require.NoError(t, tc.consensusState.ValidateBasic(), "valid test case %d failed: %s", i, tc.msg) + } else { + require.Error(t, tc.consensusState.ValidateBasic(), "invalid test case %d passed: %s", i, tc.msg) + } + } +} diff --git a/x/ibc/07-tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go index 15f527fc46b2..852709fb97c8 100644 --- a/x/ibc/07-tendermint/evidence.go +++ b/x/ibc/07-tendermint/evidence.go @@ -41,7 +41,7 @@ func (ev Evidence) GetClientID() string { // Route implements Evidence interface func (ev Evidence) Route() string { - return "client" + return clienttypes.SubModuleName } // Type implements Evidence interface @@ -60,7 +60,8 @@ func (ev Evidence) String() string { // Hash implements Evidence interface func (ev Evidence) Hash() tmbytes.HexBytes { - return tmhash.Sum(SubModuleCdc.MustMarshalBinaryBare(ev)) + bz := SubModuleCdc.MustMarshalBinaryBare(ev) + return tmhash.Sum(bz) } // GetHeight returns the height at which misbehaviour occurred diff --git a/x/ibc/07-tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go index b7811847fd4a..f73b13324807 100644 --- a/x/ibc/07-tendermint/evidence_test.go +++ b/x/ibc/07-tendermint/evidence_test.go @@ -1,15 +1,36 @@ -package tendermint +package tendermint_test import ( "bytes" "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" + + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) +func (suite *TendermintTestSuite) TestEvidence() { + signers := []tmtypes.PrivValidator{suite.privVal} + + ev := tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, height, suite.valSet, suite.valSet, signers), + FromValidatorSet: suite.valSet, + ChainID: chainID, + ClientID: "gaiamainnet", + } + + suite.Require().Equal(ev.ClientType(), clientexported.Tendermint) + suite.Require().Equal(ev.GetClientID(), "gaiamainnet") + suite.Require().Equal(ev.Type(), "client_misbehaviour") + // suite.Require().Equal(ev.Hash(), tmhash.Sum(tendermint.SubModuleCdc.MustMarshalBinaryBare(ev))) // FIXME: + +} + func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { altPrivVal := tmtypes.NewMockPV() - altVal := tmtypes.NewValidator(altPrivVal.GetPubKey(), 4) + altVal := tmtypes.NewValidator(altPrivVal.GetPubKey(), height) // Create bothValSet with both suite validator and altVal bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) @@ -29,125 +50,145 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { testCases := []struct { name string - evidence Evidence - malleateEvidence func(ev *Evidence) - expErr bool + evidence tendermint.Evidence + malleateEvidence func(ev *tendermint.Evidence) error + expPass bool }{ { "valid evidence", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("gaia", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, height, suite.valSet, bothValSet, signers), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, + func(ev *tendermint.Evidence) error { return nil }, + true, + }, + { + "invalid client ID ", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, height, suite.valSet, bothValSet, signers), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "GAIA", + }, + func(ev *tendermint.Evidence) error { return nil }, false, }, { "wrong chainID on header1", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("ethermint", 4, suite.valSet, bothValSet, signers), - ChainID: "ethermint", - ClientID: "gaiamainnet", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader("ethermint", height, suite.valSet, bothValSet, signers), + FromValidatorSet: bothValSet, + ChainID: "ethermint", + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, - true, + func(ev *tendermint.Evidence) error { return nil }, + false, }, { "wrong chainID on header2", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("ethermint", 4, suite.valSet, bothValSet, signers), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader("ethermint", height, suite.valSet, bothValSet, signers), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, - true, + func(ev *tendermint.Evidence) error { return nil }, + false, }, { "mismatched heights", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("gaia", 6, suite.valSet, bothValSet, signers), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, 6, suite.valSet, bothValSet, signers), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, - true, + func(ev *tendermint.Evidence) error { return nil }, + false, }, { "same block id", - Evidence{ - Header1: suite.header, - Header2: suite.header, - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.Evidence{ + Header1: suite.header, + Header2: suite.header, + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, - true, + func(ev *tendermint.Evidence) error { return nil }, + false, }, { - "header doesn't have 2/3 majority", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("gaia", 4, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + "header 1 doesn't have 2/3 majority", + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + Header2: suite.header, + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) { - // voteSet contains only altVal which is less than 2/3 of total power (4/14) - wrongVoteSet := tmtypes.NewVoteSet("gaia", ev.Header2.Height, 1, tmtypes.PrecommitType, altValSet) + func(ev *tendermint.Evidence) error { + // voteSet contains only altVal which is less than 2/3 of total power (height/1height) + wrongVoteSet := tmtypes.NewVoteSet(chainID, ev.Header1.Height, 1, tmtypes.PrecommitType, altValSet) var err error - ev.Header2.Commit, err = tmtypes.MakeCommit(ev.Header2.Commit.BlockID, ev.Header2.Height, ev.Header2.Commit.Round, wrongVoteSet, altSigners) - if err != nil { - panic(err) - } + ev.Header1.Commit, err = tmtypes.MakeCommit(ev.Header1.Commit.BlockID, ev.Header2.Height, ev.Header1.Commit.Round, wrongVoteSet, altSigners) + return err }, - true, + false, }, { - "validators sign off on wrong commit", - Evidence{ - Header1: suite.header, - Header2: CreateTestHeader("gaia", 4, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + "header 2 doesn't have 2/3 majority", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) { - ev.Header2.Commit.BlockID = makeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) + func(ev *tendermint.Evidence) error { + // voteSet contains only altVal which is less than 2/3 of total power (height/1height) + wrongVoteSet := tmtypes.NewVoteSet(chainID, ev.Header2.Height, 1, tmtypes.PrecommitType, altValSet) + var err error + ev.Header2.Commit, err = tmtypes.MakeCommit(ev.Header2.Commit.BlockID, ev.Header2.Height, ev.Header2.Commit.Round, wrongVoteSet, altSigners) + return err }, - true, + false, }, { - "invalid ClientID", - Evidence{ - Header1: CreateTestHeader("gaia123??", 5, suite.valSet, suite.valSet, signers), - Header2: CreateTestHeader("gaia123?", 5, suite.valSet, suite.valSet, signers), - ChainID: "gaia123?", - ClientID: "gaia123?", + "validators sign off on wrong commit", + tendermint.Evidence{ + Header1: suite.header, + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: "gaiamainnet", }, - func(ev *Evidence) {}, - true, + func(ev *tendermint.Evidence) error { + ev.Header2.Commit.BlockID = tendermint.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) + return nil + }, + false, }, } - for _, tc := range testCases { - tc := tc // pin for scopelint - suite.Run(tc.name, func() { - // reset suite for each subtest - suite.SetupTest() - - tc.malleateEvidence(&tc.evidence) + for i, tc := range testCases { + tc := tc - err := tc.evidence.ValidateBasic() + err := tc.malleateEvidence(&tc.evidence) + suite.Require().NoError(err) - if tc.expErr { - suite.Error(err, "ValidateBasic did not throw error for invalid evidence") - } else { - suite.NoError(err, "ValidateBasic returned error on valid evidence: %s", err) - } - }) + if tc.expPass { + suite.Require().NoError(tc.evidence.ValidateBasic(), "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(tc.evidence.ValidateBasic(), "invalid test case %d passed: %s", i, tc.name) + } } } diff --git a/x/ibc/07-tendermint/header.go b/x/ibc/07-tendermint/header.go index 2b91f1f79ff1..0628aa01cbee 100644 --- a/x/ibc/07-tendermint/header.go +++ b/x/ibc/07-tendermint/header.go @@ -1,6 +1,7 @@ package tendermint import ( + abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -39,3 +40,33 @@ func (h Header) ValidateBasic(chainID string) error { } return nil } + +// ToABCIHeader parses the header to an ABCI header type. +// NOTE: only for testing use. +func (h Header) ToABCIHeader() abci.Header { + return abci.Header{ + Version: abci.Version{ + App: h.Version.App.Uint64(), + Block: h.Version.Block.Uint64(), + }, + ChainID: h.ChainID, + Height: h.Height, + Time: h.Time, + LastBlockId: abci.BlockID{ + Hash: h.LastBlockID.Hash, + PartsHeader: abci.PartSetHeader{ + Total: int32(h.LastBlockID.PartsHeader.Total), + Hash: h.LastBlockID.PartsHeader.Hash, + }, + }, + LastCommitHash: h.LastCommitHash, + DataHash: h.DataHash, + ValidatorsHash: h.ValidatorsHash, + NextValidatorsHash: h.NextValidatorsHash, + ConsensusHash: h.ConsensusHash, + AppHash: h.AppHash, + LastResultsHash: h.LastResultsHash, + EvidenceHash: h.EvidenceHash, + ProposerAddress: h.ProposerAddress, + } +} diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index 7f929e805179..92925d8582a1 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -1,9 +1,13 @@ -package tendermint +package tendermint_test import ( "bytes" + "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" + + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func (suite *TendermintTestSuite) TestCheckMisbehaviour() { @@ -27,75 +31,110 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { testCases := []struct { name string - clientState ClientState - consensusState ConsensusState - evidence Evidence + clientState tendermint.ClientState + consensusState tendermint.ConsensusState + evidence tendermint.Evidence height uint64 - expErr bool + expPass bool }{ { - "trusting period misbehavior should pass", - ClientState{}, - ConsensusState{}, - Evidence{ - Header1: CreateTestHeader("gaia", 5, bothValSet, suite.valSet, bothSigners), - Header2: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + "valid misbehavior evidence", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: bothValSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, + }, + height, + true, + }, + { + "height doesn't match provided evidence", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: bothValSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, + }, + 0, + false, + }, + { + "consensus state's valset hash different from evidence", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: suite.valSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, bothValSet, suite.valSet, bothSigners), + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, }, - 5, + height, false, }, { "first valset has too much change", - ClientState{}, - ConsensusState{}, - Evidence{ - Header1: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), - Header2: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: bothValSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, altValSet, bothValSet, altSigners), + Header2: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, }, - 5, - true, + height, + false, }, { "second valset has too much change", - ClientState{}, - ConsensusState{}, - Evidence{ - Header1: CreateTestHeader("gaia", 5, bothValSet, bothValSet, bothSigners), - Header2: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: bothValSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, bothValSet, bothValSet, bothSigners), + Header2: tendermint.CreateTestHeader(chainID, height, altValSet, bothValSet, altSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, }, - 5, - true, + height, + false, }, { "both valsets have too much change", - ClientState{}, - ConsensusState{}, - Evidence{ - Header1: CreateTestHeader("gaia", 5, altValSet, altValSet, altSigners), - Header2: CreateTestHeader("gaia", 5, altValSet, bothValSet, altSigners), - ChainID: "gaia", - ClientID: "gaiamainnet", + tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: 0}, + tendermint.ConsensusState{Root: commitment.NewRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSetHash: bothValSet.Hash()}, + tendermint.Evidence{ + Header1: tendermint.CreateTestHeader(chainID, height, altValSet, altValSet, altSigners), + Header2: tendermint.CreateTestHeader(chainID, height, altValSet, bothValSet, altSigners), + FromValidatorSet: bothValSet, + ChainID: chainID, + ClientID: chainID, }, - 5, - true, + height, + false, }, } - for _, tc := range testCases { - tc := tc // pin for scopelint - suite.Run(tc.name, func() { - err := checkMisbehaviour(tc.clientState, tc.consensusState, tc.evidence, tc.height) - if tc.expErr { - suite.Error(err, "CheckMisbehaviour passed unexpectedly") - } else { - suite.NoError(err, "CheckMisbehaviour failed unexpectedly: %v", err) - } - }) + for i, tc := range testCases { + tc := tc + + clientState, err := tendermint.CheckMisbehaviourAndUpdateState(tc.clientState, tc.consensusState, tc.evidence, tc.height) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.name) + suite.Require().True(clientState.IsFrozen(), "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.name) + } } } diff --git a/x/ibc/07-tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go index 3b39b7c09d5c..8ec504dd310f 100644 --- a/x/ibc/07-tendermint/tendermint_test.go +++ b/x/ibc/07-tendermint/tendermint_test.go @@ -1,4 +1,4 @@ -package tendermint +package tendermint_test import ( "testing" @@ -6,6 +6,13 @@ import ( "github.com/stretchr/testify/suite" tmtypes "github.com/tendermint/tendermint/types" + + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" +) + +const ( + chainID = "gaia" + height = 4 ) type TendermintTestSuite struct { @@ -13,14 +20,14 @@ type TendermintTestSuite struct { privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet - header Header + header tendermint.Header } func (suite *TendermintTestSuite) SetupTest() { suite.privVal = tmtypes.NewMockPV() val := tmtypes.NewValidator(suite.privVal.GetPubKey(), 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - suite.header = CreateTestHeader("gaia", 4, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = tendermint.CreateTestHeader(chainID, height, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } func TestTendermintTestSuite(t *testing.T) { diff --git a/x/ibc/07-tendermint/test_utils.go b/x/ibc/07-tendermint/test_utils.go index ce0df416a2a7..32373fab3fd6 100644 --- a/x/ibc/07-tendermint/test_utils.go +++ b/x/ibc/07-tendermint/test_utils.go @@ -10,7 +10,7 @@ import ( ) // Copied unimported test functions from tmtypes to use them here -func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { +func MakeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { return tmtypes.BlockID{ Hash: hash, PartsHeader: tmtypes.PartSetHeader{ @@ -30,7 +30,7 @@ func CreateTestHeader(chainID string, height int64, valSet *tmtypes.ValidatorSet ChainID: chainID, Height: height, Time: timestamp, - LastBlockID: makeBlockID(make([]byte, tmhash.Size), math.MaxInt64, make([]byte, tmhash.Size)), + LastBlockID: MakeBlockID(make([]byte, tmhash.Size), math.MaxInt64, make([]byte, tmhash.Size)), LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), DataHash: tmhash.Sum([]byte("data_hash")), ValidatorsHash: vsetHash, @@ -42,7 +42,7 @@ func CreateTestHeader(chainID string, height int64, valSet *tmtypes.ValidatorSet ProposerAddress: valSet.Proposer.Address, } hhash := tmHeader.Hash() - blockID := makeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) + blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) voteSet := tmtypes.NewVoteSet(chainID, height, 1, tmtypes.PrecommitType, valSet) commit, err := tmtypes.MakeCommit(blockID, height, 1, voteSet, signers) if err != nil { diff --git a/x/ibc/07-tendermint/update_test.go b/x/ibc/07-tendermint/update_test.go index 23fbbfea15be..3dfea8858b8f 100644 --- a/x/ibc/07-tendermint/update_test.go +++ b/x/ibc/07-tendermint/update_test.go @@ -1,4 +1,4 @@ -package tendermint +package tendermint_test func (suite *TendermintTestSuite) TestCheckValidity() { // // valid header From c9fadfa55cc4e20dc7202eb26c064cec59dcb02c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jan 2020 19:18:24 +0100 Subject: [PATCH 23/35] x/ibc/07-tendermint: add tests for consensus state, header and update --- x/ibc/07-tendermint/consensus_state.go | 6 +- x/ibc/07-tendermint/evidence.go | 5 +- x/ibc/07-tendermint/evidence_test.go | 10 ++- x/ibc/07-tendermint/header.go | 8 +-- x/ibc/07-tendermint/header_test.go | 30 ++++++++ x/ibc/07-tendermint/update_test.go | 98 +++++++++++++++----------- 6 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 x/ibc/07-tendermint/header_test.go diff --git a/x/ibc/07-tendermint/consensus_state.go b/x/ibc/07-tendermint/consensus_state.go index 1af790da0960..a567c0deaa37 100644 --- a/x/ibc/07-tendermint/consensus_state.go +++ b/x/ibc/07-tendermint/consensus_state.go @@ -2,7 +2,7 @@ package tendermint import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -14,8 +14,8 @@ type ConsensusState struct { } // ClientType returns Tendermint -func (ConsensusState) ClientType() exported.ClientType { - return exported.Tendermint +func (ConsensusState) ClientType() clientexported.ClientType { + return clientexported.Tendermint } // GetRoot returns the commitment Root for the specific diff --git a/x/ibc/07-tendermint/evidence.go b/x/ibc/07-tendermint/evidence.go index 852709fb97c8..82b78ea03370 100644 --- a/x/ibc/07-tendermint/evidence.go +++ b/x/ibc/07-tendermint/evidence.go @@ -1,6 +1,8 @@ package tendermint import ( + "math" + yaml "gopkg.in/yaml.v2" "github.com/tendermint/tendermint/crypto/tmhash" @@ -51,6 +53,7 @@ func (ev Evidence) Type() string { // String implements Evidence interface func (ev Evidence) String() string { + // FIXME: implement custom marshaller bz, err := yaml.Marshal(ev) if err != nil { panic(err) @@ -68,7 +71,7 @@ func (ev Evidence) Hash() tmbytes.HexBytes { // // NOTE: assumes that evidence headers have the same height func (ev Evidence) GetHeight() int64 { - return ev.Header1.Height + return int64(math.Min(float64(ev.Header1.Height), float64(ev.Header2.Height))) } // ValidateBasic implements Evidence interface diff --git a/x/ibc/07-tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go index f73b13324807..fd8beec50983 100644 --- a/x/ibc/07-tendermint/evidence_test.go +++ b/x/ibc/07-tendermint/evidence_test.go @@ -4,13 +4,18 @@ import ( "bytes" "github.com/tendermint/tendermint/crypto/tmhash" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) func (suite *TendermintTestSuite) TestEvidence() { + cdc := codec.New() + codec.RegisterCrypto(cdc) + tendermint.RegisterCodec(cdc) signers := []tmtypes.PrivValidator{suite.privVal} ev := tendermint.Evidence{ @@ -23,9 +28,10 @@ func (suite *TendermintTestSuite) TestEvidence() { suite.Require().Equal(ev.ClientType(), clientexported.Tendermint) suite.Require().Equal(ev.GetClientID(), "gaiamainnet") + suite.Require().Equal(ev.Route(), "client") suite.Require().Equal(ev.Type(), "client_misbehaviour") - // suite.Require().Equal(ev.Hash(), tmhash.Sum(tendermint.SubModuleCdc.MustMarshalBinaryBare(ev))) // FIXME: - + suite.Require().Equal(ev.Hash(), tmbytes.HexBytes(tmhash.Sum(tendermint.SubModuleCdc.MustMarshalBinaryBare(ev)))) + suite.Require().Equal(ev.GetHeight(), int64(height)) } func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { diff --git a/x/ibc/07-tendermint/header.go b/x/ibc/07-tendermint/header.go index 0628aa01cbee..fb53e36641b8 100644 --- a/x/ibc/07-tendermint/header.go +++ b/x/ibc/07-tendermint/header.go @@ -5,11 +5,11 @@ import ( tmtypes "github.com/tendermint/tendermint/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) -var _ exported.Header = Header{} +var _ clientexported.Header = Header{} // Header defines the Tendermint consensus Header type Header struct { @@ -18,8 +18,8 @@ type Header struct { } // ClientType defines that the Header is a Tendermint consensus algorithm -func (h Header) ClientType() exported.ClientType { - return exported.Tendermint +func (h Header) ClientType() clientexported.ClientType { + return clientexported.Tendermint } // GetHeight returns the current height diff --git a/x/ibc/07-tendermint/header_test.go b/x/ibc/07-tendermint/header_test.go new file mode 100644 index 000000000000..e88362d230d3 --- /dev/null +++ b/x/ibc/07-tendermint/header_test.go @@ -0,0 +1,30 @@ +package tendermint_test + +import ( + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" +) + +func (suite *TendermintTestSuite) TestHeaderValidateBasic() { + testCases := []struct { + name string + header tendermint.Header + chainID string + expPass bool + }{ + {"valid header", suite.header, chainID, true}, + {"signed header basic validation failed", suite.header, "chainID", false}, + {"validator set nil", tendermint.Header{suite.header.SignedHeader, nil}, chainID, false}, + } + + suite.Require().Equal(clientexported.Tendermint, suite.header.ClientType()) + + for i, tc := range testCases { + tc := tc + if tc.expPass { + suite.Require().NoError(tc.header.ValidateBasic(tc.chainID), "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(tc.header.ValidateBasic(tc.chainID), "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/07-tendermint/update_test.go b/x/ibc/07-tendermint/update_test.go index 3dfea8858b8f..2513b2d6ffc5 100644 --- a/x/ibc/07-tendermint/update_test.go +++ b/x/ibc/07-tendermint/update_test.go @@ -1,47 +1,59 @@ package tendermint_test -func (suite *TendermintTestSuite) TestCheckValidity() { - // // valid header - // err := checkValidity(suite.header) - // suite.NoError(err, "validity failed") - - // // switch out header ValidatorsHash - // suite.header.ValidatorsHash = tmhash.Sum([]byte("hello")) - // err = checkValidity(suite.header) - // suite.Error(err, "validator hash is wrong") - - // // reset suite and make header.NextValidatorSet different - // // from NextValidatorSetHash - // suite.SetupTest() - // privVal := tmtypes.NewMockPV() - // val := tmtypes.NewValidator(privVal.GetPubKey(), 5) - // suite.header.NextValidatorSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - // err = checkValidity(suite.header) - // suite.Error(err, "header's next validator set is not consistent with hash") - - // // reset and make header fail validatebasic - // suite.SetupTest() - // suite.header.ChainID = "not_gaia" - // err = checkValidity(suite.header) - // suite.Error(err, "invalid header should fail ValidateBasic") - // } +import ( + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) - // func (suite *TendermintTestSuite) TestCheckUpdate() { - // // valid header should successfully update consensus state - // cs, err := CheckValidityAndUpdateState(suite.header) - - // require.Nil(suite.T(), err, "valid update failed") - // require.Equal(suite.T(), suite.header.GetHeight(), cs.GetHeight(), "height not updated") - // require.Equal(suite.T(), suite.header.AppHash.Bytes(), cs.GetRoot().GetHash(), "root not updated") - // tmCS, _ := cs.(ConsensusState) - // require.Equal(suite.T(), suite.header.ValidatorSet, tmCS.ValidatorSet, "validator set did not update") - // require.Equal(suite.T(), suite.header.NextValidatorSet, tmCS.NextValidatorSet, "next validator set did not update") - - // // make header invalid so update should be unsuccessful - // suite.SetupTest() - // suite.header.ChainID = "not_gaia" - - // cs, err = CheckValidityAndUpdateState(suite.header) - // suite.Error(err) - // require.Nil(suite.T(), cs) +func (suite *TendermintTestSuite) TestCheckValidity() { + testCases := []struct { + name string + clientState tendermint.ClientState + header tendermint.Header + chainID string + expPass bool + }{ + { + name: "successful update", + clientState: tendermint.NewClientState(chainID, height), + header: suite.header, + chainID: chainID, + expPass: true, + }, + { + name: "header basic validation failed", + clientState: tendermint.NewClientState(chainID, height), + header: suite.header, + chainID: "cosmoshub", + expPass: false, + }, + { + name: "header height < latest client height", + clientState: tendermint.NewClientState(chainID, height+1), + header: suite.header, + chainID: chainID, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + expectedConsensus := tendermint.ConsensusState{ + Root: commitment.NewRoot(tc.header.AppHash), + ValidatorSetHash: tc.header.ValidatorSet.Hash(), + } + + clientState, consensusState, err := tendermint.CheckValidityAndUpdateState(tc.clientState, tc.header, tc.chainID) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(tc.header.GetHeight(), clientState.GetLatestHeight(), "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expectedConsensus, consensusState, "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(consensusState, "invalid test case %d passed: %s", i, tc.name) + } + } } From 8e1883918a182d63a21bd1ee1af0d6f96eda901a Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 22 Jan 2020 14:50:30 +0100 Subject: [PATCH 24/35] ibc/07-tendermint: fix verification panic and add tests --- x/ibc/02-client/client/rest/query.go | 18 --- x/ibc/02-client/exported/exported.go | 1 - x/ibc/03-connection/keeper/verify.go | 2 +- x/ibc/07-tendermint/client_state.go | 37 ++--- x/ibc/07-tendermint/client_state_test.go | 167 +++++++++++++++++++++++ x/ibc/07-tendermint/evidence_test.go | 4 - x/ibc/07-tendermint/tendermint_test.go | 8 ++ x/ibc/07-tendermint/update_test.go | 12 +- x/ibc/23-commitment/merkle.go | 8 ++ 9 files changed, 203 insertions(+), 54 deletions(-) create mode 100644 x/ibc/07-tendermint/client_state_test.go diff --git a/x/ibc/02-client/client/rest/query.go b/x/ibc/02-client/client/rest/query.go index 97845c945532..be09c1bc0757 100644 --- a/x/ibc/02-client/client/rest/query.go +++ b/x/ibc/02-client/client/rest/query.go @@ -10,7 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { @@ -19,7 +18,6 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/header", queryHeaderHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/ibc/node-state", queryNodeConsensusStateHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/ibc/path", queryPathHandlerFn(cliCtx)).Methods("GET") } // queryAllClientStatesFn queries all available light clients @@ -171,19 +169,3 @@ func queryNodeConsensusStateHandlerFn(cliCtx context.CLIContext) http.HandlerFun rest.PostProcessResponse(w, cliCtx, res) } } - -// queryPathHandlerFn implements a node consensus path querying route -// -// @Summary Query IBC path -// @Tags IBC -// @Produce json -// @Success 200 {object} QueryPath "OK" -// @Failure 500 {object} rest.ErrorResponse "Internal Server Error" -// @Router /ibc/path [get] -func queryPathHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - path := commitment.NewPrefix([]byte("ibc")) - res := cliCtx.Codec.MustMarshalJSON(path) - rest.PostProcessResponse(w, cliCtx, res) - } -} diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index b8ca0e6adad7..bdaa85583bce 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -25,7 +25,6 @@ type ClientState interface { height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - clientID string, consensusState ConsensusState, ) error VerifyConnectionState( diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 432505545540..bc15b71e8131 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -24,7 +24,7 @@ func (k Keeper) VerifyClientConsensusState( } return clientState.VerifyClientConsensusState( - k.cdc, height, connection.Counterparty.Prefix, proof, clientState.GetID(), consensusState, + k.cdc, height, connection.Counterparty.Prefix, proof, consensusState, ) } diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 1838da6414f7..62eac23ecb24 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,8 +1,6 @@ package tendermint import ( - "errors" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" @@ -60,12 +58,11 @@ func (cs ClientState) VerifyClientConsensusState( height uint64, prefix commitment.PrefixI, proof commitment.ProofI, - clientID string, consensusState clientexported.ConsensusState, ) error { - path, err := commitment.ApplyPrefix(prefix, ibctypes.ConsensusStatePath(clientID, height)) + path, err := commitment.ApplyPrefix(prefix, ibctypes.ConsensusStatePath(cs.GetID(), height)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -81,11 +78,7 @@ func (cs ClientState) VerifyClientConsensusState( return err } - if consensusState.GetRoot() == nil { - return errors.New("root cannot be empty") - } - - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { return clienttypes.ErrFailedClientConsensusStateVerification } @@ -103,7 +96,7 @@ func (cs ClientState) VerifyConnectionState( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.ConnectionPath(connectionID)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -119,7 +112,7 @@ func (cs ClientState) VerifyConnectionState( return err } - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { return clienttypes.ErrFailedConnectionStateVerification } @@ -138,7 +131,7 @@ func (cs ClientState) VerifyChannelState( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.ChannelPath(portID, channelID)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -154,7 +147,7 @@ func (cs ClientState) VerifyChannelState( return err } - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { return clienttypes.ErrFailedChannelStateVerification } @@ -173,7 +166,7 @@ func (cs ClientState) VerifyPacketCommitment( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketCommitmentPath(portID, channelID, sequence)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -184,7 +177,7 @@ func (cs ClientState) VerifyPacketCommitment( return clienttypes.ErrClientFrozen } - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); !ok { return clienttypes.ErrFailedPacketCommitmentVerification } @@ -203,7 +196,7 @@ func (cs ClientState) VerifyPacketAcknowledgement( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -214,7 +207,7 @@ func (cs ClientState) VerifyPacketAcknowledgement( return clienttypes.ErrClientFrozen } - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); !ok { return clienttypes.ErrFailedPacketAckVerification } @@ -229,7 +222,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -240,7 +233,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( return clienttypes.ErrClientFrozen } - if ok := proof.VerifyNonMembership(consensusState.GetRoot(), path); !ok { + if ok := proof != nil && proof.VerifyNonMembership(consensusState.GetRoot(), path); !ok { return clienttypes.ErrFailedPacketAckAbsenceVerification } @@ -258,7 +251,7 @@ func (cs ClientState) VerifyNextSequenceRecv( ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.NextSequenceRecvPath(portID, channelID)) if err != nil { - return nil + return err } if cs.LatestHeight < height { @@ -271,7 +264,7 @@ func (cs ClientState) VerifyNextSequenceRecv( bz := sdk.Uint64ToBigEndian(nextSequenceRecv) - if ok := proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { + if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { return clienttypes.ErrFailedNextSeqRecvVerification } diff --git a/x/ibc/07-tendermint/client_state_test.go b/x/ibc/07-tendermint/client_state_test.go new file mode 100644 index 000000000000..a6a9ae9d6198 --- /dev/null +++ b/x/ibc/07-tendermint/client_state_test.go @@ -0,0 +1,167 @@ +package tendermint_test + +import ( + connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { + testCases := []struct { + name string + clientState tendermint.ClientState + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyClientConsensusState( + suite.cdc, tc.height, tc.prefix, tc.proof, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyConnectionState() { + testCases := []struct { + name string + clientState tendermint.ClientState + connectionID string + connection connection.ConnectionEnd + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyConnectionState( + suite.cdc, tc.height, tc.prefix, tc.proof, tc.connectionID, tc.connection, tc.consensusState, + ) + + 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) + } + } +} diff --git a/x/ibc/07-tendermint/evidence_test.go b/x/ibc/07-tendermint/evidence_test.go index fd8beec50983..4fb819469ef2 100644 --- a/x/ibc/07-tendermint/evidence_test.go +++ b/x/ibc/07-tendermint/evidence_test.go @@ -7,15 +7,11 @@ import ( tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" ) func (suite *TendermintTestSuite) TestEvidence() { - cdc := codec.New() - codec.RegisterCrypto(cdc) - tendermint.RegisterCodec(cdc) signers := []tmtypes.PrivValidator{suite.privVal} ev := tendermint.Evidence{ diff --git a/x/ibc/07-tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go index 8ec504dd310f..3a41cc74002c 100644 --- a/x/ibc/07-tendermint/tendermint_test.go +++ b/x/ibc/07-tendermint/tendermint_test.go @@ -7,7 +7,9 @@ import ( tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) const ( @@ -18,12 +20,18 @@ const ( type TendermintTestSuite struct { suite.Suite + cdc *codec.Codec privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet header tendermint.Header } func (suite *TendermintTestSuite) SetupTest() { + suite.cdc = codec.New() + codec.RegisterCrypto(suite.cdc) + tendermint.RegisterCodec(suite.cdc) + commitment.RegisterCodec(suite.cdc) + suite.privVal = tmtypes.NewMockPV() val := tmtypes.NewValidator(suite.privVal.GetPubKey(), 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) diff --git a/x/ibc/07-tendermint/update_test.go b/x/ibc/07-tendermint/update_test.go index 2513b2d6ffc5..7e46a97da17a 100644 --- a/x/ibc/07-tendermint/update_test.go +++ b/x/ibc/07-tendermint/update_test.go @@ -9,28 +9,24 @@ func (suite *TendermintTestSuite) TestCheckValidity() { testCases := []struct { name string clientState tendermint.ClientState - header tendermint.Header chainID string expPass bool }{ { name: "successful update", clientState: tendermint.NewClientState(chainID, height), - header: suite.header, chainID: chainID, expPass: true, }, { name: "header basic validation failed", clientState: tendermint.NewClientState(chainID, height), - header: suite.header, chainID: "cosmoshub", expPass: false, }, { name: "header height < latest client height", clientState: tendermint.NewClientState(chainID, height+1), - header: suite.header, chainID: chainID, expPass: false, }, @@ -40,15 +36,15 @@ func (suite *TendermintTestSuite) TestCheckValidity() { tc := tc expectedConsensus := tendermint.ConsensusState{ - Root: commitment.NewRoot(tc.header.AppHash), - ValidatorSetHash: tc.header.ValidatorSet.Hash(), + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.header.ValidatorSet.Hash(), } - clientState, consensusState, err := tendermint.CheckValidityAndUpdateState(tc.clientState, tc.header, tc.chainID) + clientState, consensusState, err := tendermint.CheckValidityAndUpdateState(tc.clientState, suite.header, tc.chainID) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(tc.header.GetHeight(), clientState.GetLatestHeight(), "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(suite.header.GetHeight(), clientState.GetLatestHeight(), "valid test case %d failed: %s", i, tc.name) suite.Require().Equal(expectedConsensus, consensusState, "valid test case %d failed: %s", i, tc.name) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index febf9937109b..76ca552288db 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -155,6 +155,10 @@ func (Proof) GetCommitmentType() Type { // VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value. func (proof Proof) VerifyMembership(root RootI, path PathI, value []byte) bool { + if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() || len(value) == 0 { + return false + } + runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) return err == nil @@ -162,6 +166,10 @@ func (proof Proof) VerifyMembership(root RootI, path PathI, value []byte) bool { // VerifyNonMembership verifies the absence of a merkle proof against the given root and path. func (proof Proof) VerifyNonMembership(root RootI, path PathI) bool { + if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() { + return false + } + runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) return err == nil From 32189d1fec725ad5aebcc2cd111d5a17028c6181 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 22 Jan 2020 17:04:09 +0100 Subject: [PATCH 25/35] ibc/07-tendermint: add failure test cases --- x/ibc/07-tendermint/client_state.go | 9 +- x/ibc/07-tendermint/client_state_test.go | 537 ++++++++++++++++++++++- 2 files changed, 539 insertions(+), 7 deletions(-) diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index 62eac23ecb24..af919587b13b 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -216,9 +216,12 @@ func (cs ClientState) VerifyPacketAcknowledgement( func (cs ClientState) VerifyPacketAcknowledgementAbsence( height uint64, - prefix commitment.PrefixI, proof commitment.ProofI, - portID, channelID string, - sequence uint64, consensusState clientexported.ConsensusState, + prefix commitment.PrefixI, + proof commitment.ProofI, + portID, + channelID string, + sequence uint64, + consensusState clientexported.ConsensusState, ) error { path, err := commitment.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { diff --git a/x/ibc/07-tendermint/client_state_test.go b/x/ibc/07-tendermint/client_state_test.go index a6a9ae9d6198..8a82d06f8752 100644 --- a/x/ibc/07-tendermint/client_state_test.go +++ b/x/ibc/07-tendermint/client_state_test.go @@ -2,6 +2,9 @@ package tendermint_test import ( connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -86,6 +89,10 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { } func (suite *TendermintTestSuite) TestVerifyConnectionState() { + testConnectionID := "connectionid" + counterparty := connection.NewCounterparty("clientB", testConnectionID, commitment.NewPrefix([]byte("ibc"))) + conn := connection.NewConnectionEnd(connectionexported.OPEN, "clientA", counterparty, []string{"1.0.0"}) + testCases := []struct { name string clientState tendermint.ClientState @@ -98,8 +105,108 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { expPass bool }{ // { - // name: "successful verification", - // clientState: tendermint.NewClientState(chainID, height), + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + connectionID: testConnectionID, + connection: conn, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + connectionID: testConnectionID, + connection: conn, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + connectionID: testConnectionID, + connection: conn, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + connectionID: testConnectionID, + connection: conn, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyConnectionState( + suite.cdc, tc.height, tc.prefix, tc.proof, tc.connectionID, tc.connection, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyChannelState() { + testConnectionID := "connectionid" + testPortID := "testportid" + testChannelID := "testchannelid" + counterparty := channel.NewCounterparty(testPortID, testChannelID) + ch := channel.NewChannel(channelexported.OPEN, channelexported.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + + testCases := []struct { + name string + clientState tendermint.ClientState + portID string + channelID string + channel channel.Channel + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, @@ -110,6 +217,9 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { { name: "ApplyPrefix failed", clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, @@ -120,6 +230,9 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { { name: "latest client height < height", clientState: tendermint.NewClientState(chainID, height-1), + portID: testPortID, + channelID: testChannelID, + channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, @@ -130,6 +243,9 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { { name: "client is frozen", clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + portID: testPortID, + channelID: testChannelID, + channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, @@ -140,6 +256,9 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { { name: "proof verification failed", clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), @@ -154,8 +273,418 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { for i, tc := range testCases { tc := tc - err := tc.clientState.VerifyConnectionState( - suite.cdc, tc.height, tc.prefix, tc.proof, tc.connectionID, tc.connection, tc.consensusState, + err := tc.clientState.VerifyChannelState( + suite.cdc, tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.channel, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { + testPortID := "testportid" + testChannelID := "testchannelid" + testSequence := uint64(1) + + testCases := []struct { + name string + clientState tendermint.ClientState + portID string + channelID string + seq uint64 + commitment []byte + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + commitment: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + commitment: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + commitment: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + commitment: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyPacketCommitment( + tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.commitment, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { + testPortID := "testportid" + testChannelID := "testchannelid" + testSequence := uint64(1) + + testCases := []struct { + name string + clientState tendermint.ClientState + portID string + channelID string + seq uint64 + ack []byte + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + ack: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + ack: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + ack: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + ack: []byte{}, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyPacketAcknowledgement( + tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.ack, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { + testPortID := "testportid" + testChannelID := "testchannelid" + testSequence := uint64(1) + + testCases := []struct { + name string + clientState tendermint.ClientState + portID string + channelID string + seq uint64 + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + seq: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyPacketAcknowledgementAbsence( + tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.consensusState, + ) + + 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) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { + testPortID := "testportid" + testChannelID := "testchannelid" + testSequence := uint64(1) + + testCases := []struct { + name string + clientState tendermint.ClientState + portID string + channelID string + nextSequenceRecv uint64 + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool + }{ + // { + // name: "successful verification", + // clientState: tendermint.NewClientState(chainID, height), + // connectionID: testConnectionID, + // connection: conn, + // consensusState: tendermint.ConsensusState{ + // Root: commitment.NewRoot(suite.header.AppHash), + // }, + // height: height, + // prefix: commitment.NewPrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + nextSequenceRecv: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.Prefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + portID: testPortID, + channelID: testChannelID, + nextSequenceRecv: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + portID: testPortID, + channelID: testChannelID, + nextSequenceRecv: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + portID: testPortID, + channelID: testChannelID, + nextSequenceRecv: testSequence, + consensusState: tendermint.ConsensusState{ + Root: commitment.NewRoot(suite.header.AppHash), + ValidatorSetHash: suite.valSet.Hash(), + }, + height: height, + prefix: commitment.NewPrefix([]byte("ibc")), + proof: commitment.Proof{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyNextSequenceRecv( + tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.nextSequenceRecv, tc.consensusState, ) if tc.expPass { From 9a0645154edb039f51e1a350cb8ed32c8485f4fe Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 23 Jan 2020 12:39:09 +0100 Subject: [PATCH 26/35] x/ibc/03-connection/keeper: add verification tests for failing cases --- x/ibc/03-connection/keeper/verify_test.go | 360 +++++++++++++++++++++- 1 file changed, 356 insertions(+), 4 deletions(-) diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index 5fa987c24768..e31edc3d4a55 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -5,14 +5,24 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) +const ( + testPort1 = "firstport" + testPort2 = "secondport" + + testChannel1 = "firstchannel" + testChannel2 = "secondchannel" +) + func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { counterparty := types.Counterparty{Prefix: suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()} connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} - connectionKey := ibctypes.KeyConsensusState(testClientID1, testHeight) + consensusKey := ibctypes.KeyConsensusState(testClientID1, testHeight) cases := []struct { msg string @@ -20,15 +30,69 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { malleate func() error expPass bool }{ + {"verification success", connection1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testChannel1, testHeight, suite.consensusState) + return err + }, true}, {"client state not found", connection1, func() error { return nil }, false}, {"verification failed", connection1, func() error { _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) return err }, false}, - {"verification success", connection1, func() error { + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(consensusKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + suite.ctx, tc.connection, uint64(proofHeight), proof, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyConnectionState() { + counterparty := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} + + connectionKey := ibctypes.KeyConnection(testConnectionID1) + + cases := []struct { + msg string + connectionID string + connection types.ConnectionEnd + malleate func() error + expPass bool + }{ + {"verification success", testConnectionID2, connection1, func() error { _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) return err }, true}, + {"client state not found", testConnectionID2, connection1, func() error { return nil }, false}, + {"verification failed", testConnectionID2, connection1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, } for i, tc := range cases { @@ -41,14 +105,302 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { proof, proofHeight := suite.queryProof(connectionKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( - suite.ctx, tc.connection, uint64(proofHeight), proof, suite.consensusState, + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( + suite.ctx, uint64(proofHeight), proof, tc.connectionID, tc.connection, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyChannelState() { + counterpartyConn := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + + counterparty := channeltypes.NewCounterparty(testPort2, testChannel2) + channel := channeltypes.NewChannel( + channelexported.OPEN, channelexported.ORDERED, counterparty, + []string{testConnectionID1}, "1.0", + ) + + channelKey := ibctypes.KeyChannel(testPort1, testChannel1) + + cases := []struct { + msg string + portID string + channelID string + channel channeltypes.Channel + malleate func() error + expPass bool + }{ + {"verification success", testPort1, testChannel1, channel, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + {"client state not found", testPort1, testChannel1, channel, func() error { return nil }, false}, + {"verification failed", testPort1, testChannel1, channel, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(channelKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( + suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, + tc.channelID, tc.channel, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { + counterpartyConn := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + commitmentKey := ibctypes.KeyPacketCommitment(testPort1, testChannel1, 1) + commitmentBz := []byte{1} + + cases := []struct { + msg string + portID string + channelID string + sequence uint64 + commitementBz []byte + malleate func() error + expPass bool + }{ + {"verification success", testPort1, testChannel1, 1, commitmentBz, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + {"client state not found", testPort1, testChannel1, 1, commitmentBz, func() error { return nil }, false}, + {"verification failed", testPort1, testChannel1, 1, commitmentBz, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(commitmentKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( + suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, + tc.channelID, tc.sequence, tc.commitementBz, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { + counterpartyConn := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + + packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) + ack := []byte("hello") + + cases := []struct { + msg string + portID string + channelID string + sequence uint64 + ack []byte + malleate func() error + expPass bool + }{ + {"verification success", testPort1, testChannel1, 1, ack, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + {"client state not found", testPort1, testChannel1, 1, ack, func() error { return nil }, false}, + {"verification failed", testPort1, testChannel1, 1, ack, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(packetAckKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( + suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, + tc.channelID, tc.sequence, tc.ack, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { + counterpartyConn := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + + channelKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) + + cases := []struct { + msg string + portID string + channelID string + sequence uint64 + malleate func() error + expPass bool + }{ + {"verification success", testPort1, testChannel1, 1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + {"client state not found", testPort1, testChannel1, 1, func() error { return nil }, false}, + {"verification failed", testPort1, testChannel1, 1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(channelKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( + suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, + tc.channelID, tc.sequence, suite.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { + counterpartyConn := types.NewCounterparty( + testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + + nextSeqRcvKey := ibctypes.KeyNextSequenceRecv(testPort1, testChannel1) + + cases := []struct { + msg string + portID string + channelID string + nextSeqRecv uint64 + malleate func() error + expPass bool + }{ + {"verification success", testPort1, testChannel1, 1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) + return err + }, true}, + {"client state not found", testPort1, testChannel1, 1, func() error { return nil }, false}, + {"verification failed", testPort1, testChannel1, 1, func() error { + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) + return err + }, false}, + } + + for i, tc := range cases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + err := tc.malleate() + suite.Require().NoError(err) + + proof, proofHeight := suite.queryProof(nextSeqRcvKey) + + err = suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( + suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, + tc.channelID, tc.nextSeqRecv, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotZero(proofHeight) + suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Zero(proofHeight) + suite.Require().True(proof.IsEmpty()) } }) } From c889f3d928f51a51ab6d765672539133bea30af9 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 23 Jan 2020 12:43:41 +0100 Subject: [PATCH 27/35] remove unused queriers --- x/ibc/03-connection/alias.go | 4 ---- x/ibc/03-connection/keeper/querier.go | 21 --------------------- x/ibc/03-connection/types/querier.go | 14 -------------- x/ibc/04-channel/alias.go | 3 --- x/ibc/04-channel/keeper/querier.go | 21 --------------------- x/ibc/04-channel/types/querier.go | 15 --------------- x/ibc/keeper/querier.go | 4 ---- 7 files changed, 82 deletions(-) diff --git a/x/ibc/03-connection/alias.go b/x/ibc/03-connection/alias.go index 289e9a7c5175..fcd7293ea6a6 100644 --- a/x/ibc/03-connection/alias.go +++ b/x/ibc/03-connection/alias.go @@ -19,7 +19,6 @@ const ( RouterKey = types.RouterKey QuerierRoute = types.QuerierRoute QueryAllConnections = types.QueryAllConnections - QueryConnection = types.QueryConnection QueryClientConnections = types.QueryClientConnections ) @@ -27,7 +26,6 @@ var ( // functions aliases NewKeeper = keeper.NewKeeper QuerierConnections = keeper.QuerierConnections - QuerierConnection = keeper.QuerierConnection QuerierClientConnections = keeper.QuerierClientConnections RegisterCodec = types.RegisterCodec NewConnectionEnd = types.NewConnectionEnd @@ -43,7 +41,6 @@ var ( NewMsgConnectionOpenAck = types.NewMsgConnectionOpenAck NewMsgConnectionOpenConfirm = types.NewMsgConnectionOpenConfirm NewConnectionResponse = types.NewConnectionResponse - NewQueryConnectionParams = types.NewQueryConnectionParams NewClientConnectionsResponse = types.NewClientConnectionsResponse NewQueryClientConnectionsParams = types.NewQueryClientConnectionsParams GetCompatibleVersions = types.GetCompatibleVersions @@ -69,7 +66,6 @@ type ( MsgConnectionOpenAck = types.MsgConnectionOpenAck MsgConnectionOpenConfirm = types.MsgConnectionOpenConfirm ConnectionResponse = types.ConnectionResponse - QueryConnectionParams = types.QueryConnectionParams ClientConnectionsResponse = types.ClientConnectionsResponse QueryClientConnectionsParams = types.QueryClientConnectionsParams ) diff --git a/x/ibc/03-connection/keeper/querier.go b/x/ibc/03-connection/keeper/querier.go index c888e84e32b2..8225eccaa858 100644 --- a/x/ibc/03-connection/keeper/querier.go +++ b/x/ibc/03-connection/keeper/querier.go @@ -10,27 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" ) -// QuerierConnection defines the sdk.Querier to query a connection end -func QuerierConnection(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryConnectionParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - connection, found := k.GetConnection(ctx, params.ConnectionID) - if !found { - return nil, sdkerrors.Wrap(types.ErrConnectionNotFound, params.ConnectionID) - } - - bz, err := types.SubModuleCdc.MarshalJSON(connection) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} - // QuerierConnections defines the sdk.Querier to query all the connections. func QuerierConnections(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { var params types.QueryAllConnectionsParams diff --git a/x/ibc/03-connection/types/querier.go b/x/ibc/03-connection/types/querier.go index 5cd3c6907b60..459082a7f0c0 100644 --- a/x/ibc/03-connection/types/querier.go +++ b/x/ibc/03-connection/types/querier.go @@ -12,7 +12,6 @@ import ( // query routes supported by the IBC connection Querier const ( QueryAllConnections = "connections" - QueryConnection = "connection" QueryClientConnections = "client_connections" ) @@ -37,19 +36,6 @@ func NewConnectionResponse( } } -// QueryConnectionParams defines the params for the following queries: -// - 'custom/ibc/connections/' -type QueryConnectionParams struct { - ConnectionID string -} - -// NewQueryConnectionParams creates a new QueryConnectionParams instance -func NewQueryConnectionParams(clientID string) QueryConnectionParams { - return QueryConnectionParams{ - ConnectionID: clientID, - } -} - // QueryAllConnectionsParams defines the parameters necessary for querying for all // connections. type QueryAllConnectionsParams struct { diff --git a/x/ibc/04-channel/alias.go b/x/ibc/04-channel/alias.go index 196f81da52a1..6fb4daa4586b 100644 --- a/x/ibc/04-channel/alias.go +++ b/x/ibc/04-channel/alias.go @@ -28,7 +28,6 @@ var ( // functions aliases NewKeeper = keeper.NewKeeper QuerierChannels = keeper.QuerierChannels - QuerierChannel = keeper.QuerierChannel NewChannel = types.NewChannel NewCounterparty = types.NewCounterparty RegisterCodec = types.RegisterCodec @@ -53,7 +52,6 @@ var ( NewMsgAcknowledgement = types.NewMsgAcknowledgement NewPacket = types.NewPacket NewChannelResponse = types.NewChannelResponse - NewQueryChannelParams = types.NewQueryChannelParams // variable aliases SubModuleCdc = types.SubModuleCdc @@ -84,5 +82,4 @@ type ( MsgTimeout = types.MsgTimeout Packet = types.Packet ChannelResponse = types.ChannelResponse - QueryChannelParams = types.QueryChannelParams ) diff --git a/x/ibc/04-channel/keeper/querier.go b/x/ibc/04-channel/keeper/querier.go index 8c8cac783cec..0d60cab08c8b 100644 --- a/x/ibc/04-channel/keeper/querier.go +++ b/x/ibc/04-channel/keeper/querier.go @@ -10,27 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) -// QuerierChannel defines the sdk.Querier to query a module's channel -func QuerierChannel(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryChannelParams - - if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - channel, found := k.GetChannel(ctx, params.PortID, params.ChannelID) - if !found { - return nil, sdkerrors.Wrap(types.ErrChannelNotFound, params.ChannelID) - } - - bz, err := codec.MarshalJSONIndent(k.cdc, channel) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} - // QuerierChannels defines the sdk.Querier to query all the channels. func QuerierChannels(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { var params types.QueryAllChannelsParams diff --git a/x/ibc/04-channel/types/querier.go b/x/ibc/04-channel/types/querier.go index c08e3aa2b7f6..37b24559ba20 100644 --- a/x/ibc/04-channel/types/querier.go +++ b/x/ibc/04-channel/types/querier.go @@ -36,21 +36,6 @@ func NewChannelResponse( } } -// QueryChannelParams defines the params for the following queries: -// - 'custom/ibc/channel' -type QueryChannelParams struct { - PortID string - ChannelID string -} - -// NewQueryChannelParams creates a new QueryChannelParams instance -func NewQueryChannelParams(portID, channelID string) QueryChannelParams { - return QueryChannelParams{ - PortID: portID, - ChannelID: channelID, - } -} - // QueryAllChannelsParams defines the parameters necessary for querying for all // channels. type QueryAllChannelsParams struct { diff --git a/x/ibc/keeper/querier.go b/x/ibc/keeper/querier.go index 4210f455dd68..20bd0e746e82 100644 --- a/x/ibc/keeper/querier.go +++ b/x/ibc/keeper/querier.go @@ -28,8 +28,6 @@ func NewQuerier(k Keeper) sdk.Querier { } case connection.SubModuleName: switch path[1] { - case connection.QueryConnection: - res, err = connection.QuerierConnection(ctx, req, k.ConnectionKeeper) case connection.QueryAllConnections: res, err = connection.QuerierConnections(ctx, req, k.ConnectionKeeper) case connection.QueryClientConnections: @@ -39,8 +37,6 @@ func NewQuerier(k Keeper) sdk.Querier { } case channel.SubModuleName: switch path[1] { - case channel.QueryChannel: - res, err = channel.QuerierChannel(ctx, req, k.ChannelKeeper) case channel.QueryAllChannels: res, err = channel.QuerierChannels(ctx, req, k.ChannelKeeper) default: From 8db8eea5056213b5b136a928280ec154adda308e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 23 Jan 2020 13:09:28 +0100 Subject: [PATCH 28/35] Update ICS 7 tests (#5556) * Update ICS 7 tests * Fix one problem * Also set consensus state for height 1 * Apply same fixes to ICS 4 tests * Remove unnecessary change * Fix ante tests; remove printfs * Remove printf * Update x/ibc/ante/ante_test.go Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/ibc/04-channel/keeper/handshake_test.go | 7 ++++++- x/ibc/20-transfer/handler_test.go | 2 +- x/ibc/20-transfer/keeper/relay_test.go | 2 +- x/ibc/ante/ante_test.go | 15 +++++++++------ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 79f1324c9757..f3346e44cdd2 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -28,6 +28,7 @@ func (suite *KeeperTestSuite) createClient() { } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(suite.app.LastBlockHeight()), consensusState) suite.NoError(err) } @@ -44,7 +45,11 @@ func (suite *KeeperTestSuite) updateClient() { Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height), state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height-1), state) + csi, _ := suite.app.IBCKeeper.ClientKeeper.GetClientState(suite.ctx, testClient) + cs, _ := csi.(tendermint.ClientState) + cs.LatestHeight = uint64(height - 1) + suite.app.IBCKeeper.ClientKeeper.SetClientState(suite.ctx, cs) } func (suite *KeeperTestSuite) createConnection(state connectionexported.State) { diff --git a/x/ibc/20-transfer/handler_test.go b/x/ibc/20-transfer/handler_test.go index f369d0fd5595..8a87dae74783 100644 --- a/x/ibc/20-transfer/handler_test.go +++ b/x/ibc/20-transfer/handler_test.go @@ -104,7 +104,7 @@ func (suite *HandlerTestSuite) updateClient() { Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, 1, state) } func (suite *HandlerTestSuite) createConnection(state connectionexported.State) { diff --git a/x/ibc/20-transfer/keeper/relay_test.go b/x/ibc/20-transfer/keeper/relay_test.go index 4bf615931571..74b80429b09e 100644 --- a/x/ibc/20-transfer/keeper/relay_test.go +++ b/x/ibc/20-transfer/keeper/relay_test.go @@ -45,7 +45,7 @@ func (suite *KeeperTestSuite) updateClient() { Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, 1, state) } func (suite *KeeperTestSuite) createConnection(state connectionexported.State) { diff --git a/x/ibc/ante/ante_test.go b/x/ibc/ante/ante_test.go index 60acb3873d01..f6c23c3d9627 100644 --- a/x/ibc/ante/ante_test.go +++ b/x/ibc/ante/ante_test.go @@ -75,6 +75,7 @@ func (suite *HandlerTestSuite) createClient() { } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(suite.app.LastBlockHeight()), consensusState) suite.NoError(err) } @@ -91,7 +92,11 @@ func (suite *HandlerTestSuite) updateClient() { Root: commitment.NewRoot(commitID.Hash), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height), state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height-1), state) + csi, _ := suite.app.IBCKeeper.ClientKeeper.GetClientState(suite.ctx, testClient) + cs, _ := csi.(tendermint.ClientState) + cs.LatestHeight = uint64(height - 1) + suite.app.IBCKeeper.ClientKeeper.SetClientState(suite.ctx, cs) } func (suite *HandlerTestSuite) createConnection(state connectionexported.State) { @@ -169,11 +174,6 @@ func (suite *HandlerTestSuite) TestHandleMsgPacketOrdered() { suite.Error(err, "%+v", err) // invalid proof suite.updateClient() - cctx, _ = suite.ctx.CacheContext() - proof, proofHeight = suite.queryProof(packetCommitmentPath) - msg = channel.NewMsgPacket(packet, proof, uint64(proofHeight), addr1) - _, err = handler(cctx, suite.newTx(msg), false) - suite.Error(err, "%+v", err) // next recvseq not set proof, proofHeight = suite.queryProof(packetCommitmentPath) msg = channel.NewMsgPacket(packet, proof, uint64(proofHeight), addr1) @@ -183,6 +183,9 @@ func (suite *HandlerTestSuite) TestHandleMsgPacketOrdered() { for i := 0; i < 10; i++ { suite.app.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(cctx, cpportid, cpchanid, uint64(i)) _, err := handler(cctx, suite.newTx(msg), false) + if err == nil { + err = suite.app.IBCKeeper.ChannelKeeper.PacketExecuted(cctx, packet, packet.Data) + } if i == 1 { suite.NoError(err, "%d", i) // successfully executed write() From d8e237d16c450ec8be09a231ef0c7ee4137ababb Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 23 Jan 2020 13:22:41 +0100 Subject: [PATCH 29/35] add TODOs for ADR 03 dynamic store --- x/ibc/04-channel/keeper/handshake.go | 14 +++++++------- x/ibc/04-channel/keeper/packet.go | 20 ++++++++++---------- x/ibc/04-channel/keeper/timeout.go | 5 ++--- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index 4fa918a984b8..940912448b86 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -59,7 +59,7 @@ func (k Keeper) ChanOpenInit( channel := types.NewChannel(exported.INIT, order, counterparty, connectionHops, version) k.SetChannel(ctx, portID, channelID, channel) - // TODO: generate channel capability key and set it to store + // TODO: blocked by #5542 // key := "" // k.SetChannelCapability(ctx, portID, channelID, key) k.SetNextSequenceSend(ctx, portID, channelID, 1) @@ -96,7 +96,7 @@ func (k Keeper) ChanOpenTry( sdkerrors.Wrap(types.ErrInvalidChannel, "cannot relay connection attempt") } - // TODO: use key authentication + // TODO: blocked by #5542 // key := sdk.NewKVStoreKey(portID) // if !k.portKeeper.Authenticate(key, portID) { // return sdkerrors.Wrap(port.ErrInvalidPort, portID) @@ -148,7 +148,7 @@ func (k Keeper) ChanOpenTry( k.SetChannel(ctx, portID, channelID, channel) - // TODO: generate channel capability key and set it to store + // TODO: blocked by #5542 // key := "" // k.SetChannelCapability(ctx, portID, channelID, key) k.SetNextSequenceSend(ctx, portID, channelID, 1) @@ -179,7 +179,7 @@ func (k Keeper) ChanOpenAck( ) } - // TODO: use key authentication + // TODO: blocked by #5542 // key := sdk.NewKVStoreKey(portID) // if !k.portKeeper.Authenticate(key, portID) { // return sdkerrors.Wrap(port.ErrInvalidPort, portID) @@ -253,7 +253,7 @@ func (k Keeper) ChanOpenConfirm( ) } - // TODO: use key authentication + // TODO: blocked by #5542 // capkey, found := k.GetChannelCapability(ctx, portID, channelID) // if !found { // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) @@ -321,7 +321,7 @@ func (k Keeper) ChanCloseInit( portID, channelID string, ) error { - // TODO: use key authentication + // TODO: blocked by #5542 // capkey, found := k.GetChannelCapability(ctx, portID, channelID) // if !found { // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) @@ -368,7 +368,7 @@ func (k Keeper) ChanCloseConfirm( proofInit commitment.ProofI, proofHeight uint64, ) error { - // TODO: use key authentication + // TODO: blocked by #5542 // capkey, found := k.GetChannelCapability(ctx, portID, channelID) // if !found { // return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID) diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index 3486fd7cec98..1c6ee2a5711e 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -12,7 +12,6 @@ import ( connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -39,7 +38,7 @@ func (k Keeper) SendPacket( ) } - // TODO: uncomment when channel capability key is set to store + // TODO: blocked by #5542 // capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) // if !found { // return types.ErrChannelCapabilityNotFound @@ -346,16 +345,17 @@ func (k Keeper) CleanupPacket( ) } - capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return nil, types.ErrChannelCapabilityNotFound - } + // TODO: blocked by #5542 + // capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + // if !found { + // return nil, types.ErrChannelCapabilityNotFound + // } - portCapabilityKey := sdk.NewKVStoreKey(capKey) + // portCapabilityKey := sdk.NewKVStoreKey(capKey) - if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { - return nil, sdkerrors.Wrapf(port.ErrInvalidPort, "invalid source port: %s", packet.GetSourcePort()) - } + // if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) { + // return nil, sdkerrors.Wrapf(port.ErrInvalidPort, "invalid source port: %s", packet.GetSourcePort()) + // } if packet.GetDestPort() != channel.Counterparty.PortID { return nil, sdkerrors.Wrapf(types.ErrInvalidPacket, diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index 1c1e47905ba7..bb2ffb04741f 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -122,8 +122,7 @@ func (k Keeper) TimeoutExecuted(ctx sdk.Context, packet exported.PacketI) error return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) } - // check if the packet is linked to a capability key - // TODO: uncomment + // TODO: blocked by #5542 // _, found = k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) // if !found { // return types.ErrChannelCapabilityNotFound @@ -155,7 +154,7 @@ func (k Keeper) TimeoutOnClose( return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetSourcePort(), packet.GetSourceChannel()) } - // TODO: uncomment + // TODO: blocked by #5542 // capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) // if !found { // return nil, types.ErrChannelCapabilityNotFound From 2256db7f49944aeaa826a4b31a9e303c1b83cf17 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 23 Jan 2020 16:11:32 +0100 Subject: [PATCH 30/35] add tests for msgs and packet (#5559) * Add skeleton for ValidateBasic tests * Move tests; basic one passes * Add more ValidateBasic tests * Add more ValidateBasic testcases * Fix minor typo * Add `ValidateBasic` tests for Packet * Move to packet_test.go --- x/ibc/03-connection/types/connection.go | 2 +- x/ibc/04-channel/types/msgs_test.go | 70 +++++++++++++++++++++++++ x/ibc/04-channel/types/packet_test.go | 32 +++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 x/ibc/04-channel/types/packet_test.go diff --git a/x/ibc/03-connection/types/connection.go b/x/ibc/03-connection/types/connection.go index 6f5104427500..f71e3daf07f4 100644 --- a/x/ibc/03-connection/types/connection.go +++ b/x/ibc/03-connection/types/connection.go @@ -16,7 +16,7 @@ var _ exported.ConnectionI = ConnectionEnd{} // ConnectionEnd defines a stateful object on a chain connected to another separate // one. -// NOTE: there must only be 2 defined ConnectionEnds to stablish a connection +// NOTE: there must only be 2 defined ConnectionEnds to establish a connection // between two chains. type ConnectionEnd struct { State exported.State `json:"state" yaml:"state"` diff --git a/x/ibc/04-channel/types/msgs_test.go b/x/ibc/04-channel/types/msgs_test.go index 731ebe42c01d..0e43f58d503b 100644 --- a/x/ibc/04-channel/types/msgs_test.go +++ b/x/ibc/04-channel/types/msgs_test.go @@ -478,3 +478,73 @@ func TestMsgPacketGetSigners(t *testing.T) { expected := "[746573746164647231]" require.Equal(t, expected, fmt.Sprintf("%v", res)) } + +// TestMsgTimeout tests ValidateBasic for MsgTimeout +func (suite *MsgTestSuite) TestMsgTimeout() { + testMsgs := []MsgTimeout{ + NewMsgTimeout(packet, 0, proof, 1, addr), + NewMsgTimeout(packet, 0, proof, 0, addr), + NewMsgTimeout(packet, 0, proof, 1, emptyAddr), + NewMsgTimeout(packet, 0, emptyProof, 1, addr), + NewMsgTimeout(invalidPacket, 0, proof, 1, addr), + NewMsgTimeout(packet, 0, invalidProofs1, 1, addr), + } + + testCases := []struct { + msg MsgTimeout + expPass bool + errMsg string + }{ + {testMsgs[0], true, ""}, + {testMsgs[1], false, "proof height must be > 0"}, + {testMsgs[2], false, "missing signer address"}, + {testMsgs[3], false, "cannot submit an empty proof"}, + {testMsgs[4], false, "invalid packet"}, + {testMsgs[5], false, "cannot submit an invalid proof"}, + } + + for i, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) + } else { + suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} + +// TestMsgAcknowledgement tests ValidateBasic for MsgAcknowledgement +func (suite *MsgTestSuite) TestMsgAcknowledgement() { + testMsgs := []MsgAcknowledgement{ + NewMsgAcknowledgement(packet, packet.GetData(), proof, 1, addr), + NewMsgAcknowledgement(packet, packet.GetData(), proof, 0, addr), + NewMsgAcknowledgement(packet, packet.GetData(), proof, 1, emptyAddr), + NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, 1, addr), + NewMsgAcknowledgement(invalidPacket, packet.GetData(), proof, 1, addr), + NewMsgAcknowledgement(packet, invalidPacket.GetData(), proof, 1, addr), + NewMsgAcknowledgement(packet, packet.GetData(), invalidProofs1, 1, addr), + } + + testCases := []struct { + msg MsgAcknowledgement + expPass bool + errMsg string + }{ + {testMsgs[0], true, ""}, + {testMsgs[1], false, "proof height must be > 0"}, + {testMsgs[2], false, "missing signer address"}, + {testMsgs[3], false, "cannot submit an empty proof"}, + {testMsgs[4], false, "invalid packet"}, + {testMsgs[5], false, "invalid acknowledgement"}, + {testMsgs[6], false, "cannot submit an invalid proof"}, + } + + for i, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) + } else { + suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} diff --git a/x/ibc/04-channel/types/packet_test.go b/x/ibc/04-channel/types/packet_test.go new file mode 100644 index 000000000000..9137bd7380e4 --- /dev/null +++ b/x/ibc/04-channel/types/packet_test.go @@ -0,0 +1,32 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPacketValidateBasic(t *testing.T) { + testCases := []struct { + packet Packet + expPass bool + errMsg string + }{ + {NewPacket(validPacketT{}, 1, portid, chanid, cpportid, cpchanid), true, ""}, + {NewPacket(validPacketT{}, 0, portid, chanid, cpportid, cpchanid), false, "invalid sequence"}, + {NewPacket(validPacketT{}, 1, invalidPort, chanid, cpportid, cpchanid), false, "invalid source port"}, + {NewPacket(validPacketT{}, 1, portid, invalidChannel, cpportid, cpchanid), false, "invalid source channel"}, + {NewPacket(validPacketT{}, 1, portid, chanid, invalidPort, cpchanid), false, "invalid destination port"}, + {NewPacket(validPacketT{}, 1, portid, chanid, cpportid, invalidChannel), false, "invalid destination channel"}, + {NewPacket(invalidPacketT{}, 1, portid, chanid, cpportid, cpchanid), false, "invalid packet data"}, + } + + for i, tc := range testCases { + err := tc.packet.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "Msg %d failed: %s", i, tc.errMsg) + } else { + require.Error(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} From 5a3f3fc215d291cd3def86856ae03efe922d5cb7 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 23 Jan 2020 16:15:11 +0100 Subject: [PATCH 31/35] use app.Commit for tests --- x/ibc/03-connection/keeper/handshake_test.go | 43 -------------------- x/ibc/03-connection/keeper/keeper_test.go | 43 ++++++++++++++++++++ x/ibc/03-connection/keeper/verify_test.go | 38 +++++------------ x/ibc/04-channel/keeper/handshake_test.go | 5 +-- 4 files changed, 55 insertions(+), 74 deletions(-) diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index b716adc0960b..dd47a15b0163 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -227,49 +227,6 @@ package keeper_test // } // } -// func (suite *KeeperTestSuite) createClient(clientID string) { -// suite.app.Commit() -// commitID := suite.app.LastCommitID() - -// suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) -// suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - -// consensusState := tendermint.ConsensusState{ -// Root: commitment.NewRoot(commitID.Hash), -// ValidatorSetHash: suite.valSet.Hash(), -// } - -// _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) -// suite.NoError(err) -// } - -// func (suite *KeeperTestSuite) updateClient(clientID string) { -// // always commit when updateClient and begin a new block -// suite.app.Commit() -// commitID := suite.app.LastCommitID() - -// height := suite.app.LastBlockHeight() + 1 -// suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) -// suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) - -// state := tendermint.ConsensusState{ -// Root: commitment.NewRoot(commitID.Hash), -// } - -// suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(height), state) -// } - -// func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { -// counterparty := connection.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) -// connection := connection.ConnectionEnd{ -// State: state, -// ClientID: clientID, -// Counterparty: counterparty, -// Versions: connection.GetCompatibleVersions(), -// } -// suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) -// } - // type testCase = struct { // fun func() error // expectPass bool diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index 003a3f064e7f..d903c37edc4d 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -81,6 +81,49 @@ func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { return proof, height } +func (suite *KeeperTestSuite) createClient(clientID string) { + suite.app.Commit() + commitID := suite.app.LastCommitID() + + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + + consensusState := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), + ValidatorSetHash: suite.valSet.Hash(), + } + + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) + suite.NoError(err) +} + +func (suite *KeeperTestSuite) updateClient(clientID string) { + // always commit when updateClient and begin a new block + suite.app.Commit() + commitID := suite.app.LastCommitID() + + height := suite.app.LastBlockHeight() + 1 + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + + state := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), + } + + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(suite.app.LastBlockHeight()), state) +} + +func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { + counterparty := types.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) + connection := types.ConnectionEnd{ + State: state, + ClientID: clientID, + Counterparty: counterparty, + Versions: types.GetCompatibleVersions(), + } + suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) +} + func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index e31edc3d4a55..e710780688e9 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -22,23 +22,18 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { counterparty := types.Counterparty{Prefix: suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()} connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} - consensusKey := ibctypes.KeyConsensusState(testClientID1, testHeight) - cases := []struct { msg string connection types.ConnectionEnd - malleate func() error + malleate func() expPass bool }{ - {"verification success", connection1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testChannel1, testHeight, suite.consensusState) - return err + {"verification success", connection1, func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", connection1, func() error { return nil }, false}, - {"verification failed", connection1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", connection1, func() {}, false}, + {"verification failed", connection1, func() { + suite.createClient(testClientID2) }, false}, } @@ -47,22 +42,21 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + + consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(suite.ctx.BlockHeight())) proof, proofHeight := suite.queryProof(consensusKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( suite.ctx, tc.connection, uint64(proofHeight), proof, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -111,11 +105,9 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -172,11 +164,9 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -228,11 +218,9 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -285,11 +273,9 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -340,11 +326,9 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) @@ -395,11 +379,9 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotZero(proofHeight) suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().Zero(proofHeight) suite.Require().True(proof.IsEmpty()) } }) diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index f3346e44cdd2..5c4d2d297062 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -20,7 +20,7 @@ func (suite *KeeperTestSuite) createClient() { commitID := suite.app.LastCommitID() suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) consensusState := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), @@ -28,7 +28,6 @@ func (suite *KeeperTestSuite) createClient() { } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(suite.app.LastBlockHeight()), consensusState) suite.NoError(err) } @@ -39,7 +38,7 @@ func (suite *KeeperTestSuite) updateClient() { height := suite.app.LastBlockHeight() + 1 suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) state := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), From 0eccd5e41927169ed30079728916be31b062ab6a Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 23 Jan 2020 16:56:11 +0100 Subject: [PATCH 32/35] update verify.go --- x/ibc/03-connection/keeper/keeper_test.go | 24 +- x/ibc/03-connection/keeper/verify_test.go | 261 ++++++++-------------- 2 files changed, 120 insertions(+), 165 deletions(-) diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index d903c37edc4d..6ca3f54b4574 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -15,6 +15,8 @@ import ( clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -86,7 +88,7 @@ func (suite *KeeperTestSuite) createClient(clientID string) { commitID := suite.app.LastCommitID() suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + suite.ctx = suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 1) consensusState := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), @@ -104,7 +106,7 @@ func (suite *KeeperTestSuite) updateClient(clientID string) { height := suite.app.LastBlockHeight() + 1 suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + suite.ctx = suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 1) state := tendermint.ConsensusState{ Root: commitment.NewRoot(commitID.Hash), @@ -113,7 +115,10 @@ func (suite *KeeperTestSuite) updateClient(clientID string) { suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(suite.app.LastBlockHeight()), state) } -func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string, clientID, counterpartyClientID string, state exported.State) { +func (suite *KeeperTestSuite) createConnection( + connID, counterpartyConnID, clientID, counterpartyClientID string, + state exported.State, +) types.ConnectionEnd { counterparty := types.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) connection := types.ConnectionEnd{ State: state, @@ -122,6 +127,19 @@ func (suite *KeeperTestSuite) createConnection(connID, counterpartyConnID string Versions: types.GetCompatibleVersions(), } suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) + return connection +} + +func (suite *KeeperTestSuite) createChannel( + portID, channelID, counterpartyPortID, counterpartyChannelID string, + state channelexported.State, order channelexported.Order, connectionID string, +) channeltypes.Channel { + counterparty := channeltypes.NewCounterparty(counterpartyPortID, counterpartyChannelID) + channel := channeltypes.NewChannel(state, order, counterparty, + []string{connectionID}, "1.0", + ) + suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, portID, channelID, channel) + return channel } func TestKeeperTestSuite(t *testing.T) { diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index e710780688e9..289926094239 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -3,10 +3,9 @@ package keeper_test import ( "fmt" - clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -43,8 +42,9 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { suite.SetupTest() // reset tc.malleate() + suite.updateClient(testClientID1) - consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(suite.ctx.BlockHeight())) + consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(suite.ctx.BlockHeight()-1)) proof, proofHeight := suite.queryProof(consensusKey) @@ -57,35 +57,26 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyConnectionState() { - counterparty := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} connectionKey := ibctypes.KeyConnection(testConnectionID1) cases := []struct { - msg string - connectionID string - connection types.ConnectionEnd - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testConnectionID2, connection1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testConnectionID2, connection1, func() error { return nil }, false}, - {"verification failed", testConnectionID2, connection1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -94,56 +85,40 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.updateClient(testClientID1) proof, proofHeight := suite.queryProof(connectionKey) + fmt.Println(proof) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( - suite.ctx, uint64(proofHeight), proof, tc.connectionID, tc.connection, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( + suite.ctx, uint64(proofHeight), proof, testConnectionID1, connection, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyChannelState() { - counterpartyConn := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} - - counterparty := channeltypes.NewCounterparty(testPort2, testChannel2) - channel := channeltypes.NewChannel( - channelexported.OPEN, channelexported.ORDERED, counterparty, - []string{testConnectionID1}, "1.0", - ) - channelKey := ibctypes.KeyChannel(testPort1, testChannel1) cases := []struct { - msg string - portID string - channelID string - channel channeltypes.Channel - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testPort1, testChannel1, channel, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testPort1, testChannel1, channel, func() error { return nil }, false}, - {"verification failed", testPort1, testChannel1, channel, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -152,52 +127,46 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + channel := suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, + channelexported.OPEN, channelexported.ORDERED, testConnectionID1, + ) + suite.updateClient(testClientID1) proof, proofHeight := suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( - suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, - tc.channelID, tc.channel, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( + suite.ctx, connection, uint64(proofHeight), proof, testPort1, + testChannel1, channel, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { - counterpartyConn := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} + commitmentKey := ibctypes.KeyPacketCommitment(testPort1, testChannel1, 1) - commitmentBz := []byte{1} + commitmentBz := []byte("commitment") cases := []struct { - msg string - portID string - channelID string - sequence uint64 - commitementBz []byte - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testPort1, testChannel1, 1, commitmentBz, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testPort1, testChannel1, 1, commitmentBz, func() error { return nil }, false}, - {"verification failed", testPort1, testChannel1, 1, commitmentBz, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -206,53 +175,42 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.app.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.ctx, testPort1, testChannel1, 1, commitmentBz) + suite.updateClient(testClientID1) proof, proofHeight := suite.queryProof(commitmentKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( - suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, - tc.channelID, tc.sequence, tc.commitementBz, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( + suite.ctx, connection, uint64(proofHeight), proof, testPort1, + testChannel1, 1, commitmentBz, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { - counterpartyConn := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} - packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) - ack := []byte("hello") + ack := []byte("acknowledgement") cases := []struct { - msg string - portID string - channelID string - sequence uint64 - ack []byte - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testPort1, testChannel1, 1, ack, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testPort1, testChannel1, 1, ack, func() error { return nil }, false}, - {"verification failed", testPort1, testChannel1, 1, ack, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -261,51 +219,41 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.app.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.ctx, testPort1, testChannel1, 1, ack) + suite.updateClient(testClientID1) proof, proofHeight := suite.queryProof(packetAckKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( - suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, - tc.channelID, tc.sequence, tc.ack, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( + suite.ctx, connection, uint64(proofHeight), proof, testPort1, + testChannel1, 1, ack, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { - counterpartyConn := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} - - channelKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) + packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) cases := []struct { - msg string - portID string - channelID string - sequence uint64 - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testPort1, testChannel1, 1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testPort1, testChannel1, 1, func() error { return nil }, false}, - {"verification failed", testPort1, testChannel1, 1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -314,51 +262,40 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(channelKey) + proof, proofHeight := suite.queryProof(packetAckKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( - suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, - tc.channelID, tc.sequence, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( + suite.ctx, connection, uint64(proofHeight), proof, testPort1, + testChannel1, 1, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } } func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { - counterpartyConn := types.NewCounterparty( - testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - ) - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterpartyConn} - nextSeqRcvKey := ibctypes.KeyNextSequenceRecv(testPort1, testChannel1) cases := []struct { - msg string - portID string - channelID string - nextSeqRecv uint64 - malleate func() error - expPass bool + msg string + malleate func() + expPass bool }{ - {"verification success", testPort1, testChannel1, 1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID1, clientexported.Tendermint, suite.consensusState) - return err + {"verification success", func() { + suite.createClient(testClientID1) }, true}, - {"client state not found", testPort1, testChannel1, 1, func() error { return nil }, false}, - {"verification failed", testPort1, testChannel1, 1, func() error { - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClientID2, clientexported.Tendermint, suite.consensusState) - return err + {"client state not found", func() {}, false}, + {"verification failed", func() { + suite.createClient(testClientID2) }, false}, } @@ -367,22 +304,22 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - err := tc.malleate() - suite.Require().NoError(err) + tc.malleate() + connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.app.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.ctx, testPort1, testChannel1, 1) + suite.updateClient(testClientID1) proof, proofHeight := suite.queryProof(nextSeqRcvKey) - err = suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( - suite.ctx, connection1, uint64(proofHeight), proof, tc.portID, - tc.channelID, tc.nextSeqRecv, suite.consensusState, + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( + suite.ctx, connection, uint64(proofHeight), proof, testPort1, + testChannel1, 1, suite.consensusState, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - suite.Require().True(proof.IsEmpty()) } }) } From 1fa0ad02e9eecd54f9b93efb7a47cf250b7ee437 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 27 Jan 2020 04:30:45 -0800 Subject: [PATCH 33/35] Fix all ICS 07 Tests (#5565) * ICS 07 Debugging * WIP Debugging * Foo bar baz, baz bar foo, :man_shrugging: * cleanup print statements * cleanup verification test * add return err for proof verification * cleanup; start handshake testing * connection handshake tests * scopelint * WIP Test refactor * fix ICS03 tests * fix ICS04 tests * nolint Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- simapp/app.go | 1 - simapp/test_helpers.go | 3 +- store/iavl/store.go | 2 +- x/ibc/03-connection/client/cli/tx.go | 6 +- x/ibc/03-connection/client/rest/rest.go | 7 +- x/ibc/03-connection/client/rest/tx.go | 2 +- x/ibc/03-connection/handler.go | 2 +- x/ibc/03-connection/keeper/handshake.go | 23 +- x/ibc/03-connection/keeper/handshake_test.go | 484 ++++++------- x/ibc/03-connection/keeper/keeper.go | 8 +- x/ibc/03-connection/keeper/keeper_test.go | 128 +++- x/ibc/03-connection/keeper/verify_test.go | 138 ++-- x/ibc/03-connection/types/msgs.go | 24 +- x/ibc/03-connection/types/msgs_test.go | 18 +- x/ibc/04-channel/keeper/handshake.go | 14 +- x/ibc/04-channel/keeper/handshake_test.go | 681 +++++++++++-------- x/ibc/04-channel/keeper/keeper_test.go | 160 ++++- x/ibc/04-channel/keeper/timeout.go | 6 +- x/ibc/07-tendermint/client_state.go | 60 +- x/ibc/07-tendermint/client_state_test.go | 213 ++---- x/ibc/23-commitment/merkle.go | 15 +- x/ibc/23-commitment/merkle_test.go | 19 +- x/ibc/23-commitment/types.go | 4 +- x/ibc/23-commitment/verify.go | 20 +- x/ibc/keeper/querier_test.go | 6 - 25 files changed, 1162 insertions(+), 882 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 294691891e4a..cb12d7430d46 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -255,7 +255,6 @@ func NewSimApp( slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper), distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - ibc.NewAppModule(app.IBCKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 5192e59feab0..99a351e49afd 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -14,6 +14,7 @@ import ( bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp/helpers" + stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" @@ -23,7 +24,7 @@ import ( // Setup initializes a new SimApp. A Nop logger is set in SimApp. func Setup(isCheckTx bool) *SimApp { db := dbm.NewMemDB() - app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0) + app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0, bam.SetPruning(stypes.PruneNothing)) if !isCheckTx { // init chain must be called to stop deliverState from being nil genesisState := NewDefaultGenesisState() diff --git a/store/iavl/store.go b/store/iavl/store.go index 4a346a65d7b9..29abd10c401d 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -249,8 +249,8 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { switch req.Path { case "/key": // get by key key := req.Data // data holds the key bytes - res.Key = key + if !st.VersionExists(res.Height) { res.Log = iavl.ErrVersionDoesNotExist.Error() break diff --git a/x/ibc/03-connection/client/cli/tx.go b/x/ibc/03-connection/client/cli/tx.go index cf54b6e785ff..5ce3a6e65baa 100644 --- a/x/ibc/03-connection/client/cli/tx.go +++ b/x/ibc/03-connection/client/cli/tx.go @@ -217,9 +217,13 @@ $ %s tx ibc connection open-confirm [connection-id] [path/to/proof_ack.json] } proofHeight := uint64(cliCtx.Height) + consensusHeight, err := lastHeight(cliCtx) + if err != nil { + return err + } msg := types.NewMsgConnectionOpenConfirm( - connectionID, proofAck, proofHeight, cliCtx.GetFromAddress(), + connectionID, proofAck, proofHeight, consensusHeight, cliCtx.GetFromAddress(), ) if err := msg.ValidateBasic(); err != nil { diff --git a/x/ibc/03-connection/client/rest/rest.go b/x/ibc/03-connection/client/rest/rest.go index ed8e97b69280..7e74109b23b6 100644 --- a/x/ibc/03-connection/client/rest/rest.go +++ b/x/ibc/03-connection/client/rest/rest.go @@ -56,7 +56,8 @@ type ConnectionOpenAckReq struct { // ConnectionOpenConfirmReq defines the properties of a connection open confirm request's body. type ConnectionOpenConfirmReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - ProofAck commitment.ProofI `json:"proof_ack" yaml:"proof_ack"` - ProofHeight uint64 `json:"proof_height" yaml:"proof_height"` + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + ProofAck commitment.ProofI `json:"proof_ack" yaml:"proof_ack"` + ProofHeight uint64 `json:"proof_height" yaml:"proof_height"` + ConsensusHeight uint64 `json:"consensus_height" yaml:"consensus_height"` } diff --git a/x/ibc/03-connection/client/rest/tx.go b/x/ibc/03-connection/client/rest/tx.go index f11abbccb111..095a8e77a260 100644 --- a/x/ibc/03-connection/client/rest/tx.go +++ b/x/ibc/03-connection/client/rest/tx.go @@ -191,7 +191,7 @@ func connectionOpenConfirmHandlerFn(cliCtx context.CLIContext) http.HandlerFunc // create the message msg := types.NewMsgConnectionOpenConfirm( - connectionID, req.ProofAck, req.ProofHeight, fromAddr, + connectionID, req.ProofAck, req.ProofHeight, req.ConsensusHeight, fromAddr, ) if err := msg.ValidateBasic(); err != nil { diff --git a/x/ibc/03-connection/handler.go b/x/ibc/03-connection/handler.go index b7c9eae08d3f..92873e10d054 100644 --- a/x/ibc/03-connection/handler.go +++ b/x/ibc/03-connection/handler.go @@ -87,7 +87,7 @@ func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgC // HandleMsgConnectionOpenConfirm defines the sdk.Handler for MsgConnectionOpenConfirm func HandleMsgConnectionOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg types.MsgConnectionOpenConfirm) (*sdk.Result, error) { - err := k.ConnOpenConfirm(ctx, msg.ConnectionID, msg.ProofAck, msg.ProofHeight) + err := k.ConnOpenConfirm(ctx, msg.ConnectionID, msg.ProofAck, msg.ProofHeight, msg.ConsensusHeight) if err != nil { return nil, err } diff --git a/x/ibc/03-connection/keeper/handshake.go b/x/ibc/03-connection/keeper/handshake.go index f2826ba0a548..f8eaf98db5c3 100644 --- a/x/ibc/03-connection/keeper/handshake.go +++ b/x/ibc/03-connection/keeper/handshake.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -86,19 +87,16 @@ func (k Keeper) ConnOpenTry( } // XXX: blocked by #5475 - // err = k.VerifyClientConsensusState( + // if err := k.VerifyClientConsensusState( // ctx, proofHeight, proofInit, expectedConsensusState, - // ) - // if err != nil { + // ); err != nil { // return err // } previousConnection, found := k.GetConnection(ctx, connectionID) - if found { - return sdkerrors.Wrap(types.ErrConnectionExists, "cannot relay connection attempt") - } else if !(previousConnection.State == exported.INIT && + if found && !(previousConnection.State == exported.INIT && previousConnection.Counterparty.ConnectionID == counterparty.ConnectionID && - previousConnection.Counterparty.Prefix == counterparty.Prefix && + bytes.Equal(previousConnection.Counterparty.Prefix.Bytes(), counterparty.Prefix.Bytes()) && previousConnection.ClientID == clientID && previousConnection.Counterparty.ClientID == counterparty.ClientID && previousConnection.Versions[0] == version) { @@ -169,10 +167,9 @@ func (k Keeper) ConnOpenAck( } // XXX: blocked by #5475 - // err = k.VerifyClientConsensusState( + // if err := k.VerifyClientConsensusState( // ctx, connection, proofHeight, proofInit, expectedConsensusState, - // ) - // if err != nil { + // ); err != nil { // return err // } @@ -191,7 +188,8 @@ func (k Keeper) ConnOpenConfirm( ctx sdk.Context, connectionID string, proofAck commitment.ProofI, - proofHeight uint64, + proofHeight, + consensusHeight uint64, ) error { connection, found := k.GetConnection(ctx, connectionID) if !found { @@ -205,8 +203,7 @@ func (k Keeper) ConnOpenConfirm( ) } - // NOTE: should be safe to use proofHeight here - expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, proofHeight) + expectedConsensusState, found := k.clientKeeper.GetConsensusState(ctx, connection.ClientID, consensusHeight) if !found { return clienttypes.ErrConsensusStateNotFound } diff --git a/x/ibc/03-connection/keeper/handshake_test.go b/x/ibc/03-connection/keeper/handshake_test.go index dd47a15b0163..69eb1335235e 100644 --- a/x/ibc/03-connection/keeper/handshake_test.go +++ b/x/ibc/03-connection/keeper/handshake_test.go @@ -1,234 +1,254 @@ package keeper_test -// import ( -// "fmt" - -// abci "github.com/tendermint/tendermint/abci/types" - -// connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" -// "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" -// tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" -// commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -// ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" -// ) - -// func (suite *KeeperTestSuite) TestConnOpenInit() { -// suite.createClient(testClientID1) -// counterparty := connection.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) - -// success := func() error { -// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) -// suite.NoError(err) - -// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) -// suite.True(existed) - -// expectConn := connection.ConnectionEnd{ -// State: exported.INIT, -// ClientID: testClientID1, -// Counterparty: counterparty, -// Versions: connection.GetCompatibleVersions(), -// } -// suite.EqualValues(expectConn, conn) - -// return nil -// } - -// connectionExists := func() error { -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) -// } - -// var testCases = []testCase{ -// {success, true, "success"}, -// {connectionExists, false, "connection already exists"}, -// } - -// for _, tc := range testCases { -// if tc.expectPass { -// suite.Require().NoError(tc.fun(), tc.msg) -// } else { -// suite.Require().Error(tc.fun(), tc.msg) -// } -// } -// } - -// func (suite *KeeperTestSuite) TestConnOpenTry() { -// suite.createClient(testClientID2) -// suite.createClient(testClientID1) -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) - -// connectionKey := ibctypes.KeyConnection(testConnectionID2) - -// proofInit, proofHeight := suite.queryProof(connectionKey) -// consensusKey := ibctypes.KeyConsensusState(testClientID2, uint64(proofHeight)) -// proofConsensus, consensusHeight := suite.queryProof(consensusKey) - -// invalidProof := func() error { -// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, -// testConnectionID1, counterparty, testClientID1, -// connection.GetCompatibleVersions(), -// proofInit, proofConsensus, -// uint64(proofHeight), uint64(consensusHeight)) -// } - -// success := func() error { -// suite.updateClient(testClientID1) - -// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) -// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, -// testConnectionID1, counterparty, testClientID1, -// connection.GetCompatibleVersions(), -// proofInit, proofConsensus, -// uint64(proofHeight), uint64(consensusHeight)) -// suite.NoError(err) - -// //check connection state -// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) -// suite.True(existed) -// suite.Equal(exported.TRYOPEN.String(), conn.State.String(), "invalid connection state") -// return nil -// } - -// connectionExists := func() error { -// suite.updateClient(testClientID1) -// counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry(suite.ctx, -// testConnectionID1, counterparty, testClientID1, -// connection.GetCompatibleVersions(), -// proofInit, proofConsensus, -// uint64(proofHeight), uint64(consensusHeight)) -// } - -// var testCases = []testCase{ -// {invalidProof, false, "invalid proof"}, -// {connectionExists, false, "connection already exists"}, -// {success, true, "success"}, -// } - -// for _, tc := range testCases { -// if tc.expectPass { -// suite.Require().NoError(tc.fun(), tc.msg) -// } else { -// suite.Require().Error(tc.fun(), tc.msg) -// } -// } - -// } - -// func (suite *KeeperTestSuite) TestConnOpenAck() { -// suite.createClient(testClientID2) -// suite.createClient(testClientID1) - -// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) -// connectionKey := ibctypes.KeyConnection(testConnectionID1) - -// proofTry, proofHeight := suite.queryProof(connectionKey) -// consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(proofHeight)) -// proofConsensus, consensusHeight := suite.queryProof(consensusKey) - -// connectionNotFound := func() error { -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) -// } - -// invalidConnectionState := func() error { -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) -// } - -// invalidVersion := func() error { -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, "1.0.1", proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) -// } - -// invalidProof := func() error { -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) -// } - -// success := func() error { -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) -// suite.updateClient(testClientID2) -// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck(suite.ctx, testConnectionID2, connection.GetCompatibleVersions()[0], proofTry, proofConsensus, uint64(proofHeight), uint64(consensusHeight)) -// suite.NoError(err) - -// //check connection state -// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID2) -// suite.True(existed) -// suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") -// return nil -// } - -// var testCases = []testCase{ -// {connectionNotFound, false, "connection not exists"}, -// {invalidConnectionState, false, "invalid connection state"}, -// {invalidVersion, false, "invalid version"}, -// {invalidProof, false, "invalid proof"}, -// {success, true, ""}, -// } - -// for _, tc := range testCases { -// if tc.expectPass { -// suite.Require().NoError(tc.fun(), tc.msg) -// } else { -// suite.Require().Error(tc.fun(), tc.msg) -// } -// } -// } - -// func (suite *KeeperTestSuite) TestConnOpenConfirm() { -// suite.createClient(testClientID2) -// suite.createClient(testClientID1) -// suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.OPEN) - -// connKey := ibctypes.KeyConnection(testConnectionID2) -// proof, h := suite.queryProof(connKey) - -// connectionNotFound := func() error { -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) -// } - -// invalidConnectionState := func() error { -// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) -// } - -// invalidProof := func() error { -// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) -// return suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) -// } - -// success := func() error { -// suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.TRYOPEN) -// suite.updateClient(testClientID1) -// proof, h = suite.queryProof(connKey) -// err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm(suite.ctx, testConnectionID1, proof, uint64(h)) -// suite.NoError(err) - -// conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) -// suite.True(existed) -// suite.Equal(exported.OPEN.String(), conn.State.String(), "invalid connection state") -// return nil -// } - -// var testCases = []testCase{ -// {connectionNotFound, false, "connection not exists"}, -// {invalidConnectionState, false, "invalid connection state"}, -// {invalidProof, false, "invalid proof"}, -// {success, true, ""}, -// } - -// for _, tc := range testCases { -// if tc.expectPass { -// suite.Require().NoError(tc.fun(), tc.msg) -// } else { -// suite.Require().Error(tc.fun(), tc.msg) -// } -// } -// } - -// type testCase = struct { -// fun func() error -// expectPass bool -// msg string -// } +import ( + "fmt" + + connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" +) + +// TestConnOpenInit - Chain A (ID #1) initializes (INIT state) a connection with +// Chain B (ID #2) which is yet UNINITIALIZED +func (suite *KeeperTestSuite) TestConnOpenInit() { + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID1) + }, true}, + {"connection already exists", func() { + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + }, false}, + {"couldn't add connection to client", func() {}, false}, + } + + counterparty := connection.NewCounterparty(testClientID2, testConnectionID2, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) + + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.ctx, testConnectionID1, testClientID1, counterparty) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} + +// TestConnOpenTry - Chain B (ID #2) calls ConnOpenTry to verify the state of +// connection on Chain A (ID #1) is INIT +func (suite *KeeperTestSuite) TestConnOpenTry() { + counterparty := connection.NewCounterparty( + testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + + consensusHeight := int64(0) + + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID1) // height = 2 + suite.createClient(testClientID2) + consensusHeight = suite.ctx.BlockHeight() // height = 3 + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.updateClient(testClientID1) + suite.updateClient(testClientID2) + }, true}, + {"consensus state not found", func() {}, false}, + {"connection state verification invalid", func() { + suite.createClient(testClientID1) + suite.createClient(testClientID2) + consensusHeight = suite.ctx.BlockHeight() + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.UNINITIALIZED) + suite.updateClient(testClientID1) + }, false}, + {"invalid previous connection", func() { + suite.createClient(testClientID1) + suite.createClient(testClientID2) + consensusHeight = suite.ctx.BlockHeight() + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) + suite.updateClient(testClientID1) + }, false}, + {"couldn't add connection to client", func() { + suite.createClient(testClientID1) + consensusHeight = suite.ctx.BlockHeight() + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.INIT) + suite.updateClient(testClientID1) + }, false}, + } + + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + // connectionKey := ibctypes.KeyConnection(testConnectionID1) + // proofInit, proofHeight := suite.queryProof(connectionKey) + + // consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(proofHeight)) + // proofConsensus, consensusHeight := suite.queryProof(consensusKey) + + proofHeight := consensusHeight - 1 + + if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry( + suite.ctx, testConnectionID2, counterparty, testClientID2, + connection.GetCompatibleVersions(), validProof{}, validProof{}, + uint64(proofHeight), uint64(consensusHeight), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenTry( + suite.ctx, testConnectionID2, counterparty, testClientID2, + connection.GetCompatibleVersions(), invalidProof{}, validProof{}, + uint64(proofHeight), uint64(consensusHeight), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} + +// TestConnOpenAck - Chain A (ID #1) calls TestConnOpenAck to acknowledge (ACK state) +// the initialization (TRYINIT) of the connection on Chain B (ID #2). +func (suite *KeeperTestSuite) TestConnOpenAck() { + version := connection.GetCompatibleVersions()[0] + consensusHeight := int64(0) + + testCases := []struct { + msg string + version string + malleate func() + expPass bool + }{ + {"success", version, func() { + suite.createClient(testClientID1) + consensusHeight = suite.ctx.BlockHeight() + suite.createClient(testClientID2) + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.TRYOPEN) + suite.updateClient(testClientID1) + }, true}, + {"connection not found", version, func() {}, false}, + {"connection state is not INIT", version, func() { + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.UNINITIALIZED) + suite.updateClient(testClientID1) + }, false}, + {"incompatible IBC versions", "2.0", func() { + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.updateClient(testClientID1) + }, false}, + {"consensus state not found", version, func() { + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.updateClient(testClientID1) + }, false}, + {"connection state verification failed", version, func() { + suite.createClient(testClientID1) + consensusHeight = suite.ctx.BlockHeight() + suite.createClient(testClientID2) + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) + suite.updateClient(testClientID1) + }, false}, + } + + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + // connectionKey := ibctypes.KeyConnection(testConnectionID2) + // proofTry, proofHeight := suite.queryProof(connectionKey) + + // consensusKey := ibctypes.KeyConsensusState(testClientID2, uint64(proofHeight)) + // proofConsensus, consensusHeight := suite.queryProof(consensusKey) + + proofHeight := consensusHeight - 1 + + if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck( + suite.ctx, testConnectionID1, tc.version, validProof{}, validProof{}, + uint64(proofHeight), uint64(consensusHeight), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenAck( + suite.ctx, testConnectionID1, tc.version, invalidProof{}, validProof{}, + uint64(proofHeight), uint64(consensusHeight), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} + +// TestConnOpenAck - Chain B (ID #2) calls ConnOpenConfirm to confirm that +// Chain A (ID #1) state is now OPEN. +func (suite *KeeperTestSuite) TestConnOpenConfirm() { + consensusHeight := int64(0) + + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID1) + suite.createClient(testClientID2) + consensusHeight = suite.ctx.BlockHeight() + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.TRYOPEN) + suite.updateClient(testClientID1) + }, true}, + {"connection not found", func() {}, false}, + {"chain B's connection state is not TRYOPEN", func() { + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.UNINITIALIZED) + }, false}, + {"consensus state not found", func() { + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.TRYOPEN) + suite.updateClient(testClientID2) + }, false}, + {"connection state verification failed", func() { + suite.createClient(testClientID1) + suite.createClient(testClientID2) + consensusHeight = suite.ctx.BlockHeight() + suite.updateClient(testClientID1) + suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.INIT) + suite.createConnection(testConnectionID2, testConnectionID1, testClientID2, testClientID1, exported.TRYOPEN) + suite.updateClient(testClientID1) + }, false}, + } + + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + // connectionKey := ibctypes.KeyConnection(testConnectionID2) + // proofAck, proofHeight := suite.queryProof(connectionKey) + proofHeight := consensusHeight - 1 + + if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm( + suite.ctx, testConnectionID2, validProof{}, uint64(proofHeight), + uint64(consensusHeight), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ConnectionKeeper.ConnOpenConfirm( + suite.ctx, testConnectionID2, invalidProof{}, uint64(proofHeight), + uint64(consensusHeight), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} + +type testCase = struct { + msg string + malleate func() + expPass bool +} diff --git a/x/ibc/03-connection/keeper/keeper.go b/x/ibc/03-connection/keeper/keeper.go index b7b2dff3015b..0ef8d8d77b2d 100644 --- a/x/ibc/03-connection/keeper/keeper.go +++ b/x/ibc/03-connection/keeper/keeper.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" 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/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" @@ -111,9 +112,12 @@ func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.Connecti // addConnectionToClient is used to add a connection identifier to the set of // connections associated with a client. -// -// CONTRACT: client must already exist func (k Keeper) addConnectionToClient(ctx sdk.Context, clientID, connectionID string) error { + _, found := k.clientKeeper.GetClientState(ctx, clientID) + if !found { + return clienttypes.ErrClientNotFound + } + conns, found := k.GetClientConnectionPaths(ctx, clientID) if !found { conns = []string{} diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index 6ca3f54b4574..501127348442 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "fmt" "testing" @@ -25,17 +26,17 @@ import ( const ( clientType = clientexported.Tendermint storeKey = ibctypes.StoreKey - chainID = "test" + chainID = "gaia" testHeight = 10 - testClientID1 = "testclientid1" - testConnectionID1 = "connectionid1" + testClientID1 = "testclientidone" + testConnectionID1 = "connectionidone" - testClientID2 = "testclientid2" - testConnectionID2 = "connectionid2" + testClientID2 = "testclientidtwo" + testConnectionID2 = "connectionidtwo" - testClientID3 = "testclientid3" - testConnectionID3 = "connectionid3" + testClientID3 = "testclientidthree" + testConnectionID3 = "connectionidthree" ) type KeeperTestSuite struct { @@ -54,7 +55,7 @@ func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(isCheckTx) suite.cdc = app.Codec() - suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{ChainID: chainID, Height: testHeight}) + suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{ChainID: chainID, Height: 1}) suite.app = app privVal := tmtypes.NewMockPV() @@ -62,6 +63,7 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(privVal.GetPubKey(), 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.header = tendermint.CreateTestHeader(chainID, testHeight, suite.valSet, suite.valSet, []tmtypes.PrivValidator{privVal}) + suite.consensusState = tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), @@ -70,17 +72,17 @@ func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { res := suite.app.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", storeKey), - Data: key, - Prove: true, + Path: fmt.Sprintf("store/%s/key", storeKey), + Height: suite.app.LastBlockHeight(), + Data: key, + Prove: true, }) - height := res.Height proof := commitment.Proof{ Proof: res.Proof, } - return proof, height + return proof, res.Height } func (suite *KeeperTestSuite) createClient(clientID string) { @@ -96,7 +98,18 @@ func (suite *KeeperTestSuite) createClient(clientID string) { } _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, clientType, consensusState) - suite.NoError(err) + suite.Require().NoError(err) + + // _, _, err := simapp.SignCheckDeliver( + // suite.T(), + // suite.cdc, + // suite.app.BaseApp, + // suite.ctx.BlockHeader(), + // []sdk.Msg{clienttypes.NewMsgCreateClient(clientID, clientexported.ClientTypeTendermint, consState, accountAddress)}, + // []uint64{baseAccount.GetAccountNumber()}, + // []uint64{baseAccount.GetSequence()}, + // true, true, accountPrivKey, + // ) } func (suite *KeeperTestSuite) updateClient(clientID string) { @@ -104,15 +117,29 @@ func (suite *KeeperTestSuite) updateClient(clientID string) { suite.app.Commit() commitID := suite.app.LastCommitID() - height := suite.app.LastBlockHeight() + 1 - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) suite.ctx = suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 1) - state := tendermint.ConsensusState{ - Root: commitment.NewRoot(commitID.Hash), + consensusState := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), + ValidatorSetHash: suite.valSet.Hash(), } - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, clientID, uint64(suite.app.LastBlockHeight()), state) + suite.app.IBCKeeper.ClientKeeper.SetConsensusState( + suite.ctx, clientID, uint64(suite.app.LastBlockHeight()), consensusState, + ) + + // _, _, err := simapp.SignCheckDeliver( + // suite.T(), + // suite.cdc, + // suite.app.BaseApp, + // suite.ctx.BlockHeader(), + // []sdk.Msg{clienttypes.NewMsgUpdateClient(clientID, suite.header, accountAddress)}, + // []uint64{baseAccount.GetAccountNumber()}, + // []uint64{baseAccount.GetSequence()}, + // true, true, accountPrivKey, + // ) + // suite.Require().NoError(err) } func (suite *KeeperTestSuite) createConnection( @@ -148,14 +175,14 @@ func TestKeeperTestSuite(t *testing.T) { func (suite *KeeperTestSuite) TestSetAndGetConnection() { _, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) - suite.False(existed) + suite.Require().False(existed) counterparty := types.NewCounterparty(testClientID1, testConnectionID1, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) expConn := types.NewConnectionEnd(exported.INIT, testClientID1, counterparty, types.GetCompatibleVersions()) suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnectionID1, expConn) conn, existed := suite.app.IBCKeeper.ConnectionKeeper.GetConnection(suite.ctx, testConnectionID1) - suite.True(existed) - suite.EqualValues(expConn, conn) + suite.Require().True(existed) + suite.Require().EqualValues(expConn, conn) } func (suite *KeeperTestSuite) TestSetAndGetClientConnectionPaths() { @@ -186,5 +213,60 @@ func (suite KeeperTestSuite) TestGetAllConnections() { connections := suite.app.IBCKeeper.ConnectionKeeper.GetAllConnections(suite.ctx) suite.Require().Len(connections, len(expConnections)) - suite.Require().Equal(expConnections, connections) + suite.Require().ElementsMatch(expConnections, connections) +} + +// Mocked types +// TODO: fix tests and replace for real proofs + +var ( + _ commitment.ProofI = validProof{} + _ commitment.ProofI = invalidProof{} +) + +type ( + validProof struct{} + invalidProof struct{} +) + +func (validProof) GetCommitmentType() commitment.Type { + return commitment.Merkle +} + +func (validProof) VerifyMembership( + root commitment.RootI, path commitment.PathI, value []byte) error { + return nil +} + +func (validProof) VerifyNonMembership(root commitment.RootI, path commitment.PathI) error { + return nil +} + +func (validProof) ValidateBasic() error { + return nil +} + +func (validProof) IsEmpty() bool { + return false +} + +func (invalidProof) GetCommitmentType() commitment.Type { + return commitment.Merkle +} + +func (invalidProof) VerifyMembership( + root commitment.RootI, path commitment.PathI, value []byte) error { + return errors.New("proof failed") +} + +func (invalidProof) VerifyNonMembership(root commitment.RootI, path commitment.PathI) error { + return errors.New("proof failed") +} + +func (invalidProof) ValidateBasic() error { + return errors.New("invalid proof") +} + +func (invalidProof) IsEmpty() bool { + return true } diff --git a/x/ibc/03-connection/keeper/verify_test.go b/x/ibc/03-connection/keeper/verify_test.go index 289926094239..7e20b27b9557 100644 --- a/x/ibc/03-connection/keeper/verify_test.go +++ b/x/ibc/03-connection/keeper/verify_test.go @@ -6,7 +6,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) const ( @@ -18,8 +17,15 @@ const ( ) func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { - counterparty := types.Counterparty{Prefix: suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()} - connection1 := types.ConnectionEnd{ClientID: testClientID1, Counterparty: counterparty} + counterparty := types.NewCounterparty( + testClientID2, testConnectionID2, + suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), + ) + + connection1 := types.NewConnectionEnd( + exported.UNINITIALIZED, testClientID1, counterparty, + types.GetCompatibleVersions(), + ) cases := []struct { msg string @@ -44,18 +50,21 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { tc.malleate() suite.updateClient(testClientID1) - consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(suite.ctx.BlockHeight()-1)) - - proof, proofHeight := suite.queryProof(consensusKey) + proofHeight := suite.ctx.BlockHeight() - 1 - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( - suite.ctx, tc.connection, uint64(proofHeight), proof, suite.consensusState, - ) + // TODO: remove mocked types and uncomment + // consensusKey := ibctypes.KeyConsensusState(testClientID1, uint64(suite.app.LastBlockHeight())) + // proof, proofHeight := suite.queryProof(consensusKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + suite.ctx, tc.connection, uint64(proofHeight), validProof{}, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().False(proof.IsEmpty()) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + suite.ctx, tc.connection, uint64(proofHeight), invalidProof{}, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -63,9 +72,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { } func (suite *KeeperTestSuite) TestVerifyConnectionState() { - - connectionKey := ibctypes.KeyConnection(testConnectionID1) - + // connectionKey := ibctypes.KeyConnection(testConnectionID1) cases := []struct { msg string malleate func() @@ -89,16 +96,18 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(connectionKey) - fmt.Println(proof) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( - suite.ctx, uint64(proofHeight), proof, testConnectionID1, connection, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(connectionKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( + suite.ctx, uint64(proofHeight), validProof{}, testConnectionID1, connection, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyConnectionState( + suite.ctx, uint64(proofHeight), invalidProof{}, testConnectionID1, connection, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -106,7 +115,7 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { } func (suite *KeeperTestSuite) TestVerifyChannelState() { - channelKey := ibctypes.KeyChannel(testPort1, testChannel1) + // channelKey := ibctypes.KeyChannel(testPort1, testChannel1) cases := []struct { msg string @@ -135,16 +144,20 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { ) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(channelKey) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( - suite.ctx, connection, uint64(proofHeight), proof, testPort1, - testChannel1, channel, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(channelKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( + suite.ctx, connection, uint64(proofHeight), validProof{}, testPort1, + testChannel1, channel, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyChannelState( + suite.ctx, connection, uint64(proofHeight), invalidProof{}, testPort1, + testChannel1, channel, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -152,8 +165,7 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { } func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { - - commitmentKey := ibctypes.KeyPacketCommitment(testPort1, testChannel1, 1) + // commitmentKey := ibctypes.KeyPacketCommitment(testPort1, testChannel1, 1) commitmentBz := []byte("commitment") cases := []struct { @@ -180,16 +192,20 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { suite.app.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.ctx, testPort1, testChannel1, 1, commitmentBz) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(commitmentKey) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( - suite.ctx, connection, uint64(proofHeight), proof, testPort1, - testChannel1, 1, commitmentBz, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(commitmentKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( + suite.ctx, connection, uint64(proofHeight), validProof{}, testPort1, + testChannel1, 1, commitmentBz, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( + suite.ctx, connection, uint64(proofHeight), invalidProof{}, testPort1, + testChannel1, 1, commitmentBz, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -197,7 +213,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { } func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { - packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) + // packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) ack := []byte("acknowledgement") cases := []struct { @@ -224,16 +240,20 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { suite.app.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.ctx, testPort1, testChannel1, 1, ack) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(packetAckKey) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( - suite.ctx, connection, uint64(proofHeight), proof, testPort1, - testChannel1, 1, ack, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(packetAckKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( + suite.ctx, connection, uint64(proofHeight), validProof{}, testPort1, + testChannel1, 1, ack, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( + suite.ctx, connection, uint64(proofHeight), invalidProof{}, testPort1, + testChannel1, 1, ack, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -241,7 +261,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { } func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { - packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) + // packetAckKey := ibctypes.KeyPacketAcknowledgement(testPort1, testChannel1, 1) cases := []struct { msg string @@ -266,16 +286,20 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { connection := suite.createConnection(testConnectionID1, testConnectionID2, testClientID1, testClientID2, exported.OPEN) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(packetAckKey) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( - suite.ctx, connection, uint64(proofHeight), proof, testPort1, - testChannel1, 1, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(packetAckKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( + suite.ctx, connection, uint64(proofHeight), validProof{}, testPort1, + testChannel1, 1, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence( + suite.ctx, connection, uint64(proofHeight), invalidProof{}, testPort1, + testChannel1, 1, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) @@ -283,7 +307,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() { } func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { - nextSeqRcvKey := ibctypes.KeyNextSequenceRecv(testPort1, testChannel1) + // nextSeqRcvKey := ibctypes.KeyNextSequenceRecv(testPort1, testChannel1) cases := []struct { msg string @@ -309,16 +333,20 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { suite.app.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.ctx, testPort1, testChannel1, 1) suite.updateClient(testClientID1) - proof, proofHeight := suite.queryProof(nextSeqRcvKey) - - err := suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( - suite.ctx, connection, uint64(proofHeight), proof, testPort1, - testChannel1, 1, suite.consensusState, - ) + proofHeight := suite.ctx.BlockHeight() - 1 + // proof, proofHeight := suite.queryProof(nextSeqRcvKey) if tc.expPass { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( + suite.ctx, connection, uint64(proofHeight), validProof{}, testPort1, + testChannel1, 1, suite.consensusState, + ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { + err := suite.app.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( + suite.ctx, connection, uint64(proofHeight), invalidProof{}, testPort1, + testChannel1, 1, suite.consensusState, + ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) diff --git a/x/ibc/03-connection/types/msgs.go b/x/ibc/03-connection/types/msgs.go index bf0731c3bb66..cde1eb87722b 100644 --- a/x/ibc/03-connection/types/msgs.go +++ b/x/ibc/03-connection/types/msgs.go @@ -249,21 +249,24 @@ var _ sdk.Msg = MsgConnectionOpenConfirm{} // MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to acknowledge // the change of connection state to OPEN on Chain A. type MsgConnectionOpenConfirm struct { - ConnectionID string `json:"connection_id"` - ProofAck commitment.ProofI `json:"proof_ack"` // proof for the change of the connection state on Chain A: `INIT -> OPEN` - ProofHeight uint64 `json:"proof_height"` - Signer sdk.AccAddress `json:"signer"` + ConnectionID string `json:"connection_id"` + ProofAck commitment.ProofI `json:"proof_ack"` // proof for the change of the connection state on Chain A: `INIT -> OPEN` + ProofHeight uint64 `json:"proof_height"` + ConsensusHeight uint64 `json:"consensus_height"` + Signer sdk.AccAddress `json:"signer"` } // NewMsgConnectionOpenConfirm creates a new MsgConnectionOpenConfirm instance func NewMsgConnectionOpenConfirm( - connectionID string, proofAck commitment.ProofI, proofHeight uint64, signer sdk.AccAddress, + connectionID string, proofAck commitment.ProofI, proofHeight, consensusHeight uint64, + signer sdk.AccAddress, ) MsgConnectionOpenConfirm { return MsgConnectionOpenConfirm{ - ConnectionID: connectionID, - ProofAck: proofAck, - ProofHeight: proofHeight, - Signer: signer, + ConnectionID: connectionID, + ProofAck: proofAck, + ProofHeight: proofHeight, + ConsensusHeight: consensusHeight, + Signer: signer, } } @@ -291,6 +294,9 @@ func (msg MsgConnectionOpenConfirm) ValidateBasic() error { if msg.ProofHeight == 0 { return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "proof height must be > 0") } + if msg.ConsensusHeight == 0 { + return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "consensus height must be > 0") + } if msg.Signer.Empty() { return sdkerrors.ErrInvalidAddress } diff --git a/x/ibc/03-connection/types/msgs_test.go b/x/ibc/03-connection/types/msgs_test.go index 8bca120769ba..ae6c73e55889 100644 --- a/x/ibc/03-connection/types/msgs_test.go +++ b/x/ibc/03-connection/types/msgs_test.go @@ -183,12 +183,13 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenConfirm() { signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") testMsgs := []MsgConnectionOpenConfirm{ - NewMsgConnectionOpenConfirm("test/conn1", suite.proof, 10, signer), - NewMsgConnectionOpenConfirm("ibcconntest", nil, 10, signer), - NewMsgConnectionOpenConfirm("ibcconntest", commitment.Proof{Proof: nil}, 10, signer), - NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 0, signer), - NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 10, nil), - NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 10, signer), + NewMsgConnectionOpenConfirm("test/conn1", suite.proof, 10, 10, signer), + NewMsgConnectionOpenConfirm("ibcconntest", nil, 10, 10, signer), + NewMsgConnectionOpenConfirm("ibcconntest", commitment.Proof{Proof: nil}, 10, 10, signer), + NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 0, 10, signer), + NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 10, 0, signer), + NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 10, 10, nil), + NewMsgConnectionOpenConfirm("ibcconntest", suite.proof, 10, 10, signer), } var testCases = []struct { @@ -200,8 +201,9 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenConfirm() { {testMsgs[1], false, "empty proofTry"}, {testMsgs[2], false, "empty proofTry"}, {testMsgs[3], false, "invalid proofHeight"}, - {testMsgs[4], false, "empty signer"}, - {testMsgs[5], true, "success"}, + {testMsgs[4], false, "invalid consensusHeight"}, + {testMsgs[5], false, "empty signer"}, + {testMsgs[6], true, "success"}, } for i, tc := range testCases { diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index 940912448b86..1279b6d0ba25 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -85,9 +85,7 @@ func (k Keeper) ChanOpenTry( // channel identifier and connection hop length checked on msg.ValidateBasic() previousChannel, found := k.GetChannel(ctx, portID, channelID) - if found { - return sdkerrors.Wrap(types.ErrChannelExists, channelID) - } else if !(previousChannel.State == exported.INIT && + if found && !(previousChannel.State == exported.INIT && previousChannel.Ordering == order && previousChannel.Counterparty.PortID == counterparty.PortID && previousChannel.Counterparty.ChannelID == counterparty.ChannelID && @@ -143,7 +141,7 @@ func (k Keeper) ChanOpenTry( ctx, connectionEnd, proofHeight, proofInit, counterparty.PortID, counterparty.ChannelID, expectedChannel, consensusState, ); err != nil { - return sdkerrors.Wrap(err, "channel membership verification failed") + return err } k.SetChannel(ctx, portID, channelID, channel) @@ -222,7 +220,7 @@ func (k Keeper) ChanOpenAck( channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, ); err != nil { - return sdkerrors.Wrap(err, "channel membership verification failed") + return err } channel.State = exported.OPEN @@ -249,7 +247,7 @@ func (k Keeper) ChanOpenConfirm( if channel.State != exported.TRYOPEN { return sdkerrors.Wrapf( types.ErrInvalidChannelState, - "channel state is not OPENTRY (got %s)", channel.State.String(), + "channel state is not TRYOPEN (got %s)", channel.State.String(), ) } @@ -300,7 +298,7 @@ func (k Keeper) ChanOpenConfirm( channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, ); err != nil { - return sdkerrors.Wrap(err, "channel membership verification failed") + return err } channel.State = exported.OPEN @@ -424,7 +422,7 @@ func (k Keeper) ChanCloseConfirm( channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, ); err != nil { - return sdkerrors.Wrap(err, "channel membership verification failed") + return err } channel.State = exported.CLOSED diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 5c4d2d297062..aee68d6cd00c 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -3,330 +3,423 @@ package keeper_test import ( "fmt" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) -func (suite *KeeperTestSuite) createClient() { - suite.app.Commit() - commitID := suite.app.LastCommitID() - - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) - - consensusState := tendermint.ConsensusState{ - Root: commitment.NewRoot(commitID.Hash), - ValidatorSetHash: suite.valSet.Hash(), - } - - _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, testClient, testClientType, consensusState) - suite.NoError(err) -} - -func (suite *KeeperTestSuite) updateClient() { - // always commit and begin a new block on updateClient - suite.app.Commit() - commitID := suite.app.LastCommitID() - - height := suite.app.LastBlockHeight() + 1 - suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) - suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) - - state := tendermint.ConsensusState{ - Root: commitment.NewRoot(commitID.Hash), - } - - suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClient, uint64(height-1), state) - csi, _ := suite.app.IBCKeeper.ClientKeeper.GetClientState(suite.ctx, testClient) - cs, _ := csi.(tendermint.ClientState) - cs.LatestHeight = uint64(height - 1) - suite.app.IBCKeeper.ClientKeeper.SetClientState(suite.ctx, cs) -} - -func (suite *KeeperTestSuite) createConnection(state connectionexported.State) { - connection := connection.ConnectionEnd{ - State: state, - ClientID: testClient, - Counterparty: connection.Counterparty{ - ClientID: testClient, - ConnectionID: testConnection, - Prefix: suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(), - }, - Versions: connection.GetCompatibleVersions(), - } - - suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, testConnection, connection) -} - -func (suite *KeeperTestSuite) createChannel(portID string, chanID string, connID string, counterpartyPort string, counterpartyChan string, state exported.State) { - channel := types.Channel{ - State: state, - Ordering: testChannelOrder, - Counterparty: types.Counterparty{ - PortID: counterpartyPort, - ChannelID: counterpartyChan, - }, - ConnectionHops: []string{connID}, - Version: testChannelVersion, - } - - suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, portID, chanID, channel) -} - -func (suite *KeeperTestSuite) deleteChannel(portID string, chanID string) { - store := suite.ctx.KVStore(suite.app.GetKey(ibctypes.StoreKey)) - store.Delete(ibctypes.KeyChannel(portID, chanID)) -} - -func (suite *KeeperTestSuite) bindPort(portID string) sdk.CapabilityKey { - return suite.app.IBCKeeper.PortKeeper.BindPort(portID) -} - -func (suite *KeeperTestSuite) queryProof(key []byte) (proof commitment.Proof, height int64) { - res := suite.app.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibctypes.StoreKey), - Data: key, - Prove: true, - }) - - height = res.Height - proof = commitment.Proof{ - Proof: res.Proof, - } - - return -} - func (suite *KeeperTestSuite) TestChanOpenInit() { counterparty := types.NewCounterparty(testPort2, testChannel2) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) - err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) - suite.Error(err) // channel has already exist - - suite.deleteChannel(testPort1, testChannel1) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.UNINITIALIZED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) - suite.Error(err) // invalid connection state - - suite.createConnection(connectionexported.OPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel1, counterparty, testChannelVersion) - suite.NoError(err) // successfully executed + testCases := []testCase{ + {"success", func() { + suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.INIT, + ) + }, true}, + {"channel already exists", func() { + suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.INIT, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"connection doesn't exist", func() {}, false}, + {"connection is UNINITIALIZED", func() { + suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.UNINITIALIZED, + ) + }, false}, + } - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) - suite.True(found) - suite.Equal(exported.INIT.String(), channel.State.String(), "invalid channel state") + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenInit( + suite.ctx, exported.ORDERED, []string{testConnectionID1}, + testPort1, testChannel1, counterparty, testChannelVersion, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } } func (suite *KeeperTestSuite) TestChanOpenTry() { counterparty := types.NewCounterparty(testPort1, testChannel1) - suite.bindPort(testPort2) - channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.INIT) - suite.updateClient() - proofInit, proofHeight := suite.queryProof(channelKey) - err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.Error(err) // channel has already exist - - suite.deleteChannel(testPort2, testChannel2) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort1, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.Error(err) // unauthenticated port - - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.UNINITIALIZED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.Error(err) // invalid connection state - suite.createConnection(connectionexported.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.TRYOPEN) - suite.updateClient() - proofInit, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.Error(err) // channel membership verification failed due to invalid counterparty - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) - suite.updateClient() - proofInit, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry(suite.ctx, testChannelOrder, []string{testConnection}, testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, proofInit, uint64(proofHeight)) - suite.NoError(err) // successfully executed + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + }, true}, + {"previous channel with invalid state", func() { + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.UNINITIALIZED, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"connection doesn't exist", func() {}, false}, + {"connection is not OPEN", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.INIT, + ) + }, false}, + {"consensus state not found", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + }, false}, + {"channel verification failed", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + }, false}, + } - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) - suite.True(found) - suite.Equal(exported.TRYOPEN.String(), channel.State.String(), "invalid channel state") + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + if tc.expPass { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry( + suite.ctx, exported.ORDERED, []string{testConnectionID2}, + testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, + validProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenTry( + suite.ctx, exported.ORDERED, []string{testConnectionID2}, + testPort2, testChannel2, counterparty, testChannelVersion, testChannelVersion, + invalidProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } } func (suite *KeeperTestSuite) TestChanOpenAck() { - suite.bindPort(testPort1) - channelKey := ibctypes.KeyChannel(testPort2, testChannel2) - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) - suite.updateClient() - proofTry, proofHeight := suite.queryProof(channelKey) - err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // channel does not exist - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // invalid channel state - - suite.createChannel(testPort2, testChannel1, testConnection, testPort1, testChannel2, exported.INIT) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort2, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // unauthenticated port - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.INIT) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.UNINITIALIZED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // invalid connection state - - suite.createConnection(connectionexported.OPEN) - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) - suite.updateClient() - proofTry, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.Error(err) // channel membership verification failed due to invalid counterparty - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) - suite.updateClient() - proofTry, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck(suite.ctx, testPort1, testChannel1, testChannelVersion, proofTry, uint64(proofHeight)) - suite.NoError(err) // successfully executed + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID1) + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.TRYOPEN, + exported.ORDERED, testConnectionID1, + ) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not INIT or TRYOPEN", func() { + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.UNINITIALIZED, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"connection not found", func() { + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.TRYOPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"connection is not OPEN", func() { + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.TRYOPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.TRYOPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"consensus state not found", func() { + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.TRYOPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"channel verification failed", func() { + suite.createClient(testClientID1) + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.TRYOPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + } - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) - suite.True(found) - suite.Equal(exported.OPEN.String(), channel.State.String(), "invalid channel state") + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + if tc.expPass { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck( + suite.ctx, testPort1, testChannel1, testChannelVersion, + validProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenAck( + suite.ctx, testPort1, testChannel1, testChannelVersion, + invalidProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } } func (suite *KeeperTestSuite) TestChanOpenConfirm() { - suite.bindPort(testPort2) - channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) - suite.updateClient() - proofAck, proofHeight := suite.queryProof(channelKey) - err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // channel does not exist - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // invalid channel state - - suite.createChannel(testPort1, testChannel2, testConnection, testPort2, testChannel1, exported.TRYOPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort1, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // unauthenticated port - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.TRYOPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.UNINITIALIZED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // invalid connection state - - suite.createConnection(connectionexported.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.TRYOPEN) - suite.updateClient() - proofAck, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.Error(err) // channel membership verification failed due to invalid counterparty - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) - suite.updateClient() - proofAck, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm(suite.ctx, testPort2, testChannel2, proofAck, uint64(proofHeight)) - suite.NoError(err) // successfully executed + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.TRYOPEN, + exported.ORDERED, testConnectionID2, + ) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not TRYOPEN", func() { + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.UNINITIALIZED, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"connection not found", func() { + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.TRYOPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"connection is not OPEN", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.TRYOPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.TRYOPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"consensus state not found", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.TRYOPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"channel verification failed", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.TRYOPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + } - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) - suite.True(found) - suite.Equal(exported.OPEN.String(), channel.State.String(), "invalid channel state") + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + if tc.expPass { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm( + suite.ctx, testPort2, testChannel2, + validProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ChannelKeeper.ChanOpenConfirm( + suite.ctx, testPort2, testChannel2, + invalidProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } } func (suite *KeeperTestSuite) TestChanCloseInit() { - suite.bindPort(testPort1) - - err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort2, testChannel1) - suite.Error(err) // authenticated port - - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) - suite.Error(err) // channel does not exist - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) - suite.Error(err) // channel is already closed - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.TRYOPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) - suite.Error(err) // invalid connection state - - suite.createConnection(connectionexported.OPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit(suite.ctx, testPort1, testChannel1) - suite.NoError(err) // successfully executed + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID1) + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.OPEN, + exported.ORDERED, testConnectionID1, + ) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is CLOSED", func() { + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.CLOSED, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"connection not found", func() { + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.OPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"connection is not OPEN", func() { + _ = suite.createConnection( + testConnectionID1, testConnectionID2, testClientID1, testClientID2, + connectionexported.TRYOPEN, + ) + _ = suite.createChannel( + testPort1, testChannel1, testPort2, testChannel2, exported.UNINITIALIZED, + exported.ORDERED, testConnectionID1, + ) + }, false}, + } - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort1, testChannel1) - suite.True(found) - suite.Equal(exported.CLOSED.String(), channel.State.String(), "invalid channel state") + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseInit( + suite.ctx, testPort1, testChannel1, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } } func (suite *KeeperTestSuite) TestChanCloseConfirm() { - suite.bindPort(testPort2) - channelKey := ibctypes.KeyChannel(testPort1, testChannel1) - - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) - suite.updateClient() - proofInit, proofHeight := suite.queryProof(channelKey) - err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort1, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // unauthenticated port - - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // channel does not exist - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.CLOSED) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // channel is already closed - - suite.createChannel(testPort2, testChannel2, testConnection, testPort1, testChannel1, exported.OPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // connection does not exist - - suite.createConnection(connectionexported.TRYOPEN) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // invalid connection state - - suite.createConnection(connectionexported.OPEN) - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.OPEN) - suite.updateClient() - proofInit, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.Error(err) // channel membership verification failed due to invalid counterparty + testCases := []testCase{ + {"success", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.OPEN, + exported.ORDERED, testConnectionID2, + ) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is CLOSED", func() { + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.CLOSED, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"connection not found", func() { + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.OPEN, + exported.ORDERED, testConnectionID1, + ) + }, false}, + {"connection is not OPEN", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.TRYOPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.OPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"consensus state not found", func() { + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.OPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + {"channel verification failed", func() { + suite.createClient(testClientID2) + _ = suite.createConnection( + testConnectionID2, testConnectionID1, testClientID2, testClientID1, + connectionexported.OPEN, + ) + _ = suite.createChannel( + testPort2, testChannel2, testPort1, testChannel1, exported.OPEN, + exported.ORDERED, testConnectionID2, + ) + }, false}, + } - suite.createChannel(testPort1, testChannel1, testConnection, testPort2, testChannel2, exported.CLOSED) - suite.updateClient() - proofInit, proofHeight = suite.queryProof(channelKey) - err = suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm(suite.ctx, testPort2, testChannel2, proofInit, uint64(proofHeight)) - suite.NoError(err) // successfully executed + for i, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + if tc.expPass { + err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm( + suite.ctx, testPort2, testChannel2, + validProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + err := suite.app.IBCKeeper.ChannelKeeper.ChanCloseConfirm( + suite.ctx, testPort2, testChannel2, + invalidProof{}, uint64(suite.ctx.BlockHeight()), + ) + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + }) + } +} - channel, found := suite.app.IBCKeeper.ChannelKeeper.GetChannel(suite.ctx, testPort2, testChannel2) - suite.True(found) - suite.Equal(exported.CLOSED.String(), channel.State.String(), "invalid channel state") +type testCase = struct { + msg string + malleate func() + expPass bool } diff --git a/x/ibc/04-channel/keeper/keeper_test.go b/x/ibc/04-channel/keeper/keeper_test.go index d5140e34cd72..b502bbfe7347 100644 --- a/x/ibc/04-channel/keeper/keeper_test.go +++ b/x/ibc/04-channel/keeper/keeper_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + "errors" + "fmt" "testing" "github.com/stretchr/testify/suite" @@ -11,17 +13,24 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // define constants used for testing const ( - testChainID = "test-chain-id" - testClient = "test-client" testClientType = clientexported.Tendermint - testConnection = "testconnection" + testClientID1 = "testclientidone" + testConnectionID1 = "connectionidone" + + testClientID2 = "testclientidtwo" + testConnectionID2 = "connectionidtwo" testPort1 = "firstport" testPort2 = "secondport" @@ -56,8 +65,6 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(privVal.GetPubKey(), 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - - suite.createClient() } func (suite *KeeperTestSuite) TestSetChannel() { @@ -71,7 +78,7 @@ func (suite *KeeperTestSuite) TestSetChannel() { PortID: testPort2, ChannelID: testChannel2, }, - ConnectionHops: []string{testConnection}, + ConnectionHops: []string{testConnectionID1}, Version: testChannelVersion, } suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, testPort1, testChannel1, channel) @@ -91,7 +98,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { State: exported.INIT, Ordering: testChannelOrder, Counterparty: counterparty3, - ConnectionHops: []string{testConnection}, + ConnectionHops: []string{testConnectionID1}, Version: testChannelVersion, } @@ -99,7 +106,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { State: exported.INIT, Ordering: testChannelOrder, Counterparty: counterparty1, - ConnectionHops: []string{testConnection}, + ConnectionHops: []string{testConnectionID1}, Version: testChannelVersion, } @@ -107,7 +114,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { State: exported.CLOSED, Ordering: testChannelOrder, Counterparty: counterparty2, - ConnectionHops: []string{testConnection}, + ConnectionHops: []string{testConnectionID1}, Version: testChannelVersion, } @@ -184,3 +191,138 @@ func (suite *KeeperTestSuite) TestSetPacketAcknowledgement() { func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } + +func (suite *KeeperTestSuite) createClient(clientID string) { + suite.app.Commit() + commitID := suite.app.LastCommitID() + + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1}}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + + consensusState := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), + ValidatorSetHash: suite.valSet.Hash(), + } + + _, err := suite.app.IBCKeeper.ClientKeeper.CreateClient(suite.ctx, clientID, testClientType, consensusState) + suite.Require().NoError(err) +} + +// nolint: unused +func (suite *KeeperTestSuite) updateClient() { + // always commit and begin a new block on updateClient + suite.app.Commit() + commitID := suite.app.LastCommitID() + + height := suite.app.LastBlockHeight() + 1 + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: height}}) + suite.ctx = suite.app.BaseApp.NewContext(false, abci.Header{Height: suite.app.LastBlockHeight()}) + + state := tendermint.ConsensusState{ + Root: commitment.NewRoot(commitID.Hash), + } + + suite.app.IBCKeeper.ClientKeeper.SetConsensusState(suite.ctx, testClientID1, uint64(height-1), state) + csi, _ := suite.app.IBCKeeper.ClientKeeper.GetClientState(suite.ctx, testClientID1) + cs, _ := csi.(tendermint.ClientState) + cs.LatestHeight = uint64(height - 1) + suite.app.IBCKeeper.ClientKeeper.SetClientState(suite.ctx, cs) +} + +func (suite *KeeperTestSuite) createConnection( + connID, counterpartyConnID, clientID, counterpartyClientID string, + state connectionexported.State, +) connectiontypes.ConnectionEnd { + counterparty := connectiontypes.NewCounterparty(counterpartyClientID, counterpartyConnID, suite.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix()) + connection := connectiontypes.ConnectionEnd{ + State: state, + ClientID: clientID, + Counterparty: counterparty, + Versions: connectiontypes.GetCompatibleVersions(), + } + suite.app.IBCKeeper.ConnectionKeeper.SetConnection(suite.ctx, connID, connection) + return connection +} + +func (suite *KeeperTestSuite) createChannel( + portID, channelID, counterpartyPortID, counterpartyChannelID string, + state exported.State, order exported.Order, connectionID string, +) types.Channel { + counterparty := types.NewCounterparty(counterpartyPortID, counterpartyChannelID) + channel := types.NewChannel(state, order, counterparty, + []string{connectionID}, "1.0", + ) + suite.app.IBCKeeper.ChannelKeeper.SetChannel(suite.ctx, portID, channelID, channel) + return channel +} + +// nolint: unused +func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { + res := suite.app.Query(abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", ibctypes.StoreKey), + Height: suite.app.LastBlockHeight(), + Data: key, + Prove: true, + }) + + proof := commitment.Proof{ + Proof: res.Proof, + } + + return proof, res.Height +} + +// Mocked types +// TODO: fix tests and replace for real proofs + +var ( + _ commitment.ProofI = validProof{} + _ commitment.ProofI = invalidProof{} +) + +type ( + validProof struct{} + invalidProof struct{} +) + +func (validProof) GetCommitmentType() commitment.Type { + return commitment.Merkle +} + +func (validProof) VerifyMembership( + root commitment.RootI, path commitment.PathI, value []byte) error { + return nil +} + +func (validProof) VerifyNonMembership(root commitment.RootI, path commitment.PathI) error { + return nil +} + +func (validProof) ValidateBasic() error { + return nil +} + +func (validProof) IsEmpty() bool { + return false +} + +func (invalidProof) GetCommitmentType() commitment.Type { + return commitment.Merkle +} + +func (invalidProof) VerifyMembership( + root commitment.RootI, path commitment.PathI, value []byte) error { + return errors.New("proof failed") +} + +func (invalidProof) VerifyNonMembership(root commitment.RootI, path commitment.PathI) error { + return errors.New("proof failed") +} + +func (invalidProof) ValidateBasic() error { + return errors.New("invalid proof") +} + +func (invalidProof) IsEmpty() bool { + return true +} diff --git a/x/ibc/04-channel/keeper/timeout.go b/x/ibc/04-channel/keeper/timeout.go index bb2ffb04741f..63b522d88b22 100644 --- a/x/ibc/04-channel/keeper/timeout.go +++ b/x/ibc/04-channel/keeper/timeout.go @@ -108,7 +108,7 @@ func (k Keeper) TimeoutPacket( } if err != nil { - return nil, sdkerrors.Wrap(err, "packet verification failed") + return nil, err } // NOTE: the remaining code is located on the TimeoutExecuted function @@ -216,7 +216,7 @@ func (k Keeper) TimeoutOnClose( channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedChannel, consensusState, ); err != nil { - return nil, sdkerrors.Wrap(err, "channel membership verification failed") + return nil, err } var err error @@ -239,7 +239,7 @@ func (k Keeper) TimeoutOnClose( } if err != nil { - return nil, sdkerrors.Wrap(err, "packet verification failed") + return nil, err } k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index af919587b13b..bfd3451205ee 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,8 +1,11 @@ package tendermint import ( + "fmt" + "github.com/cosmos/cosmos-sdk/codec" 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" connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" @@ -73,13 +76,17 @@ func (cs ClientState) VerifyClientConsensusState( return clienttypes.ErrClientFrozen } + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + bz, err := cdc.MarshalBinaryBare(consensusState) if err != nil { return err } - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienttypes.ErrFailedClientConsensusStateVerification + if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedClientConsensusStateVerification, err.Error()) } return nil @@ -100,6 +107,7 @@ func (cs ClientState) VerifyConnectionState( } if cs.LatestHeight < height { + fmt.Println(cs.LatestHeight, height) return ibctypes.ErrInvalidHeight } @@ -107,13 +115,17 @@ func (cs ClientState) VerifyConnectionState( return clienttypes.ErrClientFrozen } + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + bz, err := cdc.MarshalBinaryBare(connectionEnd) if err != nil { return err } - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienttypes.ErrFailedConnectionStateVerification + if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedConnectionStateVerification, err.Error()) } return nil @@ -142,13 +154,17 @@ func (cs ClientState) VerifyChannelState( return clienttypes.ErrClientFrozen } + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + bz, err := cdc.MarshalBinaryBare(channel) if err != nil { return err } - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienttypes.ErrFailedChannelStateVerification + if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedChannelStateVerification, err.Error()) } return nil @@ -177,8 +193,12 @@ func (cs ClientState) VerifyPacketCommitment( return clienttypes.ErrClientFrozen } - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); !ok { - return clienttypes.ErrFailedPacketCommitmentVerification + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + + if err := proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedPacketCommitmentVerification, err.Error()) } return nil @@ -207,8 +227,12 @@ func (cs ClientState) VerifyPacketAcknowledgement( return clienttypes.ErrClientFrozen } - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); !ok { - return clienttypes.ErrFailedPacketAckVerification + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + + if err := proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckVerification, err.Error()) } return nil @@ -236,8 +260,12 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( return clienttypes.ErrClientFrozen } - if ok := proof != nil && proof.VerifyNonMembership(consensusState.GetRoot(), path); !ok { - return clienttypes.ErrFailedPacketAckAbsenceVerification + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + + if err := proof.VerifyNonMembership(consensusState.GetRoot(), path); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckAbsenceVerification, err.Error()) } return nil @@ -265,10 +293,14 @@ func (cs ClientState) VerifyNextSequenceRecv( return clienttypes.ErrClientFrozen } + if proof == nil { + return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + } + bz := sdk.Uint64ToBigEndian(nextSequenceRecv) - if ok := proof != nil && proof.VerifyMembership(consensusState.GetRoot(), path, bz); !ok { - return clienttypes.ErrFailedNextSeqRecvVerification + if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedNextSeqRecvVerification, err.Error()) } return nil diff --git a/x/ibc/07-tendermint/client_state_test.go b/x/ibc/07-tendermint/client_state_test.go index 8a82d06f8752..9814bd6877ea 100644 --- a/x/ibc/07-tendermint/client_state_test.go +++ b/x/ibc/07-tendermint/client_state_test.go @@ -9,12 +9,18 @@ import ( commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +const ( + testConnectionID = "connectionid" + testPortID = "testportid" + testChannelID = "testchannelid" + testSequence = 1 +) + func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { testCases := []struct { name string clientState tendermint.ClientState consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -25,7 +31,6 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, @@ -35,7 +40,6 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, @@ -45,7 +49,6 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, @@ -55,7 +58,6 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, @@ -66,7 +68,6 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -77,7 +78,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { tc := tc err := tc.clientState.VerifyClientConsensusState( - suite.cdc, tc.height, tc.prefix, tc.proof, tc.consensusState, + suite.cdc, height, tc.prefix, tc.proof, tc.consensusState, ) if tc.expPass { @@ -89,17 +90,14 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { } func (suite *TendermintTestSuite) TestVerifyConnectionState() { - testConnectionID := "connectionid" counterparty := connection.NewCounterparty("clientB", testConnectionID, commitment.NewPrefix([]byte("ibc"))) conn := connection.NewConnectionEnd(connectionexported.OPEN, "clientA", counterparty, []string{"1.0.0"}) testCases := []struct { name string clientState tendermint.ClientState - connectionID string connection connection.ConnectionEnd consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -107,61 +105,51 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { - name: "ApplyPrefix failed", - clientState: tendermint.NewClientState(chainID, height), - connectionID: testConnectionID, - connection: conn, + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), + connection: conn, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { - name: "latest client height < height", - clientState: tendermint.NewClientState(chainID, height-1), - connectionID: testConnectionID, - connection: conn, + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), + connection: conn, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { - name: "client is frozen", - clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - connectionID: testConnectionID, - connection: conn, + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, + connection: conn, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { - name: "proof verification failed", - clientState: tendermint.NewClientState(chainID, height), - connectionID: testConnectionID, - connection: conn, + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), + connection: conn, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -172,7 +160,7 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { tc := tc err := tc.clientState.VerifyConnectionState( - suite.cdc, tc.height, tc.prefix, tc.proof, tc.connectionID, tc.connection, tc.consensusState, + suite.cdc, height, tc.prefix, tc.proof, testConnectionID, tc.connection, tc.consensusState, ) if tc.expPass { @@ -184,17 +172,12 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { } func (suite *TendermintTestSuite) TestVerifyChannelState() { - testConnectionID := "connectionid" - testPortID := "testportid" - testChannelID := "testchannelid" counterparty := channel.NewCounterparty(testPortID, testChannelID) ch := channel.NewChannel(channelexported.OPEN, channelexported.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") testCases := []struct { name string clientState tendermint.ClientState - portID string - channelID string channel channel.Channel consensusState tendermint.ConsensusState height uint64 @@ -205,65 +188,51 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { name: "ApplyPrefix failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { name: "latest client height < height", clientState: tendermint.NewClientState(chainID, height-1), - portID: testPortID, - channelID: testChannelID, channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "client is frozen", clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - portID: testPortID, - channelID: testChannelID, channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "proof verification failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, channel: ch, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -274,7 +243,7 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { tc := tc err := tc.clientState.VerifyChannelState( - suite.cdc, tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.channel, tc.consensusState, + suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, tc.channel, tc.consensusState, ) if tc.expPass { @@ -286,16 +255,9 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { } func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { - testPortID := "testportid" - testChannelID := "testchannelid" - testSequence := uint64(1) - testCases := []struct { name string clientState tendermint.ClientState - portID string - channelID string - seq uint64 commitment []byte consensusState tendermint.ConsensusState height uint64 @@ -306,69 +268,51 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { name: "ApplyPrefix failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, commitment: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { name: "latest client height < height", clientState: tendermint.NewClientState(chainID, height-1), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, commitment: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "client is frozen", clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - portID: testPortID, - channelID: testChannelID, - seq: testSequence, commitment: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "proof verification failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, commitment: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -379,7 +323,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { tc := tc err := tc.clientState.VerifyPacketCommitment( - tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.commitment, tc.consensusState, + height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment, tc.consensusState, ) if tc.expPass { @@ -391,16 +335,9 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { } func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { - testPortID := "testportid" - testChannelID := "testchannelid" - testSequence := uint64(1) - testCases := []struct { name string clientState tendermint.ClientState - portID string - channelID string - seq uint64 ack []byte consensusState tendermint.ConsensusState height uint64 @@ -411,69 +348,51 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { name: "ApplyPrefix failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, ack: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { name: "latest client height < height", clientState: tendermint.NewClientState(chainID, height-1), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, ack: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "client is frozen", clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - portID: testPortID, - channelID: testChannelID, - seq: testSequence, ack: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "proof verification failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, ack: []byte{}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -484,7 +403,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { tc := tc err := tc.clientState.VerifyPacketAcknowledgement( - tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.ack, tc.consensusState, + height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack, tc.consensusState, ) if tc.expPass { @@ -496,16 +415,9 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { } func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { - testPortID := "testportid" - testChannelID := "testchannelid" - testSequence := uint64(1) - testCases := []struct { name string clientState tendermint.ClientState - portID string - channelID string - seq uint64 consensusState tendermint.ConsensusState height uint64 prefix commitment.Prefix @@ -515,65 +427,47 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { name: "ApplyPrefix failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { name: "latest client height < height", clientState: tendermint.NewClientState(chainID, height-1), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "client is frozen", clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - portID: testPortID, - channelID: testChannelID, - seq: testSequence, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { name: "proof verification failed", clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - seq: testSequence, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -584,7 +478,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { tc := tc err := tc.clientState.VerifyPacketAcknowledgementAbsence( - tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.seq, tc.consensusState, + height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, ) if tc.expPass { @@ -596,84 +490,59 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { } func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { - testPortID := "testportid" - testChannelID := "testchannelid" - testSequence := uint64(1) - testCases := []struct { - name string - clientState tendermint.ClientState - portID string - channelID string - nextSequenceRecv uint64 - consensusState tendermint.ConsensusState - height uint64 - prefix commitment.Prefix - proof commitment.Proof - expPass bool + name string + clientState tendermint.ClientState + consensusState tendermint.ConsensusState + height uint64 + prefix commitment.Prefix + proof commitment.Proof + expPass bool }{ // { // name: "successful verification", // clientState: tendermint.NewClientState(chainID, height), - // connectionID: testConnectionID, // connection: conn, // consensusState: tendermint.ConsensusState{ // Root: commitment.NewRoot(suite.header.AppHash), // }, - // height: height, // prefix: commitment.NewPrefix([]byte("ibc")), // expPass: true, // }, { - name: "ApplyPrefix failed", - clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - nextSequenceRecv: testSequence, + name: "ApplyPrefix failed", + clientState: tendermint.NewClientState(chainID, height), consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.Prefix{}, expPass: false, }, { - name: "latest client height < height", - clientState: tendermint.NewClientState(chainID, height-1), - portID: testPortID, - channelID: testChannelID, - nextSequenceRecv: testSequence, + name: "latest client height < height", + clientState: tendermint.NewClientState(chainID, height-1), consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { - name: "client is frozen", - clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, - portID: testPortID, - channelID: testChannelID, - nextSequenceRecv: testSequence, + name: "client is frozen", + clientState: tendermint.ClientState{ID: chainID, LatestHeight: height, FrozenHeight: height - 1}, consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), expPass: false, }, { - name: "proof verification failed", - clientState: tendermint.NewClientState(chainID, height), - portID: testPortID, - channelID: testChannelID, - nextSequenceRecv: testSequence, + name: "proof verification failed", + clientState: tendermint.NewClientState(chainID, height), consensusState: tendermint.ConsensusState{ Root: commitment.NewRoot(suite.header.AppHash), ValidatorSetHash: suite.valSet.Hash(), }, - height: height, prefix: commitment.NewPrefix([]byte("ibc")), proof: commitment.Proof{}, expPass: false, @@ -684,7 +553,7 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { tc := tc err := tc.clientState.VerifyNextSequenceRecv( - tc.height, tc.prefix, tc.proof, tc.portID, tc.channelID, tc.nextSequenceRecv, tc.consensusState, + height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, ) if tc.expPass { diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 76ca552288db..d3649e7313f9 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -134,7 +134,6 @@ func ApplyPrefix(prefix PrefixI, path string) (Path, error) { if prefix == nil || prefix.IsEmpty() { return Path{}, errors.New("prefix can't be empty") } - return NewPath([]string{string(prefix.Bytes()), path}), nil } @@ -154,25 +153,23 @@ func (Proof) GetCommitmentType() Type { } // VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value. -func (proof Proof) VerifyMembership(root RootI, path PathI, value []byte) bool { +func (proof Proof) VerifyMembership(root RootI, path PathI, value []byte) error { if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() || len(value) == 0 { - return false + return errors.New("empty params or proof") } runtime := rootmulti.DefaultProofRuntime() - err := runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) - return err == nil + return runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) } // VerifyNonMembership verifies the absence of a merkle proof against the given root and path. -func (proof Proof) VerifyNonMembership(root RootI, path PathI) bool { +func (proof Proof) VerifyNonMembership(root RootI, path PathI) error { if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() { - return false + return errors.New("empty params or proof") } runtime := rootmulti.DefaultProofRuntime() - err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) - return err == nil + return runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) } // IsEmpty returns true if the root is empty diff --git a/x/ibc/23-commitment/merkle_test.go b/x/ibc/23-commitment/merkle_test.go index 7dcb5cbfd0fc..4ce2c697df4d 100644 --- a/x/ibc/23-commitment/merkle_test.go +++ b/x/ibc/23-commitment/merkle_test.go @@ -48,13 +48,18 @@ func (suite *MerkleTestSuite) TestVerifyMembership() { } for i, tc := range cases { + tc := tc suite.Run(tc.name, func() { root := commitment.NewRoot(tc.root) path := commitment.NewPath(tc.pathArr) - ok := proof.VerifyMembership(root, path, tc.value) + err := proof.VerifyMembership(root, path, tc.value) - require.True(suite.T(), ok == tc.shouldPass, "Test case %d failed", i) + if tc.shouldPass { + suite.Require().NoError(err, "test case %d should have passed", i) + } else { + suite.Require().Error(err, "test case %d should have failed", i) + } }) } @@ -95,13 +100,19 @@ func (suite *MerkleTestSuite) TestVerifyNonMembership() { } for i, tc := range cases { + tc := tc + suite.Run(tc.name, func() { root := commitment.NewRoot(tc.root) path := commitment.NewPath(tc.pathArr) - ok := proof.VerifyNonMembership(root, path) + err := proof.VerifyNonMembership(root, path) - require.True(suite.T(), ok == tc.shouldPass, "Test case %d failed", i) + if tc.shouldPass { + suite.Require().NoError(err, "test case %d should have passed", i) + } else { + suite.Require().Error(err, "test case %d should have failed", i) + } }) } diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index f7556a54260b..aaab6f9d656b 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -39,8 +39,8 @@ type PathI interface { // Proofs includes key but value is provided dynamically at the verification time. type ProofI interface { GetCommitmentType() Type - VerifyMembership(RootI, PathI, []byte) bool - VerifyNonMembership(RootI, PathI) bool + VerifyMembership(RootI, PathI, []byte) error + VerifyNonMembership(RootI, PathI) error IsEmpty() bool ValidateBasic() error diff --git a/x/ibc/23-commitment/verify.go b/x/ibc/23-commitment/verify.go index 7c620c2b5a90..8e070595949f 100644 --- a/x/ibc/23-commitment/verify.go +++ b/x/ibc/23-commitment/verify.go @@ -19,21 +19,21 @@ func BatchVerifyMembership( proof ProofI, prefix PrefixI, items map[string][]byte, -) bool { +) error { root := CalculateRoot(ctx) for pathStr, value := range items { path, err := ApplyPrefix(prefix, pathStr) if err != nil { - return false + return err } - if ok := proof.VerifyMembership(root, path, value); !ok { - return false + if err := proof.VerifyMembership(root, path, value); err != nil { + return err } } - return true + return nil } // BatchVerifyNonMembership verifies a proof that many paths have not been set @@ -45,18 +45,18 @@ func BatchVerifyNonMembership( proof ProofI, prefix PrefixI, paths []string, -) bool { +) error { root := CalculateRoot(ctx) for _, pathStr := range paths { path, err := ApplyPrefix(prefix, pathStr) if err != nil { - return false + return err } - if ok := proof.VerifyNonMembership(root, path); !ok { - return false + if err := proof.VerifyNonMembership(root, path); err != nil { + return err } } - return true + return nil } diff --git a/x/ibc/keeper/querier_test.go b/x/ibc/keeper/querier_test.go index b8093bf9dd9d..3c8231c6be06 100644 --- a/x/ibc/keeper/querier_test.go +++ b/x/ibc/keeper/querier_test.go @@ -49,12 +49,6 @@ func (suite *KeeperTestSuite) TestNewQuerier() { true, fmt.Sprintf("unknown IBC %s query endpoint", client.SubModuleName), }, - { - "connection - QuerierConnection", - []string{connection.SubModuleName, connection.QueryConnection}, - false, - "", - }, { "connection - QuerierConnections", []string{connection.SubModuleName, connection.QueryAllConnections}, From 0f1b1d712ee0265503e9d78eb6195b68deb650e1 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 27 Jan 2020 16:33:12 +0100 Subject: [PATCH 34/35] cleanup comments and add a few tests --- x/ibc/03-connection/handler.go | 38 +++--- x/ibc/03-connection/keeper/keeper_test.go | 1 + x/ibc/03-connection/types/connection.go | 2 - x/ibc/03-connection/types/connection_test.go | 78 ++++++++++++- x/ibc/04-channel/exported/exported.go | 3 + x/ibc/04-channel/types/channel.go | 1 - x/ibc/04-channel/types/channel_test.go | 29 ++++- x/ibc/07-tendermint/client_state.go | 117 ++++++++----------- x/ibc/07-tendermint/client_state_test.go | 5 - x/ibc/07-tendermint/update.go | 3 + 10 files changed, 180 insertions(+), 97 deletions(-) diff --git a/x/ibc/03-connection/handler.go b/x/ibc/03-connection/handler.go index 92873e10d054..1c1e90ed6e55 100644 --- a/x/ibc/03-connection/handler.go +++ b/x/ibc/03-connection/handler.go @@ -2,12 +2,10 @@ package connection import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/keeper" - "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" ) // HandleMsgConnectionOpenInit defines the sdk.Handler for MsgConnectionOpenInit -func HandleMsgConnectionOpenInit(ctx sdk.Context, k keeper.Keeper, msg types.MsgConnectionOpenInit) (*sdk.Result, error) { +func HandleMsgConnectionOpenInit(ctx sdk.Context, k Keeper, msg MsgConnectionOpenInit) (*sdk.Result, error) { err := k.ConnOpenInit(ctx, msg.ConnectionID, msg.ClientID, msg.Counterparty) if err != nil { return nil, err @@ -15,13 +13,13 @@ func HandleMsgConnectionOpenInit(ctx sdk.Context, k keeper.Keeper, msg types.Msg ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeConnectionOpenInit, - sdk.NewAttribute(types.AttributeKeyConnectionID, msg.ConnectionID), - sdk.NewAttribute(types.AttributeKeyCounterpartyClientID, msg.Counterparty.ClientID), + EventTypeConnectionOpenInit, + sdk.NewAttribute(AttributeKeyConnectionID, msg.ConnectionID), + sdk.NewAttribute(AttributeKeyCounterpartyClientID, msg.Counterparty.ClientID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) @@ -32,7 +30,7 @@ func HandleMsgConnectionOpenInit(ctx sdk.Context, k keeper.Keeper, msg types.Msg } // HandleMsgConnectionOpenTry defines the sdk.Handler for MsgConnectionOpenTry -func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg types.MsgConnectionOpenTry) (*sdk.Result, error) { +func HandleMsgConnectionOpenTry(ctx sdk.Context, k Keeper, msg MsgConnectionOpenTry) (*sdk.Result, error) { err := k.ConnOpenTry( ctx, msg.ConnectionID, msg.Counterparty, msg.ClientID, msg.CounterpartyVersions, msg.ProofInit, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight) @@ -42,13 +40,13 @@ func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg types.MsgC ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeConnectionOpenTry, - sdk.NewAttribute(types.AttributeKeyConnectionID, msg.ConnectionID), - sdk.NewAttribute(types.AttributeKeyCounterpartyClientID, msg.Counterparty.ClientID), + EventTypeConnectionOpenTry, + sdk.NewAttribute(AttributeKeyConnectionID, msg.ConnectionID), + sdk.NewAttribute(AttributeKeyCounterpartyClientID, msg.Counterparty.ClientID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) @@ -59,7 +57,7 @@ func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg types.MsgC } // HandleMsgConnectionOpenAck defines the sdk.Handler for MsgConnectionOpenAck -func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgConnectionOpenAck) (*sdk.Result, error) { +func HandleMsgConnectionOpenAck(ctx sdk.Context, k Keeper, msg MsgConnectionOpenAck) (*sdk.Result, error) { err := k.ConnOpenAck( ctx, msg.ConnectionID, msg.Version, msg.ProofTry, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, @@ -70,12 +68,12 @@ func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgC ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeConnectionOpenAck, - sdk.NewAttribute(types.AttributeKeyConnectionID, msg.ConnectionID), + EventTypeConnectionOpenAck, + sdk.NewAttribute(AttributeKeyConnectionID, msg.ConnectionID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) @@ -86,7 +84,7 @@ func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgC } // HandleMsgConnectionOpenConfirm defines the sdk.Handler for MsgConnectionOpenConfirm -func HandleMsgConnectionOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg types.MsgConnectionOpenConfirm) (*sdk.Result, error) { +func HandleMsgConnectionOpenConfirm(ctx sdk.Context, k Keeper, msg MsgConnectionOpenConfirm) (*sdk.Result, error) { err := k.ConnOpenConfirm(ctx, msg.ConnectionID, msg.ProofAck, msg.ProofHeight, msg.ConsensusHeight) if err != nil { return nil, err @@ -94,12 +92,12 @@ func HandleMsgConnectionOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg types. ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeConnectionOpenConfirm, - sdk.NewAttribute(types.AttributeKeyConnectionID, msg.ConnectionID), + EventTypeConnectionOpenConfirm, + sdk.NewAttribute(AttributeKeyConnectionID, msg.ConnectionID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) diff --git a/x/ibc/03-connection/keeper/keeper_test.go b/x/ibc/03-connection/keeper/keeper_test.go index 501127348442..b353d3578a4c 100644 --- a/x/ibc/03-connection/keeper/keeper_test.go +++ b/x/ibc/03-connection/keeper/keeper_test.go @@ -70,6 +70,7 @@ func (suite *KeeperTestSuite) SetupTest() { } } +// nolint: unused func (suite *KeeperTestSuite) queryProof(key []byte) (commitment.Proof, int64) { res := suite.app.Query(abci.RequestQuery{ Path: fmt.Sprintf("store/%s/key", storeKey), diff --git a/x/ibc/03-connection/types/connection.go b/x/ibc/03-connection/types/connection.go index f71e3daf07f4..9f0678310dd7 100644 --- a/x/ibc/03-connection/types/connection.go +++ b/x/ibc/03-connection/types/connection.go @@ -61,8 +61,6 @@ func (c ConnectionEnd) GetVersions() []string { // ValidateBasic implements the Connection interface func (c ConnectionEnd) ValidateBasic() error { - // NOTE: invalid state is considered "UNINITIALIZED" - if err := host.DefaultClientIdentifierValidator(c.ClientID); err != nil { return sdkerrors.Wrapf(err, "invalid client ID: %s", c.ClientID) } diff --git a/x/ibc/03-connection/types/connection_test.go b/x/ibc/03-connection/types/connection_test.go index 079ad171875a..895eaf45afdb 100644 --- a/x/ibc/03-connection/types/connection_test.go +++ b/x/ibc/03-connection/types/connection_test.go @@ -1,3 +1,79 @@ package types -// TODO: connection and counterpaty validations +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +func TestConnectionValidateBasic(t *testing.T) { + testCases := []struct { + name string + connection ConnectionEnd + expPass bool + }{ + { + "valid connection", + ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitment.NewPrefix([]byte("prefix"))}, []string{"1.0.0"}}, + true, + }, + { + "invalid client id", + ConnectionEnd{exported.INIT, "ClientIDTwo", Counterparty{"clientidtwo", "connectionidone", commitment.NewPrefix([]byte("prefix"))}, []string{"1.0.0"}}, + false, + }, + { + "empty versions", + ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitment.NewPrefix([]byte("prefix"))}, nil}, + false, + }, + { + "invalid version", + ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitment.NewPrefix([]byte("prefix"))}, []string{""}}, + false, + }, + { + "invalid counterparty", + ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", nil}, []string{"1.0.0"}}, + false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.connection.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestCounterpartyValidateBasic(t *testing.T) { + testCases := []struct { + name string + counterparty Counterparty + expPass bool + }{ + {"valid counterparty", Counterparty{"clientidone", "connectionidone", commitment.NewPrefix([]byte("prefix"))}, true}, + {"invalid client id", Counterparty{"InvalidClient", "channelidone", commitment.NewPrefix([]byte("prefix"))}, false}, + {"invalid connection id", Counterparty{"clientidone", "InvalidConnection", commitment.NewPrefix([]byte("prefix"))}, false}, + {"invalid prefix", Counterparty{"clientidone", "connectionidone", nil}, false}, + } + + for i, tc := range testCases { + tc := tc + + err := tc.counterparty.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/04-channel/exported/exported.go b/x/ibc/04-channel/exported/exported.go index 718d239760f2..ad4f75cacfa8 100644 --- a/x/ibc/04-channel/exported/exported.go +++ b/x/ibc/04-channel/exported/exported.go @@ -5,6 +5,7 @@ import ( "fmt" ) +// ChannelI defines the standard interface for a channel end. type ChannelI interface { GetState() State GetOrdering() Order @@ -14,6 +15,8 @@ type ChannelI interface { ValidateBasic() error } +// CounterpartyI defines the standard interface for a channel end's +// counterparty. type CounterpartyI interface { GetPortID() string GetChannelID() string diff --git a/x/ibc/04-channel/types/channel.go b/x/ibc/04-channel/types/channel.go index c82456aa5063..ebb026bc86ef 100644 --- a/x/ibc/04-channel/types/channel.go +++ b/x/ibc/04-channel/types/channel.go @@ -59,7 +59,6 @@ func (ch Channel) GetVersion() string { // ValidateBasic performs a basic validation of the channel fields func (ch Channel) ValidateBasic() error { - // TODO: update channel and ordering validation if ch.State.String() == "" { return sdkerrors.Wrap(ErrInvalidChannel, ErrInvalidChannelState.Error()) } diff --git a/x/ibc/04-channel/types/channel_test.go b/x/ibc/04-channel/types/channel_test.go index 2cd07f4c7d71..ba1ccb10bf39 100644 --- a/x/ibc/04-channel/types/channel_test.go +++ b/x/ibc/04-channel/types/channel_test.go @@ -1,3 +1,30 @@ package types -// TODO: channel and counterparty validation +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCounterpartyValidateBasic(t *testing.T) { + testCases := []struct { + name string + counterparty Counterparty + expPass bool + }{ + {"valid counterparty", Counterparty{"portidone", "channelidone"}, true}, + {"invalid port id", Counterparty{"InvalidPort", "channelidone"}, false}, + {"invalid channel id", Counterparty{"portidone", "InvalidChannel"}, false}, + } + + for i, tc := range testCases { + tc := tc + + err := tc.counterparty.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/07-tendermint/client_state.go b/x/ibc/07-tendermint/client_state.go index bfd3451205ee..ff10c73a514e 100644 --- a/x/ibc/07-tendermint/client_state.go +++ b/x/ibc/07-tendermint/client_state.go @@ -1,8 +1,6 @@ package tendermint import ( - "fmt" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -56,6 +54,8 @@ func (cs ClientState) IsFrozen() bool { return cs.FrozenHeight != 0 } +// VerifyClientConsensusState verifies a proof of the consensus state of the +// Tendermint client stored on the target machine. func (cs ClientState) VerifyClientConsensusState( cdc *codec.Codec, height uint64, @@ -68,16 +68,8 @@ func (cs ClientState) VerifyClientConsensusState( return err } - if cs.LatestHeight < height { - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } bz, err := cdc.MarshalBinaryBare(consensusState) @@ -92,6 +84,8 @@ func (cs ClientState) VerifyClientConsensusState( return nil } +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored on the target machine. func (cs ClientState) VerifyConnectionState( cdc *codec.Codec, height uint64, @@ -106,17 +100,8 @@ func (cs ClientState) VerifyConnectionState( return err } - if cs.LatestHeight < height { - fmt.Println(cs.LatestHeight, height) - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } bz, err := cdc.MarshalBinaryBare(connectionEnd) @@ -131,6 +116,8 @@ func (cs ClientState) VerifyConnectionState( return nil } +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the target machine. func (cs ClientState) VerifyChannelState( cdc *codec.Codec, height uint64, @@ -146,16 +133,8 @@ func (cs ClientState) VerifyChannelState( return err } - if cs.LatestHeight < height { - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } bz, err := cdc.MarshalBinaryBare(channel) @@ -170,6 +149,8 @@ func (cs ClientState) VerifyChannelState( return nil } +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. func (cs ClientState) VerifyPacketCommitment( height uint64, prefix commitment.PrefixI, @@ -185,16 +166,8 @@ func (cs ClientState) VerifyPacketCommitment( return err } - if cs.LatestHeight < height { - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } if err := proof.VerifyMembership(consensusState.GetRoot(), path, commitmentBytes); err != nil { @@ -204,6 +177,8 @@ func (cs ClientState) VerifyPacketCommitment( return nil } +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. func (cs ClientState) VerifyPacketAcknowledgement( height uint64, prefix commitment.PrefixI, @@ -219,16 +194,8 @@ func (cs ClientState) VerifyPacketAcknowledgement( return err } - if cs.LatestHeight < height { - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } if err := proof.VerifyMembership(consensusState.GetRoot(), path, acknowledgement); err != nil { @@ -238,6 +205,9 @@ func (cs ClientState) VerifyPacketAcknowledgement( return nil } +// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an +// incoming packet acknowledgement at the specified port, specified channel, and +// specified sequence. func (cs ClientState) VerifyPacketAcknowledgementAbsence( height uint64, prefix commitment.PrefixI, @@ -252,16 +222,8 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( return err } - if cs.LatestHeight < height { - return ibctypes.ErrInvalidHeight - } - - if cs.IsFrozen() && cs.FrozenHeight <= height { - return clienttypes.ErrClientFrozen - } - - if proof == nil { - return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err } if err := proof.VerifyNonMembership(consensusState.GetRoot(), path); err != nil { @@ -271,6 +233,8 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( return nil } +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. func (cs ClientState) VerifyNextSequenceRecv( height uint64, prefix commitment.PrefixI, @@ -285,6 +249,27 @@ func (cs ClientState) VerifyNextSequenceRecv( return err } + if err := validateVerificationArgs(cs, height, proof, consensusState); err != nil { + return err + } + + bz := sdk.Uint64ToBigEndian(nextSequenceRecv) + + if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedNextSeqRecvVerification, err.Error()) + } + + return nil +} + +// validateVerificationArgs perfoms the basic checks on the arguments that are +// shared between the verification functions. +func validateVerificationArgs( + cs ClientState, + height uint64, + proof commitment.ProofI, + consensusState clientexported.ConsensusState, +) error { if cs.LatestHeight < height { return ibctypes.ErrInvalidHeight } @@ -297,10 +282,8 @@ func (cs ClientState) VerifyNextSequenceRecv( return sdkerrors.Wrap(commitment.ErrInvalidProof, "proof cannot be empty") } - bz := sdk.Uint64ToBigEndian(nextSequenceRecv) - - if err := proof.VerifyMembership(consensusState.GetRoot(), path, bz); err != nil { - return sdkerrors.Wrap(clienttypes.ErrFailedNextSeqRecvVerification, err.Error()) + if consensusState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") } return nil diff --git a/x/ibc/07-tendermint/client_state_test.go b/x/ibc/07-tendermint/client_state_test.go index 9814bd6877ea..c749647bfa9b 100644 --- a/x/ibc/07-tendermint/client_state_test.go +++ b/x/ibc/07-tendermint/client_state_test.go @@ -180,7 +180,6 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { clientState tendermint.ClientState channel channel.Channel consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -260,7 +259,6 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { clientState tendermint.ClientState commitment []byte consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -340,7 +338,6 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { clientState tendermint.ClientState ack []byte consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -419,7 +416,6 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { name string clientState tendermint.ClientState consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool @@ -494,7 +490,6 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { name string clientState tendermint.ClientState consensusState tendermint.ConsensusState - height uint64 prefix commitment.Prefix proof commitment.Proof expPass bool diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go index bf333d9694b9..262443165637 100644 --- a/x/ibc/07-tendermint/update.go +++ b/x/ibc/07-tendermint/update.go @@ -13,6 +13,9 @@ import ( // - the header is invalid // - header height is lower than the latest client height // - header valset commit verification fails +// +// Tendermint client validity checking uses the bisection algorithm described +// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). func CheckValidityAndUpdateState( clientState clientexported.ClientState, header clientexported.Header, chainID string, ) (clientexported.ClientState, clientexported.ConsensusState, error) { From ddf6a25fd62e0f9d512f64bcf6e92b9a0a33162e Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 27 Jan 2020 16:40:52 +0100 Subject: [PATCH 35/35] typo --- x/ibc/02-client/keeper/client_test.go | 5 +++-- x/ibc/07-tendermint/consensus_state_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index 625ef800cdfc..c331889237aa 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -4,10 +4,11 @@ import ( "bytes" "fmt" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - tmtypes "github.com/tendermint/tendermint/types" ) const ( @@ -28,7 +29,7 @@ func (suite *KeeperTestSuite) TestCreateClient() { expPass bool expPanic bool }{ - {"sucess", params{testClientID, exported.Tendermint}, true, false}, + {"success", params{testClientID, exported.Tendermint}, true, false}, {"client ID exists", params{testClientID, exported.Tendermint}, false, false}, {"client type exists", params{testClientID2, exported.Tendermint}, false, true}, {"invalid client type", params{testClientID3, invalidClientType}, false, false}, diff --git a/x/ibc/07-tendermint/consensus_state_test.go b/x/ibc/07-tendermint/consensus_state_test.go index 08365d13cd3b..9288d433a6ac 100644 --- a/x/ibc/07-tendermint/consensus_state_test.go +++ b/x/ibc/07-tendermint/consensus_state_test.go @@ -16,7 +16,7 @@ func TestConsensusStateValidateBasic(t *testing.T) { consensusState tendermint.ConsensusState expectPass bool }{ - {"sucess", + {"success", tendermint.ConsensusState{ Root: commitment.NewRoot([]byte("app_hash")), ValidatorSetHash: []byte("valset_hash"),