From bda927633b0ef156c6ff4e27de69357b55373fcd Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 1 Dec 2023 19:47:57 +0100 Subject: [PATCH 1/6] some improvements (naming, documentation, errors) --- modules/core/03-connection/keeper/verify.go | 55 +++--- modules/core/04-channel/keeper/handshake.go | 80 ++++----- modules/core/04-channel/keeper/timeout.go | 4 +- modules/core/04-channel/types/multihop.go | 55 +++--- modules/core/33-multihop/verify.go | 188 +++++++++----------- 5 files changed, 191 insertions(+), 191 deletions(-) diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index e6ec92a084a..d37b4c1aac8 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -1,7 +1,6 @@ package keeper import ( - "fmt" "math" errorsmod "cosmossdk.io/errors" @@ -377,23 +376,31 @@ func (k Keeper) VerifyMultihopMembership( connectionHops []string, kvGenerator channeltypes.KeyValueGenFunc, ) error { - - var mProof channeltypes.MsgMultihopProofs - if err := k.cdc.Unmarshal(proof, &mProof); err != nil { + var multihopProof channeltypes.MsgMultihopProofs + if err := k.cdc.Unmarshal(proof, &multihopProof); err != nil { return err } - multihopConnectionEnd, err := mProof.GetMultihopConnectionEnd(k.cdc, connection) + // get the last hop connection on the other side of the multihop channel + // the last hop connection is the connection end on the chain before the counterparty multihop chain + lastHopConnectionEnd, err := multihopProof.GetLastHopConnectionEnd(k.cdc, connection) if err != nil { return err } - key, value, err := kvGenerator(&mProof, multihopConnectionEnd) + // generate the key/value for the expected value on the counterparty end that needs to be proven + key, value, err := kvGenerator(&multihopProof, lastHopConnectionEnd) if err != nil { - return fmt.Errorf("failed to generate key and value: %s", err) + return errorsmod.Wrap(err, "failed to generate key and value to prove") + } + + // the counterparty of the last hop connection end is the connection end on the other end of the multihop channel + prefix := lastHopConnectionEnd.GetCounterparty().GetPrefix() + path, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(key)) + if err != nil { + return err } - prefix := multihopConnectionEnd.GetCounterparty().GetPrefix() clientID := connection.GetClientID() clientStore := k.clientKeeper.ClientStore(ctx, clientID) @@ -402,7 +409,7 @@ func (k Keeper) VerifyMultihopMembership( return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } - // check last client is active + // check client associated with connection on this end of the multihop channel is active if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } @@ -415,7 +422,7 @@ func (k Keeper) VerifyMultihopMembership( ) } - delayPeriod, err := mProof.GetMaximumDelayPeriod(k.cdc, connection) + delayPeriod, err := multihopProof.GetMaximumDelayPeriod(k.cdc, connection) if err != nil { return err } @@ -429,11 +436,10 @@ func (k Keeper) VerifyMultihopMembership( consensusState, found := k.clientKeeper.GetClientConsensusState(ctx, clientID, height) if !found { - return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, - "consensus state not found for client id: %s", clientID) + return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "consensus state not found for client ID (%s) at height (%s)", clientID, height) } - return mh.VerifyMultihopMembership(k.cdc, consensusState, connectionHops, &mProof, prefix, key, value) + return mh.VerifyMultihopMembership(k.cdc, consensusState, connectionHops, &multihopProof, path, value) } // VerifyMultihopNonMembership verifies a multi-hop non-membership proof. @@ -445,23 +451,29 @@ func (k Keeper) VerifyMultihopNonMembership( connectionHops []string, kvGenerator channeltypes.KeyGenFunc, ) error { - var mProof channeltypes.MsgMultihopProofs if err := k.cdc.Unmarshal(proof, &mProof); err != nil { return err } - multihopConnectionEnd, err := mProof.GetMultihopConnectionEnd(k.cdc, connection) + lastHopConnectionEnd, err := mProof.GetLastHopConnectionEnd(k.cdc, connection) if err != nil { return err } - key, err := kvGenerator(&mProof, multihopConnectionEnd) + // generate the key on the counterparty end that needs to be proven + key, err := kvGenerator(&mProof, lastHopConnectionEnd) + if err != nil { + return errorsmod.Wrap(err, "failed to generate key") + } + + // the counterparty of the last hop connection end is the connection end on the other end of the multihop channel + prefix := lastHopConnectionEnd.GetCounterparty().GetPrefix() + path, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(key)) if err != nil { - return fmt.Errorf("failed to generate key: %s", err) + return err } - prefix := multihopConnectionEnd.GetCounterparty().GetPrefix() clientID := connection.GetClientID() clientStore := k.clientKeeper.ClientStore(ctx, clientID) @@ -470,7 +482,7 @@ func (k Keeper) VerifyMultihopNonMembership( return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } - // check last client is active + // check client associated with connection on this end of the multihop channel is active if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } @@ -497,11 +509,10 @@ func (k Keeper) VerifyMultihopNonMembership( consensusState, found := k.clientKeeper.GetClientConsensusState(ctx, clientID, height) if !found { - return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, - "consensus state not found for client id: %s", clientID) + return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "consensus state not found for client ID (%s) at height (%s)", clientID, height) } - return mh.VerifyMultihopNonMembership(k.cdc, consensusState, connectionHops, &mProof, prefix, key) + return mh.VerifyMultihopNonMembership(k.cdc, consensusState, connectionHops, &mProof, path) } // getBlockDelay calculates the block delay period from the time delay of the connection diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index 068a03a1ef3..e4c06243b31 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -152,57 +152,34 @@ func (k Keeper) ChanOpenTry( ) } - // check version support - versionCheckFunc := func(connection *connectiontypes.ConnectionEnd) error { - getVersions := connection.GetVersions() - if len(getVersions) != 1 { - return errorsmod.Wrapf( - connectiontypes.ErrInvalidVersion, - "single version must be negotiated on connection before opening channel, got: %v", - getVersions, - ) - } - - // verify chain A supports the requested ordering. - if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { - return errorsmod.Wrapf( - connectiontypes.ErrInvalidVersion, - "connection version %s does not support channel ordering: %s", - getVersions[0], order.String(), - ) - } - return nil - } // handle multihop case if len(connectionHops) > 1 { - - kvGenerator := func(mProof *types.MsgMultihopProofs, multihopConnectionEnd *connectiontypes.ConnectionEnd) (string, []byte, error) { + kvGenerator := func(mProof *types.MsgMultihopProofs, lastHopConnectionEnd *connectiontypes.ConnectionEnd) (string, []byte, error) { // check version support - if err := versionCheckFunc(multihopConnectionEnd); err != nil { + if err := checkVersion(lastHopConnectionEnd, order); err != nil { return "", nil, err } + // channel end storekey of the counterparty channel on the other end of the multihop channel key := host.ChannelPath(counterparty.PortId, counterparty.ChannelId) - counterpartyHops, err := mProof.GetCounterpartyHops(k.cdc, &connectionEnd) + // connection hops + counterpartyHops, err := mProof.GetCounterpartyConnectionHops(k.cdc, &connectionEnd) if err != nil { return "", nil, err } - // expectedCounterparty is the counterparty of the counterparty's channel end - // (i.e self) + + // expectedCounterparty is the counterparty of the counterparty's channel end (i.e self) expectedCounterparty := types.NewCounterparty(portID, "") - expectedChannel := types.NewChannel( - types.INIT, order, expectedCounterparty, - counterpartyHops, counterpartyVersion, - ) + expectedChannel := types.NewChannel(types.INIT, order, expectedCounterparty, counterpartyHops, counterpartyVersion) // expected value bytes - value, err := expectedChannel.Marshal() + bz, err := k.cdc.Marshal(&expectedChannel) if err != nil { return "", nil, err } - return key, value, nil + return key, bz, nil } if err := k.connectionKeeper.VerifyMultihopMembership( @@ -212,17 +189,15 @@ func (k Keeper) ChanOpenTry( } } else { - // determine counterparty hops counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} - // check versions - if err := versionCheckFunc(&connectionEnd); err != nil { + // check version support + if err := checkVersion(&connectionEnd, order); err != nil { return "", nil, err } - // expectedCounterparty is the counterparty of the counterparty's channel end - // (i.e self) + // expectedCounterparty is the counterparty of the counterparty's channel end (i.e self) expectedCounterparty := types.NewCounterparty(portID, "") expectedChannel := types.NewChannel( types.INIT, order, expectedCounterparty, @@ -319,7 +294,7 @@ func (k Keeper) ChanOpenAck( kvGenerator := func(mProof *types.MsgMultihopProofs, _ *connectiontypes.ConnectionEnd) (string, []byte, error) { key := host.ChannelPath(channel.Counterparty.PortId, counterpartyChannelID) - counterpartyHops, err := mProof.GetCounterpartyHops(k.cdc, &connectionEnd) + counterpartyHops, err := mProof.GetCounterpartyConnectionHops(k.cdc, &connectionEnd) if err != nil { return "", nil, err } @@ -424,7 +399,7 @@ func (k Keeper) ChanOpenConfirm( kvGenerator := func(mProof *types.MsgMultihopProofs, _ *connectiontypes.ConnectionEnd) (string, []byte, error) { key := host.ChannelPath(channel.Counterparty.PortId, channel.Counterparty.ChannelId) - counterpartyHops, err := mProof.GetCounterpartyHops(k.cdc, &connectionEnd) + counterpartyHops, err := mProof.GetCounterpartyConnectionHops(k.cdc, &connectionEnd) if err != nil { return "", nil, err } @@ -579,7 +554,7 @@ func (k Keeper) ChanCloseConfirm( kvGenerator := func(mProof *types.MsgMultihopProofs, _ *connectiontypes.ConnectionEnd) (string, []byte, error) { key := host.ChannelPath(channel.Counterparty.PortId, channel.Counterparty.ChannelId) - counterpartyHops, err := mProof.GetCounterpartyHops(k.cdc, &connectionEnd) + counterpartyHops, err := mProof.GetCounterpartyConnectionHops(k.cdc, &connectionEnd) if err != nil { return "", nil, err } @@ -753,3 +728,26 @@ func (k Keeper) ChanCloseFrozen( emitChannelCloseFrozenEvent(ctx, portID, channelID, channel) return nil } + +// chekcVersion checks that the connection's features include support for order. +func checkVersion(connection *connectiontypes.ConnectionEnd, order types.Order) error { + getVersions := connection.GetVersions() + if len(getVersions) != 1 { + return errorsmod.Wrapf( + connectiontypes.ErrInvalidVersion, + "single version must be negotiated on connection before opening channel, got: %v", + getVersions, + ) + } + + // verify chain supports the requested ordering. + if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { + return errorsmod.Wrapf( + connectiontypes.ErrInvalidVersion, + "connection version %s does not support channel ordering: %s", + getVersions[0], order.String(), + ) + } + + return nil +} diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index 8b6f63c6f70..516b4a4f9cd 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -70,7 +70,7 @@ func (k Keeper) TimeoutPacket( return err } - consensusState, err := mProof.GetMultihopCounterpartyConsensus(k.cdc) + consensusState, err := mProof.GeLastHopConsensusState(k.cdc) if err != nil { return err } @@ -289,7 +289,7 @@ func (k Keeper) TimeoutOnClose( // verify multihop proof if len(channel.ConnectionHops) > 1 { kvGenerator := func(mProof *types.MsgMultihopProofs, _ *connectiontypes.ConnectionEnd) (string, []byte, error) { - counterpartyHops, err := mProof.GetCounterpartyHops(k.cdc, &connectionEnd) + counterpartyHops, err := mProof.GetCounterpartyConnectionHops(k.cdc, &connectionEnd) if err != nil { return "", nil, err } diff --git a/modules/core/04-channel/types/multihop.go b/modules/core/04-channel/types/multihop.go index 75242a7ab2c..5ec41297405 100644 --- a/modules/core/04-channel/types/multihop.go +++ b/modules/core/04-channel/types/multihop.go @@ -1,10 +1,10 @@ package types import ( - "fmt" - + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" ) @@ -12,10 +12,13 @@ type ConnectionEnd = connectiontypes.ConnectionEnd // TODO: Create multihop proof struct to serialize multihop proofs into. -// GetMultihopConnectionEnd returns the final connectionEnd from the counterparty perspective -func (m *MsgMultihopProofs) GetMultihopConnectionEnd(cdc codec.BinaryCodec, connection exported.ConnectionI) (*ConnectionEnd, error) { +// GetLastHopConnectionEnd returns the last hop connectionEnd from the perpespective of the executing chain. +// The last hop connection is the connection end on the chain before the counterparty multihop chain (i.e. the chain on the other end of the multihop channel). +func (m *MsgMultihopProofs) GetLastHopConnectionEnd(cdc codec.BinaryCodec, connection exported.ConnectionI) (*ConnectionEnd, error) { var connectionEnd ConnectionEnd if len(m.ConnectionProofs) > 0 { + // connection proofs are ordered from executing chain to counterparty, + // so the last hop connection end is the value of last connection proof if err := cdc.Unmarshal(m.ConnectionProofs[len(m.ConnectionProofs)-1].Value, &connectionEnd); err != nil { return nil, err } @@ -23,23 +26,28 @@ func (m *MsgMultihopProofs) GetMultihopConnectionEnd(cdc codec.BinaryCodec, conn var ok bool connectionEnd, ok = connection.(connectiontypes.ConnectionEnd) if !ok { - return nil, fmt.Errorf("failed to cast connection interface. expected type 'connectiontypes.ConnectionEnd'") + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", connectiontypes.ConnectionEnd{}, connection) } } return &connectionEnd, nil } -// GetMultihopCounterpartyConsensus returns the final consensusState from the counterparty perspective (e.g. the source chain state). -func (m *MsgMultihopProofs) GetMultihopCounterpartyConsensus(cdc codec.BinaryCodec) (consensusState exported.ConsensusState, err error) { - err = cdc.UnmarshalInterface(m.ConsensusProofs[len(m.ConsensusProofs)-1].Value, &consensusState) - return +// GeLastHopConsensusState returns the last hop consensusState from the perspective of the executing chain. +// The last hop connection is the connection end on the chain before the counterparty multihop chain (i.e. the chain on the other end of the multihop channel). +func (m *MsgMultihopProofs) GeLastHopConsensusState(cdc codec.BinaryCodec) (exported.ConsensusState, error) { + var consensusState exported.ConsensusState + if len(m.ConnectionProofs) > 0 { + if err := cdc.UnmarshalInterface(m.ConsensusProofs[len(m.ConsensusProofs)-1].Value, &consensusState); err != nil { + return nil, err + } + } else { + panic("") // TODO: is this reachable? + } + return consensusState, nil } // GetMaximumDelayPeriod returns the maximum delay period over all connections in the multi-hop channel path. -func (m *MsgMultihopProofs) GetMaximumDelayPeriod( - cdc codec.BinaryCodec, - lastConnection exported.ConnectionI, -) (uint64, error) { +func (m *MsgMultihopProofs) GetMaximumDelayPeriod(cdc codec.BinaryCodec, lastConnection exported.ConnectionI) (uint64, error) { delayPeriod := lastConnection.GetDelayPeriod() for _, connData := range m.ConnectionProofs { var connectionEnd ConnectionEnd @@ -53,22 +61,23 @@ func (m *MsgMultihopProofs) GetMaximumDelayPeriod( return delayPeriod, nil } -// GetCounterpartyHops returns the counter party connectionHops. Connection proofs are ordered from receiving chain to sending chain -// so in order to get the counterparty connection hops we need to reverse iterate through the proofs and then add the final counterparty -// connection id for the receiving chain. -func (m *MsgMultihopProofs) GetCounterpartyHops( - cdc codec.BinaryCodec, - lastConnection *ConnectionEnd, -) (counterpartyHops []string, err error) { +// GetCounterpartyConnectionHops returns the counterparty connectionHops. +// connection is the connection end on one of the ends of the multihop channel, and this function returns +// the connection hops for the connection on the other end of the multihop channel. +// Since connection proofs are ordered from the perspective of the connection parameter, in order to get the +// counterparty connection hops we need to reverse iterate through the proofs and then add the final +// counterparty connection ID for connection. +func (m *MsgMultihopProofs) GetCounterpartyConnectionHops(cdc codec.BinaryCodec, connection *ConnectionEnd) (counterpartyHops []string, err error) { var connectionEnd ConnectionEnd - for _, connData := range m.ConnectionProofs { - if err = cdc.Unmarshal(connData.Value, &connectionEnd); err != nil { + for _, connectionProof := range m.ConnectionProofs { + if err = cdc.Unmarshal(connectionProof.Value, &connectionEnd); err != nil { return nil, err } counterpartyHops = append([]string{connectionEnd.GetCounterparty().GetConnectionID()}, counterpartyHops...) } - counterpartyHops = append(counterpartyHops, lastConnection.GetCounterparty().GetConnectionID()) + // the last hop is the counterparty connection ID of the connection on the other end of the multihop channel + counterpartyHops = append(counterpartyHops, connection.GetCounterparty().GetConnectionID()) return counterpartyHops, nil } diff --git a/modules/core/33-multihop/verify.go b/modules/core/33-multihop/verify.go index 5d2fd6d356a..ce0135b32ed 100644 --- a/modules/core/33-multihop/verify.go +++ b/modules/core/33-multihop/verify.go @@ -12,11 +12,12 @@ import ( connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" tmclient "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ) -// helper function to parse a client or connection id from a key path +// parseID is a helper function that parses a client or connection id from a key path func parseID(keyPath []string) (string, error) { if len(keyPath) < 2 { return "", fmt.Errorf( @@ -31,12 +32,12 @@ func parseID(keyPath []string) (string, error) { return parts[1], nil } -// helper function to parse the client id from a consensus state key path +// parseClientIDFromKey parses the client id from a consensus state key path and returns it. func parseClientIDFromKey(keyPath []string) (string, error) { return parseID(keyPath) } -// Parse the connectionID from the connection proof key and return it. +// parseConnectionIDFromKey parses the connectionID from the connection proof key and returns it. func parseConnectionIDFromKey(keyPath []string) (string, error) { return parseID(keyPath) } @@ -72,134 +73,150 @@ func VerifyMultihopMembership( consensusState exported.ConsensusState, connectionHops []string, proofs *channeltypes.MsgMultihopProofs, - prefix exported.Prefix, - key string, + path exported.Path, value []byte, ) error { - if len(proofs.ConsensusProofs) != len(proofs.ConnectionProofs) { - return fmt.Errorf("the number of connection (%d) and consensus (%d) proofs must be equal", - len(proofs.ConnectionProofs), len(proofs.ConsensusProofs)) + return fmt.Errorf("the number of connection (%d) and consensus (%d) proofs must be equal", len(proofs.ConnectionProofs), len(proofs.ConsensusProofs)) } // verify connection states and ordering - if err := verifyConnectionStates(cdc, proofs.ConnectionProofs, connectionHops); err != nil { + if err := validateIntermediateStates(cdc, proofs.ConsensusProofs, proofs.ConnectionProofs, connectionHops[1:]); err != nil { return err } // verify intermediate consensus and connection states from destination --> source - if err := verifyIntermediateStateProofs(cdc, consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs); err != nil { - return fmt.Errorf("failed to verify consensus state proof: %w", err) + if err := verifyIntermediateStates(cdc, consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs); err != nil { + return err } - // verify the keyproof on source chain's consensus state. - return verifyKeyValueMembership(cdc, consensusState, proofs, prefix, key, value) + // verify the key proof on counterparty chain's consensus state. + return verifyKeyValueMembership(cdc, consensusState, proofs, path, value) } -// VerifyMultihopNonMembership verifies a multihop proof. A nil value indicates a non-inclusion proof (proof of absence). +// VerifyMultihopNonMembership verifies a multihop proof. func VerifyMultihopNonMembership( cdc codec.BinaryCodec, consensusState exported.ConsensusState, connectionHops []string, proofs *channeltypes.MsgMultihopProofs, - prefix exported.Prefix, - key string, + path exported.Path, ) error { - if len(proofs.ConsensusProofs) != len(proofs.ConnectionProofs) { - return fmt.Errorf("the number of connection (%d) and consensus (%d) proofs must be equal", - len(proofs.ConnectionProofs), len(proofs.ConsensusProofs)) + return fmt.Errorf("the number of connection (%d) and consensus (%d) proofs must be equal", len(proofs.ConnectionProofs), len(proofs.ConsensusProofs)) } - // verify connection states and ordering - if err := verifyConnectionStates(cdc, proofs.ConnectionProofs, connectionHops); err != nil { + // validate connection states and ordering + if err := validateIntermediateStates(cdc, proofs.ConsensusProofs, proofs.ConnectionProofs, connectionHops[1:]); err != nil { return err } // verify intermediate consensus and connection states from destination --> source - if err := verifyIntermediateStateProofs(cdc, consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs); err != nil { - return fmt.Errorf("failed to verify consensus state proof: %w", err) + if err := verifyIntermediateStates(cdc, consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs); err != nil { + return err } // verify the keyproof on source chain's consensus state. - return verifyKeyNonMembership(cdc, consensusState, proofs, prefix, key) + return verifyKeyNonMembership(cdc, consensusState, proofs, path) } -// verifyConnectionStates verifies that the provided connections match the connectionHops field of the channel and are in OPEN state -func verifyConnectionStates( +// validateIntermediateStates validates the following: +// 1. That the connections in the values of connectionProofs match the connectionHops of the channel and are in OPEN state. +// 2. That the client IDs in the keys of consensusProofs matches the client ID in the connection end on the same hop. +// - consensusProofs is a list of proofs, with the corresponding key/value pair, for the consensus statesfrom the executing chain +// (but excluding it) to the counterparty. +// - connectionProofs is a list of proofs, with the corresponding key/value pair, for the connection ends that represent the connection hops +// from the executing chain (but excluding it) to the counterparty. +// - connectionHops contains the connection IDs for the hops from the executing chain to the counterparty, but it does not include the +// connection ID for the first hop on the executing chain. +func validateIntermediateStates( cdc codec.BinaryCodec, - connectionProofData []*channeltypes.MultihopProof, + consensusProofs []*channeltypes.MultihopProof, + connectionProofs []*channeltypes.MultihopProof, connectionHops []string, ) error { - if len(connectionProofData) != len(connectionHops)-1 { + // check that the are as many proofs as connection hops + if len(connectionProofs) != len(connectionHops) { return errorsmod.Wrapf(connectiontypes.ErrInvalidLengthConnection, "connectionHops length (%d) must match the connectionProofData length (%d)", - len(connectionHops)-1, len(connectionProofData)) + len(connectionHops), len(connectionProofs)) } - // check all connections are in OPEN state and that the connection IDs match and are in the right order - for i, connData := range connectionProofData { + // check all connections are in OPEN state and that the connection IDs match and are in the right order. + for i := 0; i < len(consensusProofs); i++ { + consensusProof := consensusProofs[i] + connectionProof := connectionProofs[i] + var connectionEnd connectiontypes.ConnectionEnd - if err := cdc.Unmarshal(connData.Value, &connectionEnd); err != nil { - return err + if err := cdc.Unmarshal(connectionProof.Value, &connectionEnd); err != nil { + return errorsmod.Wrapf(err, "failed to unmarshal connection end for hop %d", i) } - // Verify the rest of the connectionHops (first hop already verified) - // 1. check the connectionHop values match the proofs and are in the same order. - connectionID, err := parseConnectionIDFromKey(connData.PrefixedKey.KeyPath) + // parse connection ID from the connectionEnd key path + connectionID, err := parseConnectionIDFromKey(connectionProof.PrefixedKey.KeyPath) if err != nil { return err } - // parts := strings.Split(connData.PrefixedKey.GetKeyPath()[len(connData.PrefixedKey.KeyPath)-1], "/") - if connectionID != connectionHops[i+1] { + // 1. check the connectionHop value matches the connection ID from the proof and are in the same order. + if connectionID != connectionHops[i] { return errorsmod.Wrapf( connectiontypes.ErrConnectionPath, - "connectionHops (%s) does not match connection proof hop (%s) for hop %d", - connectionHops[i+1], connectionID, i) + "connection hop (%s) does not match connection proof hop (%s) for hop %d", + connectionHops[i], connectionID, i) } - // 2. check that the connectionEnd's are in the OPEN state. + // 2. check that the connectionEnd is in the OPEN state. if connectionEnd.GetState() != int32(connectiontypes.OPEN) { return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, - "connection state is not OPEN for connectionID=%s (got %s)", - connectionEnd.Counterparty.ConnectionId, + "connection state is not OPEN (got %s) for connection ID (%s) for hop %d", connectiontypes.State(connectionEnd.GetState()).String(), + connectionEnd.Counterparty.ConnectionId, i) + } + + // parse the client ID from the the consensusState key path + clientID, err := parseClientIDFromKey(consensusProof.PrefixedKey.KeyPath) + if err != nil { + return err + } + + // check that the client ID matches the client ID in the connectionEnd on the same hop. + if connectionEnd.ClientId != clientID { + return fmt.Errorf("consensus state client id (%s) does not match expected client id (%s)", + connectionEnd.ClientId, + clientID, ) } } return nil } -// verifyIntermediateStateProofs verifies the intermediate consensus, connection, client states in the multi-hop proof. -func verifyIntermediateStateProofs( +// verifyIntermediateStates verifies the intermediate consensus and connection states in the multi-hop proof. +// - consensusState +// - consensusProofs is a list of proofs, with the corresponding key/value pair, for the consensus statesfrom the executing chain +// (but excluding it) to the counterparty. +// - connectionProofs is a list of proofs, with the corresponding key/value pair, for the connection ends that represent the connection hops +// from the executing chain (but excluding it) to the counterparty. +func verifyIntermediateStates( cdc codec.BinaryCodec, consensusState exported.ConsensusState, consensusProofs []*channeltypes.MultihopProof, connectionProofs []*channeltypes.MultihopProof, ) error { - - var connectionEnd connectiontypes.ConnectionEnd for i := 0; i < len(consensusProofs); i++ { consensusProof := consensusProofs[i] connectionProof := connectionProofs[i] cs, ok := consensusState.(*tmclient.ConsensusState) if !ok { - return fmt.Errorf("expected consensus state to be tendermint consensus state, got: %T", consensusState) - } - - // the client id in the consensusState key path should match the clientID for the next connectionEnd - expectedClientID, err := parseClientIDFromKey(consensusProof.PrefixedKey.KeyPath) - if err != nil { - return err + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got: %T", tmclient.ConsensusState{}, consensusState) } // prove consensus state var proof commitmenttypes.MerkleProof if err := cdc.Unmarshal(consensusProof.Proof, &proof); err != nil { - return fmt.Errorf("failed to unmarshal consensus state proof: %w", err) + return errorsmod.Wrapf(err, "failed to unmarshal consensus state proof") } if err := proof.VerifyMembership( commitmenttypes.GetSDKSpecs(), @@ -207,13 +224,13 @@ func verifyIntermediateStateProofs( *consensusProof.PrefixedKey, consensusProof.Value, ); err != nil { - return fmt.Errorf("failed to verify consensus proof: %w", err) + return errorsmod.Wrapf(err, "failed to verify consensus state proof for hop %d", i) } // prove connection state proof.Reset() if err := cdc.Unmarshal(connectionProof.Proof, &proof); err != nil { - return fmt.Errorf("failed to unmarshal connection state proof: %w", err) + return errorsmod.Wrapf(err, "failed to unmarshal connection state proof for hop %d", i) } if err := proof.VerifyMembership( commitmenttypes.GetSDKSpecs(), @@ -221,25 +238,12 @@ func verifyIntermediateStateProofs( *connectionProof.PrefixedKey, connectionProof.Value, ); err != nil { - return fmt.Errorf("failed to verify connection proof: %w", err) - } - - // determine the next expected client id - if err := cdc.Unmarshal(connectionProof.Value, &connectionEnd); err != nil { - return fmt.Errorf("failed to unmarshal connection end: %w", err) - } - - // verify consensus state client id matches next connectionEnd clientID - if connectionEnd.ClientId != expectedClientID { - return fmt.Errorf("consensus state client id (%s) does not match expected client id (%s)", - connectionEnd.ClientId, - expectedClientID, - ) + return errorsmod.Wrapf(err, "failed to verify connection proof for hop %d", i) } // set the next consensus state if err := cdc.UnmarshalInterface(consensusProof.Value, &consensusState); err != nil { - return fmt.Errorf("failed to unpack consesnsus state: %w", err) + return errorsmod.Wrapf(err, "failed to unmarshal consensus state for hop %d", i) } } return nil @@ -250,43 +254,31 @@ func verifyKeyValueMembership( cdc codec.BinaryCodec, consensusStateI exported.ConsensusState, proofs *channeltypes.MsgMultihopProofs, - prefix exported.Prefix, - key string, + path exported.Path, value []byte, ) error { - // no keyproof provided, nothing to verify if proofs.KeyProof == nil { return nil } - prefixedKey, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(key)) - if err != nil { - return err - } - - var keyProof commitmenttypes.MerkleProof - if err := cdc.Unmarshal(proofs.KeyProof.Proof, &keyProof); err != nil { - return fmt.Errorf("failed to unmarshal key proof: %w", err) + var merkleProof commitmenttypes.MerkleProof + if err := cdc.Unmarshal(proofs.KeyProof.Proof, &merkleProof); err != nil { + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof") } if len(proofs.ConsensusProofs) > 0 { index := uint32(len(proofs.ConsensusProofs)) - 1 if err := cdc.UnmarshalInterface(proofs.ConsensusProofs[index].Value, &consensusStateI); err != nil { - return fmt.Errorf("failed to unpack consensus state: %w", err) + return errorsmod.Wrap(err, "failed to unmarshall consensus state") } } consensusState, ok := consensusStateI.(*tmclient.ConsensusState) if !ok { - return fmt.Errorf("expected consensus state to be tendermint consensus state, got: %T", consensusStateI) + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got: %T", tmclient.ConsensusState{}, consensusState) } - return keyProof.VerifyMembership( - commitmenttypes.GetSDKSpecs(), - consensusState.GetRoot(), - prefixedKey, - value, - ) + return merkleProof.VerifyMembership(commitmenttypes.GetSDKSpecs(), consensusState.GetRoot(), path, value) } // verifyKeyNonMembership verifies a multihop non-membership proof including all intermediate state proofs. @@ -294,19 +286,13 @@ func verifyKeyNonMembership( cdc codec.BinaryCodec, consensusStateI exported.ConsensusState, proofs *channeltypes.MsgMultihopProofs, - prefix exported.Prefix, - key string, + path exported.Path, ) error { // no keyproof provided, nothing to verify if proofs.KeyProof == nil { return nil } - prefixedKey, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(key)) - if err != nil { - return err - } - var keyProof commitmenttypes.MerkleProof if err := cdc.Unmarshal(proofs.KeyProof.Proof, &keyProof); err != nil { return fmt.Errorf("failed to unmarshal key proof: %w", err) @@ -323,9 +309,5 @@ func verifyKeyNonMembership( return fmt.Errorf("expected consensus state to be tendermint consensus state, got: %T", consensusStateI) } - return keyProof.VerifyNonMembership( - commitmenttypes.GetSDKSpecs(), - consensusState.GetRoot(), - prefixedKey, - ) + return keyProof.VerifyNonMembership(commitmenttypes.GetSDKSpecs(), consensusState.GetRoot(), path) } From 75c595e7e2575138b76cc137824fe5e817025d22 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 5 Dec 2023 15:26:36 +0100 Subject: [PATCH 2/6] fix linter issues --- e2e/tests/wasm/grandpa_test.go | 4 +- modules/core/02-client/keeper/grpc_query.go | 6 +-- .../02-client/migrations/v7/solomachine.go | 2 +- modules/core/04-channel/keeper/handshake.go | 48 ++++++++--------- .../keeper/multihop_handshake_test.go | 3 -- .../04-channel/keeper/multihop_packet_test.go | 2 +- .../core/04-channel/keeper/multihop_test.go | 28 +++++----- .../keeper/multihop_timeout_test.go | 2 +- .../core/04-channel/types/expected_keepers.go | 6 ++- modules/core/04-channel/types/multihop.go | 2 + modules/core/33-multihop/query.go | 52 +++++++++++-------- modules/core/33-multihop/verify.go | 4 +- .../06-solomachine/consensus_state.go | 2 +- modules/light-clients/07-tendermint/store.go | 6 +-- testing/chain.go | 2 - testing/coordinator_multihop.go | 12 ++--- testing/endpoint_multihop.go | 47 ++++++++++------- testing/path_multihop.go | 3 +- 18 files changed, 126 insertions(+), 105 deletions(-) diff --git a/e2e/tests/wasm/grandpa_test.go b/e2e/tests/wasm/grandpa_test.go index 6769c1a107f..91a92a9bfc0 100644 --- a/e2e/tests/wasm/grandpa_test.go +++ b/e2e/tests/wasm/grandpa_test.go @@ -394,7 +394,7 @@ func (s *GrandpaTestSuite) TestRecoverClient_Succeeds_GrandpaContract() { // set the trusting period to a value which will still be valid upon client creation, but invalid before the first update // the contract uses 1600s as the unbonding period with the trusting period evaluating to (unbonding period / 3) - var modifiedTrustingPeriod = (1600 * time.Second) / 3 + modifiedTrustingPeriod := (1600 * time.Second) / 3 chainA, chainB := s.GetGrandpaTestChains() @@ -449,7 +449,7 @@ func (s *GrandpaTestSuite) TestRecoverClient_Succeeds_GrandpaContract() { // wait the bad trusting period time.Sleep(modifiedTrustingPeriod) - // create client pair with substitue + // create client pair with substitute substituteClientID := clienttypes.FormatClientIdentifier(ibcexported.Wasm, 1) s.SetupClients(ctx, r, ibc.DefaultClientOpts()) diff --git a/modules/core/02-client/keeper/grpc_query.go b/modules/core/02-client/keeper/grpc_query.go index 8bee33723eb..c58b3cc0793 100644 --- a/modules/core/02-client/keeper/grpc_query.go +++ b/modules/core/02-client/keeper/grpc_query.go @@ -147,7 +147,7 @@ func (k Keeper) ConsensusState(c context.Context, req *types.QueryConsensusState } // NextConsensusState implements the Query/GetNextConsensusState gRPC method -func (q Keeper) NextConsensusStateHeight(c context.Context, req *types.QueryNextConsensusStateHeightRequest) (*types.QueryNextConsensusStateHeightResponse, error) { +func (k Keeper) NextConsensusStateHeight(c context.Context, req *types.QueryNextConsensusStateHeightRequest) (*types.QueryNextConsensusStateHeightResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -163,8 +163,8 @@ func (q Keeper) NextConsensusStateHeight(c context.Context, req *types.QueryNext return nil, status.Error(codes.InvalidArgument, "consensus state height cannot be 0") } - clientStore := q.ClientStore(ctx, req.ClientId) - consensusStateHeight, _ := tm.GetNextConsensusStateHeight(clientStore, q.cdc, height) + clientStore := k.ClientStore(ctx, req.ClientId) + consensusStateHeight, _ := tm.GetNextConsensusStateHeight(clientStore, k.cdc, height) var resp types.QueryNextConsensusStateHeightResponse if consensusStateHeight != nil { diff --git a/modules/core/02-client/migrations/v7/solomachine.go b/modules/core/02-client/migrations/v7/solomachine.go index c250efe6357..e2ef9fe2707 100644 --- a/modules/core/02-client/migrations/v7/solomachine.go +++ b/modules/core/02-client/migrations/v7/solomachine.go @@ -262,6 +262,6 @@ func (ConsensusState) ValidateBasic() error { } // GetRoot panics! -func (cs ConsensusState) GetRoot() exported.Root { +func (ConsensusState) GetRoot() exported.Root { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index e4c06243b31..4e702c5061b 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -314,20 +314,20 @@ func (k Keeper) ChanOpenAck( ctx, connectionEnd, proofHeight, proofTry, channel.ConnectionHops, kvGenerator) - } else { - counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} - expectedCounterparty := types.NewCounterparty(portID, channelID) - expectedChannel := types.NewChannel( - types.TRYOPEN, channel.Ordering, expectedCounterparty, - counterpartyHops, counterpartyVersion, - ) - - return k.connectionKeeper.VerifyChannelState( - ctx, connectionEnd, proofHeight, proofTry, - channel.Counterparty.PortId, counterpartyChannelID, - expectedChannel, - ) } + + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} + expectedCounterparty := types.NewCounterparty(portID, channelID) + expectedChannel := types.NewChannel( + types.TRYOPEN, channel.Ordering, expectedCounterparty, + counterpartyHops, counterpartyVersion, + ) + + return k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofTry, + channel.Counterparty.PortId, counterpartyChannelID, + expectedChannel, + ) } // WriteOpenAckChannel writes an updated channel state for the successful OpenAck handshake step. @@ -419,18 +419,18 @@ func (k Keeper) ChanOpenConfirm( ctx, connectionEnd, proofHeight, proofAck, channel.ConnectionHops, kvGenerator) - } else { - counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} - counterparty := types.NewCounterparty(portID, channelID) - expectedChannel := types.NewChannel( - types.OPEN, channel.Ordering, counterparty, - counterpartyHops, channel.Version) - - return k.connectionKeeper.VerifyChannelState( - ctx, connectionEnd, proofHeight, proofAck, - channel.Counterparty.PortId, channel.Counterparty.ChannelId, - expectedChannel) } + + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} + counterparty := types.NewCounterparty(portID, channelID) + expectedChannel := types.NewChannel( + types.OPEN, channel.Ordering, counterparty, + counterpartyHops, channel.Version) + + return k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofAck, + channel.Counterparty.PortId, channel.Counterparty.ChannelId, + expectedChannel) } // WriteOpenConfirmChannel writes an updated channel state for the successful OpenConfirm handshake step. diff --git a/modules/core/04-channel/keeper/multihop_handshake_test.go b/modules/core/04-channel/keeper/multihop_handshake_test.go index a06b1b5f848..926c320d2c1 100644 --- a/modules/core/04-channel/keeper/multihop_handshake_test.go +++ b/modules/core/04-channel/keeper/multihop_handshake_test.go @@ -4,7 +4,6 @@ import ( "fmt" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" @@ -16,7 +15,6 @@ import ( // TestChannOpenInit tests the OpenInit handshake call for multihop channels. func (suite *MultihopTestSuite) TestChanOpenInit() { - var ( features []string portCap *capabilitytypes.Capability @@ -721,7 +719,6 @@ func (suite *MultihopTestSuite) TestChanCloseFrozenMultihop() { testCases := []testCase{ {"success", func() { - var clientState exported.ClientState suite.SetupChannels() diff --git a/modules/core/04-channel/keeper/multihop_packet_test.go b/modules/core/04-channel/keeper/multihop_packet_test.go index 9235f23c874..790fc912223 100644 --- a/modules/core/04-channel/keeper/multihop_packet_test.go +++ b/modules/core/04-channel/keeper/multihop_packet_test.go @@ -5,8 +5,8 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" diff --git a/modules/core/04-channel/keeper/multihop_test.go b/modules/core/04-channel/keeper/multihop_test.go index 12e54489cff..3b743681df4 100644 --- a/modules/core/04-channel/keeper/multihop_test.go +++ b/modules/core/04-channel/keeper/multihop_test.go @@ -3,18 +3,19 @@ package keeper_test import ( "testing" + testifysuite "github.com/stretchr/testify/suite" + ibctesting "github.com/cosmos/ibc-go/v8/testing" - "github.com/stretchr/testify/suite" ) // TestMultihopMultihopTestSuite runs all multihop related tests. func TestMultihopTestSuite(t *testing.T) { - suite.Run(t, new(MultihopTestSuite)) + testifysuite.Run(t, new(MultihopTestSuite)) } // MultihopTestSuite is a testing suite to test keeper functions. type MultihopTestSuite struct { - suite.Suite + testifysuite.Suite // multihop channel path chanPath *ibctesting.PathM coord *ibctesting.CoordinatorM @@ -29,26 +30,27 @@ func (suite *MultihopTestSuite) SetupTest(numChains int) { } // SetupConnections creates connections between each pair of chains in the multihop path. -func (s *MultihopTestSuite) SetupConnections() { - s.coord.SetupConnections(s.chanPath) +func (suite *MultihopTestSuite) SetupConnections() { + suite.coord.SetupConnections(suite.chanPath) } // SetupConnections creates connections between each pair of chains in the multihop path. -func (s *MultihopTestSuite) SetupAllButTheSpecifiedConnection(index int) { - s.coord.SetupAllButTheSpecifiedConnection(s.chanPath, index) +func (suite *MultihopTestSuite) SetupAllButTheSpecifiedConnection(index int) { + err := suite.coord.SetupAllButTheSpecifiedConnection(suite.chanPath, index) + suite.Require().NoError(err) } // SetupChannels create a multihop channel after creating all its preprequisites in order, ie. clients, connections. -func (s *MultihopTestSuite) SetupChannels() { - s.coord.SetupChannels(s.chanPath) +func (suite *MultihopTestSuite) SetupChannels() { + suite.coord.SetupChannels(suite.chanPath) } // A returns the one endpoint of the multihop channel. -func (s *MultihopTestSuite) A() *ibctesting.EndpointM { - return s.chanPath.EndpointA +func (suite *MultihopTestSuite) A() *ibctesting.EndpointM { + return suite.chanPath.EndpointA } // Z returns the other endpoint of the multihop channel. -func (s *MultihopTestSuite) Z() *ibctesting.EndpointM { - return s.chanPath.EndpointZ +func (suite *MultihopTestSuite) Z() *ibctesting.EndpointM { + return suite.chanPath.EndpointZ } diff --git a/modules/core/04-channel/keeper/multihop_timeout_test.go b/modules/core/04-channel/keeper/multihop_timeout_test.go index f05aa5ed92d..63d960a0ef3 100644 --- a/modules/core/04-channel/keeper/multihop_timeout_test.go +++ b/modules/core/04-channel/keeper/multihop_timeout_test.go @@ -5,8 +5,8 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index 27ad4b9ae1b..44f212d5d41 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -10,8 +10,10 @@ import ( "github.com/cosmos/ibc-go/v8/modules/core/exported" ) -type KeyValueGenFunc func(*MsgMultihopProofs, *connectiontypes.ConnectionEnd) (string, []byte, error) -type KeyGenFunc func(*MsgMultihopProofs, *connectiontypes.ConnectionEnd) (string, error) +type ( + KeyValueGenFunc func(*MsgMultihopProofs, *connectiontypes.ConnectionEnd) (string, []byte, error) + KeyGenFunc func(*MsgMultihopProofs, *connectiontypes.ConnectionEnd) (string, error) +) // ClientKeeper expected account IBC client keeper type ClientKeeper interface { diff --git a/modules/core/04-channel/types/multihop.go b/modules/core/04-channel/types/multihop.go index 5ec41297405..6d799c348ec 100644 --- a/modules/core/04-channel/types/multihop.go +++ b/modules/core/04-channel/types/multihop.go @@ -2,7 +2,9 @@ package types import ( errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" diff --git a/modules/core/33-multihop/query.go b/modules/core/33-multihop/query.go index cb8d88e16a9..0503a8eeb79 100644 --- a/modules/core/33-multihop/query.go +++ b/modules/core/33-multihop/query.go @@ -96,20 +96,25 @@ func (p ChanPath) QueryMultihopProof( keyHeight exported.Height, includeKeyValue bool, ) ( - multihopProof channeltypes.MsgMultihopProofs, - multihopProofHeight exported.Height, - err error, + channeltypes.MsgMultihopProofs, + exported.Height, + error, ) { + var ( + multihopProof channeltypes.MsgMultihopProofs + multihopProofHeight exported.Height + err error + ) if len(p.Paths) < 1 { err = fmt.Errorf("multihop proof query requires channel path length >= 1") - return + return multihopProof, multihopProofHeight, err } // calculate proof heights along channel path proofHeights := make([]*ProofHeights, len(p.Paths)) if err = p.calcProofHeights(0, keyHeight, proofHeights); err != nil { - return + return multihopProof, multihopProofHeight, err } // the consensus state height of the proving chain's counterparty @@ -117,19 +122,19 @@ func (p ChanPath) QueryMultihopProof( multihopProofHeight, ok := proofHeights[len(proofHeights)-1].consensusHeight.Decrement() if !ok { err = fmt.Errorf("failed to decrement consensusHeight for multihop proof height") - return + return multihopProof, multihopProofHeight, err } // the key/value proof height is the height of the consensusState on the first chain keyHeight, ok = proofHeights[0].consensusHeight.Decrement() if !ok { err = fmt.Errorf("failed to decrement consensusHeight for key height") - return + return multihopProof, multihopProofHeight, err } // query the proof of the key/value on the source chain if multihopProof.KeyProof, err = queryProof(p.Source(), key, keyHeight, false, includeKeyValue); err != nil { - return + return multihopProof, multihopProofHeight, err } // query proofs of consensus/connection states on intermediate chains @@ -139,21 +144,25 @@ func (p ChanPath) QueryMultihopProof( len(p.Paths)-2, proofHeights, multihopProof.ConsensusProofs, multihopProof.ConnectionProofs); err != nil { - return + return multihopProof, multihopProofHeight, err } - return + return multihopProof, multihopProofHeight, nil } // calcProofHeights calculates the optimal proof heights to generate a multi-hop proof along the channel path // and performs client updates as needed. -func (p ChanPath) calcProofHeights(pathIdx int, consensusHeight exported.Height, proofHeights []*ProofHeights) (err error) { - var height ProofHeights +func (p ChanPath) calcProofHeights(pathIdx int, consensusHeight exported.Height, proofHeights []*ProofHeights) error { + var ( + err error + height ProofHeights + ) + chain := p.Paths[pathIdx].EndpointB // find minimum consensus height provable on the next chain if height.proofHeight, height.consensusHeight, err = chain.QueryMinimumConsensusHeight(consensusHeight, 3); err != nil { - return + return err } // optimized consensus height query @@ -168,7 +177,7 @@ func (p ChanPath) calcProofHeights(pathIdx int, consensusHeight exported.Height, // on subsequent chains. if height.proofHeight == nil { if err = chain.UpdateClient(); err != nil { - return + return err } height.proofHeight = chain.GetLatestHeight() @@ -178,16 +187,16 @@ func (p ChanPath) calcProofHeights(pathIdx int, consensusHeight exported.Height, // stop on the next to last path segment if pathIdx == len(p.Paths)-1 { proofHeights[pathIdx] = &height - return + return err } // use the proofHeight as the next consensus height if err = p.calcProofHeights(pathIdx+1, height.proofHeight, proofHeights); err != nil { - return + return err } proofHeights[pathIdx] = &height - return + return nil } // queryIntermediateProofs recursively queries intermediate chains in a multi-hop channel path for consensus state @@ -198,11 +207,12 @@ func (p ChanPath) queryIntermediateProofs( proofHeights []*ProofHeights, consensusProofs []*channeltypes.MultihopProof, connectionProofs []*channeltypes.MultihopProof, -) (err error) { +) error { + var err error // no need to query proofs on final chain since the clientState is already known if proofIdx < 0 { - return + return nil } chain := p.Paths[proofIdx].EndpointB @@ -210,12 +220,12 @@ func (p ChanPath) queryIntermediateProofs( // query proof of the consensusState if consensusProofs[len(p.Paths)-proofIdx-2], err = queryConsensusStateProof(chain, ph.proofHeight, ph.consensusHeight); err != nil { - return + return err } // query proof of the connectionEnd if connectionProofs[len(p.Paths)-proofIdx-2], err = queryConnectionProof(chain, ph.proofHeight); err != nil { - return + return err } // continue querying proofs on the next chain in the path diff --git a/modules/core/33-multihop/verify.go b/modules/core/33-multihop/verify.go index ce0135b32ed..5278b08a352 100644 --- a/modules/core/33-multihop/verify.go +++ b/modules/core/33-multihop/verify.go @@ -7,8 +7,10 @@ import ( errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" @@ -58,7 +60,7 @@ func VerifyDelayPeriodPassed( // getBlockDelay calculates the block delay period from the time delay of the connection // and the maximum expected time per block. -func getBlockDelay(ctx sdk.Context, timeDelay uint64, expectedTimePerBlock uint64) uint64 { +func getBlockDelay(_ sdk.Context, timeDelay uint64, expectedTimePerBlock uint64) uint64 { // expectedTimePerBlock should never be zero, however if it is then return a 0 block delay for safety // as the expectedTimePerBlock parameter was not set. if expectedTimePerBlock == 0 { diff --git a/modules/light-clients/06-solomachine/consensus_state.go b/modules/light-clients/06-solomachine/consensus_state.go index b86db985dde..2b085e9b6cf 100644 --- a/modules/light-clients/06-solomachine/consensus_state.go +++ b/modules/light-clients/06-solomachine/consensus_state.go @@ -24,7 +24,7 @@ func (cs ConsensusState) GetTimestamp() uint64 { } // GetRoot returns nil since solo machines do not have roots. -func (cs ConsensusState) GetRoot() exported.Root { +func (ConsensusState) GetRoot() exported.Root { return nil } diff --git a/modules/light-clients/07-tendermint/store.go b/modules/light-clients/07-tendermint/store.go index f049ba4792f..1590254f6c0 100644 --- a/modules/light-clients/07-tendermint/store.go +++ b/modules/light-clients/07-tendermint/store.go @@ -360,9 +360,9 @@ func bigEndianHeightBytes(height exported.Height) []byte { return heightBytes } -func bigEndianBytesToHeight(bytes []byte) exported.Height { - revisionNumber := binary.BigEndian.Uint64(bytes[:8]) - revisionHeight := binary.BigEndian.Uint64(bytes[8:]) +func bigEndianBytesToHeight(bz []byte) exported.Height { + revisionNumber := binary.BigEndian.Uint64(bz[:8]) + revisionHeight := binary.BigEndian.Uint64(bz[8:]) return clienttypes.NewHeight(revisionNumber, revisionHeight) } diff --git a/testing/chain.go b/testing/chain.go index 0a37673322e..7260cd8ee40 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -242,7 +242,6 @@ func (chain *TestChain) QueryStateAtHeight(key []byte, height int64, doProof boo // The second returned height is the minimum consensusState height falling within the provided height range. // The third returned parameter is a boolean indicating whether a client update is required. func (chain *TestChain) QueryNextConsensusHeight(clientID string, minConsensusHeight exported.Height) (exported.Height, exported.Height, error) { - req := clienttypes.QueryNextConsensusStateHeightRequest{ ClientId: clientID, RevisionNumber: minConsensusHeight.GetRevisionNumber(), @@ -285,7 +284,6 @@ func (chain *TestChain) QueryNextConsensusHeight(clientID string, minConsensusHe // The second returned height is the minimum consensusState height falling within the provided height range. // The third returned parameter is a boolean indicating whether a client update is required. func (chain *TestChain) QueryMinimumConsensusHeight(clientID string, minConsensusHeight exported.Height, limit uint64) (exported.Height, exported.Height, error) { - // search consensusStates to find the one with the minimum height in [minHeight, maxHeight] consensusHeight := minConsensusHeight for i := uint64(0); i < limit; i++ { diff --git a/testing/coordinator_multihop.go b/testing/coordinator_multihop.go index 4b07ca38a25..d4941f5c00f 100644 --- a/testing/coordinator_multihop.go +++ b/testing/coordinator_multihop.go @@ -26,8 +26,8 @@ func (coord *CoordinatorM) SetupClients(path *PathM) { require.Empty(coord.T, path.EndpointA.ConnectionID) require.Empty(coord.T, path.EndpointB.ConnectionID) // add variability to the clientIDs - N := rand.Int() % 5 - for n := 0; n <= N; n++ { + number := rand.Int() % 5 + for n := 0; n <= number; n++ { coord.Coordinator.SetupClients(path) } } @@ -48,8 +48,8 @@ func (coord *CoordinatorM) SetupAllButTheSpecifiedConnection(path *PathM, index for _, path := range path.EndpointA.Paths[:index] { // add variability to the clientIDs - N := rand.Int() % 5 - for n := 0; n <= N; n++ { + number := rand.Int() % 5 + for n := 0; n <= number; n++ { coord.Coordinator.SetupClients(path) } coord.Coordinator.CreateConnections(path) @@ -57,8 +57,8 @@ func (coord *CoordinatorM) SetupAllButTheSpecifiedConnection(path *PathM, index for _, path := range path.EndpointA.Paths[index+1:] { // add variability to the clientIDs - N := rand.Int() % 5 - for n := 0; n <= N; n++ { + number := rand.Int() % 5 + for n := 0; n <= number; n++ { coord.Coordinator.SetupClients(path) } coord.Coordinator.CreateConnections(path) diff --git a/testing/endpoint_multihop.go b/testing/endpoint_multihop.go index 99887b6336e..ec37acae972 100644 --- a/testing/endpoint_multihop.go +++ b/testing/endpoint_multihop.go @@ -3,6 +3,8 @@ package ibctesting import ( "fmt" + "github.com/stretchr/testify/require" + errorsmod "cosmossdk.io/errors" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -12,7 +14,6 @@ import ( host "github.com/cosmos/ibc-go/v8/modules/core/24-host" multihop "github.com/cosmos/ibc-go/v8/modules/core/33-multihop" "github.com/cosmos/ibc-go/v8/modules/core/exported" - "github.com/stretchr/testify/require" ) // EndpointM represents a multihop channel endpoint. @@ -35,7 +36,9 @@ type EndpointM struct { // NewEndpointMFromLinkedPaths constructs a new EndpointM without the counterparty. // CONTRACT: the counterparty EndpointM must be set by the caller. -func NewEndpointMFromLinkedPaths(path LinkedPaths) (a, z EndpointM) { +func NewEndpointMFromLinkedPaths(path LinkedPaths) (EndpointM, EndpointM) { + var a, z EndpointM + a.Paths = path a.Endpoint = a.Paths.A() a.Counterparty = &z @@ -47,7 +50,8 @@ func NewEndpointMFromLinkedPaths(path LinkedPaths) (a, z EndpointM) { // create multihop channel paths a.mChanPath = a.Paths.ToMultihopChanPath() z.mChanPath = z.Paths.ToMultihopChanPath() - return + + return a, z } // ChanOpenInit will construct and execute a MsgChannelOpenInit on the associated EndpointM. @@ -73,7 +77,6 @@ func (ep *EndpointM) ChanOpenInit() error { // ChanOpenTry will construct and execute a MsgChannelOpenTry on the associated EndpointM. func (ep *EndpointM) ChanOpenTry(chanInitHeight exported.Height) error { - proof, proofHeight, err := ep.Counterparty.QueryChannelProof(chanInitHeight) if err != nil { return err @@ -104,7 +107,6 @@ func (ep *EndpointM) ChanOpenTry(chanInitHeight exported.Height) error { // ChanOpenAck will construct and execute a MsgChannelOpenAck on the associated EndpointM. func (ep *EndpointM) ChanOpenAck(chanTryHeight exported.Height) error { - proof, proofHeight, err := ep.Counterparty.QueryChannelProof(chanTryHeight) if err != nil { return err @@ -127,7 +129,6 @@ func (ep *EndpointM) ChanOpenAck(chanTryHeight exported.Height) error { // ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated EndpointM. func (ep *EndpointM) ChanOpenConfirm(chanAckHeight exported.Height) error { - proof, proofHeight, err := ep.Counterparty.QueryChannelProof(chanAckHeight) if err != nil { return err @@ -145,7 +146,7 @@ func (ep *EndpointM) ChanOpenConfirm(chanAckHeight exported.Height) error { // ChanCloseInit will construct and execute a MsgChannelCloseInit on the associated EndpointM. // // NOTE: does not work with ibc-transfer module -func (ep *EndpointM) ChanCloseInit() error { +func (*EndpointM) ChanCloseInit() error { return nil } @@ -269,17 +270,25 @@ func (ep *EndpointM) QueryChannelProof(channelHeight exported.Height) ([]byte, c } // QueryFrozenClientProof queries proof of a frozen client in the multi-hop channel path. -func (ep *EndpointM) QueryFrozenClientProof(connectionID, clientID string, frozenHeight exported.Height) (proofConnection []byte, proofClientState []byte, proofHeight clienttypes.Height, err error) { +func (ep *EndpointM) QueryFrozenClientProof(connectionID, clientID string, frozenHeight exported.Height) ([]byte, []byte, clienttypes.Height, error) { + var ( + proofConnection []byte + proofClientState []byte + proofHeight clienttypes.Height + err error + ) + connectionKey := host.ConnectionKey(connectionID) if proofConnection, proofHeight, err = ep.QueryMultihopProof(connectionKey, frozenHeight, true); err != nil { - return + return proofConnection, proofClientState, proofHeight, err } clientKey := host.FullClientStateKey(clientID) if proofClientState, _, err = ep.QueryMultihopProof(clientKey, frozenHeight, true); err != nil { - return + return proofConnection, proofClientState, proofHeight, err } - return + + return proofConnection, proofClientState, proofHeight, nil } // QueryPacketProof queries the multihop packet proof on the endpoint chain. @@ -311,21 +320,20 @@ func (ep *EndpointM) QueryPacketTimeoutProof(packet *channeltypes.Packet, packet } // QueryMultihopProof queries the proof for a key/value on this endpoint, which is verified on the counterparty chain. -func (ep *EndpointM) QueryMultihopProof(key []byte, keyHeight exported.Height, includeKeyValue bool) (proof []byte, proofHeight clienttypes.Height, err error) { - +func (ep *EndpointM) QueryMultihopProof(key []byte, keyHeight exported.Height, includeKeyValue bool) ([]byte, clienttypes.Height, error) { multiHopProof, height, err := ep.mChanPath.QueryMultihopProof(key, keyHeight, includeKeyValue) if err != nil { - return + return []byte{}, clienttypes.Height{}, err } - proof = ep.Chain.Codec.MustMarshal(&multiHopProof) - + proof := ep.Chain.Codec.MustMarshal(&multiHopProof) proofHeight, ok := height.Increment().(clienttypes.Height) if !ok { err = errorsmod.Wrap(channeltypes.ErrMultihopProofGeneration, "height type conversion failed") + return []byte{}, clienttypes.Height{}, err } - return + return proof, proofHeight, nil } // ProofHeight returns the proof height passed to this endpoint where the proof is generated for the counterparty chain. @@ -339,8 +347,8 @@ type multihopEndpoint struct { } // MultihopEndpoint returns a multihop.Endpoint implementation for the test endpoint. -func (tep *Endpoint) MultihopEndpoint() multihop.Endpoint { - return multihopEndpoint{tep} +func (endpoint *Endpoint) MultihopEndpoint() multihop.Endpoint { + return multihopEndpoint{endpoint} } var _ multihop.Endpoint = multihopEndpoint{} @@ -413,7 +421,6 @@ func (mep multihopEndpoint) QueryNextConsensusHeight(minConsensusHeight exported // UpdateClient updates the IBC client associated with the endpoint. // Returns error for non-existent clients. func (mep multihopEndpoint) UpdateClient() (err error) { - _, found := mep.testEndpoint.Chain.App.GetIBCKeeper().ClientKeeper.GetClientState(mep.testEndpoint.Chain.GetContext(), mep.testEndpoint.ClientID) if !found { return fmt.Errorf("client=%s not found on chain=%s", mep.testEndpoint.ClientID, mep.testEndpoint.Chain.ChainID) diff --git a/testing/path_multihop.go b/testing/path_multihop.go index b3e3a48a72c..c544da58240 100644 --- a/testing/path_multihop.go +++ b/testing/path_multihop.go @@ -1,9 +1,10 @@ package ibctesting import ( + "github.com/stretchr/testify/suite" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" multihop "github.com/cosmos/ibc-go/v8/modules/core/33-multihop" - "github.com/stretchr/testify/suite" ) // PathM represents a multihop channel path between two chains. From 5730ad1ba4e7afd600fd73d406fdcc864bccec5c Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 5 Jan 2024 14:34:58 +0100 Subject: [PATCH 3/6] remove unused parameter --- modules/core/33-multihop/verify.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/33-multihop/verify.go b/modules/core/33-multihop/verify.go index 5278b08a352..3f8d301c177 100644 --- a/modules/core/33-multihop/verify.go +++ b/modules/core/33-multihop/verify.go @@ -54,13 +54,13 @@ func VerifyDelayPeriodPassed( expectedTimePerBlock uint64, ) error { // get time and block delays - blockDelay := getBlockDelay(ctx, timeDelay, expectedTimePerBlock) + blockDelay := getBlockDelay(timeDelay, expectedTimePerBlock) return tmclient.VerifyDelayPeriodPassed(ctx, store, proofHeight, timeDelay, blockDelay) } // getBlockDelay calculates the block delay period from the time delay of the connection // and the maximum expected time per block. -func getBlockDelay(_ sdk.Context, timeDelay uint64, expectedTimePerBlock uint64) uint64 { +func getBlockDelay(timeDelay uint64, expectedTimePerBlock uint64) uint64 { // expectedTimePerBlock should never be zero, however if it is then return a 0 block delay for safety // as the expectedTimePerBlock parameter was not set. if expectedTimePerBlock == 0 { From 7cd3d83e06dc671a39097d162ccac98b20b18c51 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 5 Jan 2024 16:17:25 +0100 Subject: [PATCH 4/6] linting --- modules/core/04-channel/keeper/handshake_test.go | 13 +++++++++---- .../04-channel/keeper/multihop_handshake_test.go | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/core/04-channel/keeper/handshake_test.go b/modules/core/04-channel/keeper/handshake_test.go index c775e7e6297..b16ed3b452b 100644 --- a/modules/core/04-channel/keeper/handshake_test.go +++ b/modules/core/04-channel/keeper/handshake_test.go @@ -12,6 +12,11 @@ import ( ibctesting "github.com/cosmos/ibc-go/v8/testing" ) +const ( + connectionID = "connection-0" + unauthorizedStatus = "status is Unauthorized" +) + type testCase = struct { msg string malleate func() @@ -42,8 +47,8 @@ func (suite *KeeperTestSuite) TestChanOpenInit() { }, false}, {"connection doesn't exist", func() { // any non-empty values - path.EndpointA.ConnectionID = "connection-0" - path.EndpointB.ConnectionID = "connection-0" + path.EndpointA.ConnectionID = connectionID + path.EndpointB.ConnectionID = connectionID }, false}, {"capability is incorrect", func() { suite.coordinator.SetupConnections(path) @@ -89,7 +94,7 @@ func (suite *KeeperTestSuite) TestChanOpenInit() { msg: "unauthorized client", expPass: false, malleate: func() { - expErrorMsgSubstring = "status is Unauthorized" + expErrorMsgSubstring = unauthorizedStatus suite.coordinator.SetupConnections(path) // remove client from allowed list @@ -685,7 +690,7 @@ func (suite *KeeperTestSuite) TestChanCloseInit() { params := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetParams(suite.chainA.GetContext()) params.AllowedClients = []string{} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetParams(suite.chainA.GetContext(), params) - expErrorMsgSubstring = "status is Unauthorized" + expErrorMsgSubstring = unauthorizedStatus }, }, } diff --git a/modules/core/04-channel/keeper/multihop_handshake_test.go b/modules/core/04-channel/keeper/multihop_handshake_test.go index 926c320d2c1..5a3991b589d 100644 --- a/modules/core/04-channel/keeper/multihop_handshake_test.go +++ b/modules/core/04-channel/keeper/multihop_handshake_test.go @@ -36,8 +36,8 @@ func (suite *MultihopTestSuite) TestChanOpenInit() { }, false}, {"connection doesn't exist", func() { // any non-empty values - suite.A().ConnectionID = "connection-0" - suite.Z().ConnectionID = "connection-0" + suite.A().ConnectionID = connectionID + suite.Z().ConnectionID = connectionID }, false}, {"capability is incorrect", func() { suite.SetupConnections() @@ -84,7 +84,7 @@ func (suite *MultihopTestSuite) TestChanOpenInit() { portCap = suite.A().Chain.GetPortCapability(ibctesting.MockPort) }, true}, {"unauthorized client", func() { - expErrorMsgSubstring = "status is Unauthorized" + expErrorMsgSubstring = unauthorizedStatus suite.coord.SetupConnections(suite.chanPath) // remove client from allowed list From 4cc0e0e4438f1186dcf4d19a2b15a746b30ad0c1 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 2 Feb 2024 13:30:35 +0100 Subject: [PATCH 5/6] another linter fix --- modules/capability/module.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/capability/module.go b/modules/capability/module.go index 786e41cf6e8..5f048a3b838 100644 --- a/modules/capability/module.go +++ b/modules/capability/module.go @@ -47,7 +47,7 @@ func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic { } // Name returns the capability module's name. -func (am AppModuleBasic) Name() string { +func (AppModuleBasic) Name() string { return types.ModuleName } From 50f8586ff91d816bc1ee25875cecc5e8b98f3f08 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 2 Feb 2024 13:36:43 +0100 Subject: [PATCH 6/6] lint --- modules/core/keeper/msg_server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 26089e3ef76..2a31305e071 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -447,7 +447,7 @@ func (k Keeper) ChannelCloseFrozen(goCtx context.Context, msg *channeltypes.MsgC ctx := sdk.UnwrapSDKContext(goCtx) // Lookup module by channel capability - module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) + module, capability, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) if err != nil { ctx.Logger().Error("channel close frozen callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") @@ -466,7 +466,7 @@ func (k Keeper) ChannelCloseFrozen(goCtx context.Context, msg *channeltypes.MsgC return nil, errorsmod.Wrapf(err, "channel close confirm callback failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) } - err = k.ChannelKeeper.ChanCloseFrozen(ctx, msg.PortId, msg.ChannelId, cap, msg.ProofConnection, msg.ProofClientState, msg.ProofHeight) + err = k.ChannelKeeper.ChanCloseFrozen(ctx, msg.PortId, msg.ChannelId, capability, msg.ProofConnection, msg.ProofClientState, msg.ProofHeight) if err != nil { ctx.Logger().Error("channel handshake close frozen callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) return nil, errorsmod.Wrap(err, "channel handshake close frozen failed")