Skip to content

Commit

Permalink
channel/v2: Add Keeper Tests (#7456)
Browse files Browse the repository at this point in the history
* test progress

* use export_test and fix send_packet

* fix test build

* in-progress fixing tests

* remove test file

* fix mockv2 acknowledgment

* comment on export_test

* appease linter

* fix bug

* fix nits and add test case

* add writeAck tests

* use existing endpoint test fns
  • Loading branch information
AdityaSripal authored Oct 17, 2024
1 parent 4074c58 commit a3a61ff
Show file tree
Hide file tree
Showing 13 changed files with 1,202 additions and 1,155 deletions.
70 changes: 70 additions & 0 deletions modules/core/04-channel/v2/keeper/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package keeper

/*
This file is to allow for unexported functions to be accessible to the testing package.
*/

import (
"context"

channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
)

func (k *Keeper) SendPacketTest(
ctx context.Context,
sourceChannel string,
timeoutTimestamp uint64,
data []channeltypesv2.PacketData,
) (uint64, string, error) {
return k.sendPacket(
ctx,
sourceChannel,
timeoutTimestamp,
data,
)
}

func (k *Keeper) RecvPacketTest(
ctx context.Context,
packet channeltypesv2.Packet,
proof []byte,
proofHeight exported.Height,
) error {
return k.recvPacket(
ctx,
packet,
proof,
proofHeight,
)
}

func (k *Keeper) AcknowledgePacketTest(
ctx context.Context,
packet channeltypesv2.Packet,
acknowledgement channeltypesv2.Acknowledgement,
proof []byte,
proofHeight exported.Height,
) error {
return k.acknowledgePacket(
ctx,
packet,
acknowledgement,
proof,
proofHeight,
)
}

func (k *Keeper) TimeoutPacketTest(
ctx context.Context,
packet channeltypesv2.Packet,
proof []byte,
proofHeight exported.Height,
) error {
return k.timeoutPacket(
ctx,
packet,
proof,
proofHeight,
)
}
18 changes: 6 additions & 12 deletions modules/core/04-channel/v2/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ func (suite *KeeperTestSuite) TestMsgSendPacket() {
expError: mock.MockApplicationCallbackError,
},
{
name: "failure: counterparty not found",
name: "failure: channel not found",
malleate: func() {
path.EndpointA.ChannelID = ibctesting.InvalidID
},
expError: channeltypesv1.ErrChannelNotFound,
expError: channeltypesv2.ErrChannelNotFound,
},
{
name: "failure: route to non existing app",
Expand Down Expand Up @@ -212,11 +212,8 @@ func (suite *KeeperTestSuite) TestMsgRecvPacket() {
expectedAck = channeltypesv2.Acknowledgement{
AcknowledgementResults: []channeltypesv2.AcknowledgementResult{
{
AppName: mockv2.ModuleNameB,
RecvPacketResult: channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Success,
Acknowledgement: mock.MockPacketData,
},
AppName: mockv2.ModuleNameB,
RecvPacketResult: mockv2.MockRecvPacketResult,
},
},
}
Expand Down Expand Up @@ -411,11 +408,8 @@ func (suite *KeeperTestSuite) TestMsgAcknowledgement() {
ack = channeltypesv2.Acknowledgement{
AcknowledgementResults: []channeltypesv2.AcknowledgementResult{
{
AppName: mockv2.ModuleNameB,
RecvPacketResult: channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Success,
Acknowledgement: mock.MockPacketData,
},
AppName: mockv2.ModuleNameB,
RecvPacketResult: mockv2.MockRecvPacketResult,
},
},
}
Expand Down
65 changes: 39 additions & 26 deletions modules/core/04-channel/v2/keeper/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"bytes"
"context"
"strconv"
"time"

errorsmod "cosmossdk.io/errors"

sdk "github.com/cosmos/cosmos-sdk/types"

clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types"
channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
hostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
)
Expand All @@ -22,15 +23,15 @@ func (k *Keeper) sendPacket(
ctx context.Context,
sourceChannel string,
timeoutTimestamp uint64,
data []channeltypesv2.PacketData,
data []types.PacketData,
) (uint64, string, error) {
// Lookup channel associated with our source channel to retrieve the destination channel
channel, ok := k.GetChannel(ctx, sourceChannel)
if !ok {
// TODO: figure out how aliasing will work when more than one packet data is sent.
channel, ok = k.convertV1Channel(ctx, data[0].SourcePort, sourceChannel)
if !ok {
return 0, "", errorsmod.Wrap(channeltypesv2.ErrChannelNotFound, sourceChannel)
return 0, "", errorsmod.Wrap(types.ErrChannelNotFound, sourceChannel)
}
}

Expand All @@ -40,13 +41,13 @@ func (k *Keeper) sendPacket(
sequence, found := k.GetNextSequenceSend(ctx, sourceChannel)
if !found {
return 0, "", errorsmod.Wrapf(
channeltypesv2.ErrSequenceSendNotFound,
types.ErrSequenceSendNotFound,
"source channel: %s", sourceChannel,
)
}

// construct packet from given fields and channel state
packet := channeltypesv2.NewPacket(sequence, sourceChannel, destChannel, timeoutTimestamp, data...)
packet := types.NewPacket(sequence, sourceChannel, destChannel, timeoutTimestamp, data...)

if err := packet.ValidateBasic(); err != nil {
return 0, "", errorsmod.Wrapf(channeltypes.ErrInvalidPacket, "constructed packet failed basic validation: %v", err)
Expand All @@ -68,12 +69,14 @@ func (k *Keeper) sendPacket(
return 0, "", err
}
// check if packet is timed out on the receiving chain
timeout := channeltypes.NewTimeoutWithTimestamp(timeoutTimestamp)
// convert packet timeout to nanoseconds for now to use existing helper function
// TODO: Remove this workaround with Issue #7414: https://github.com/cosmos/ibc-go/issues/7414
timeout := channeltypes.NewTimeoutWithTimestamp(uint64(time.Unix(int64(packet.GetTimeoutTimestamp()), 0).UnixNano()))
if timeout.TimestampElapsed(latestTimestamp) {
return 0, "", errorsmod.Wrap(timeout.ErrTimeoutElapsed(latestHeight, latestTimestamp), "invalid packet timeout")
}

commitment := channeltypesv2.CommitPacket(packet)
commitment := types.CommitPacket(packet)

// bump the sequence and set the packet commitment, so it is provable by the counterparty
k.SetNextSequenceSend(ctx, sourceChannel, sequence+1)
Expand All @@ -95,7 +98,7 @@ func (k *Keeper) sendPacket(
// to indicate to the counterparty successful delivery.
func (k *Keeper) recvPacket(
ctx context.Context,
packet channeltypesv2.Packet,
packet types.Packet,
proof []byte,
proofHeight exported.Height,
) error {
Expand All @@ -107,7 +110,7 @@ func (k *Keeper) recvPacket(
// TODO: figure out how aliasing will work when more than one packet data is sent.
channel, ok = k.convertV1Channel(ctx, packet.Data[0].DestinationPort, packet.DestinationChannel)
if !ok {
return errorsmod.Wrap(channeltypesv2.ErrChannelNotFound, packet.DestinationChannel)
return errorsmod.Wrap(types.ErrChannelNotFound, packet.DestinationChannel)
}
}
if channel.CounterpartyChannelId != packet.SourceChannel {
Expand All @@ -118,7 +121,9 @@ func (k *Keeper) recvPacket(
// check if packet timed out by comparing it with the latest height of the chain
sdkCtx := sdk.UnwrapSDKContext(ctx)
selfHeight, selfTimestamp := clienttypes.GetSelfHeight(ctx), uint64(sdkCtx.BlockTime().UnixNano())
timeout := channeltypes.NewTimeoutWithTimestamp(packet.GetTimeoutTimestamp())
// convert packet timeout to nanoseconds for now to use existing helper function
// TODO: Remove this workaround with Issue #7414: https://github.com/cosmos/ibc-go/issues/7414
timeout := channeltypes.NewTimeoutWithTimestamp(uint64(time.Unix(int64(packet.GetTimeoutTimestamp()), 0).UnixNano()))
if timeout.Elapsed(selfHeight, selfTimestamp) {
return errorsmod.Wrap(timeout.ErrTimeoutElapsed(selfHeight, selfTimestamp), "packet timeout elapsed")
}
Expand All @@ -135,9 +140,9 @@ func (k *Keeper) recvPacket(
}

path := hostv2.PacketCommitmentKey(packet.SourceChannel, packet.Sequence)
merklePath := channeltypesv2.BuildMerklePath(channel.MerklePathPrefix, path)
merklePath := types.BuildMerklePath(channel.MerklePathPrefix, path)

commitment := channeltypesv2.CommitPacket(packet)
commitment := types.CommitPacket(packet)

if err := k.ClientKeeper.VerifyMembership(
ctx,
Expand All @@ -164,15 +169,15 @@ func (k *Keeper) recvPacket(
// WriteAcknowledgement writes the acknowledgement to the store.
func (k Keeper) WriteAcknowledgement(
ctx context.Context,
packet channeltypesv2.Packet,
ack channeltypesv2.Acknowledgement,
packet types.Packet,
ack types.Acknowledgement,
) error {
// Lookup channel associated with destination channel ID and ensure
// that the packet was indeed sent by our counterparty by verifying
// packet sender is our channel's counterparty channel id.
channel, ok := k.GetChannel(ctx, packet.DestinationChannel)
if !ok {
return errorsmod.Wrapf(channeltypesv2.ErrChannelNotFound, "channel (%s) not found", packet.DestinationChannel)
return errorsmod.Wrapf(types.ErrChannelNotFound, "channel (%s) not found", packet.DestinationChannel)
}

if channel.CounterpartyChannelId != packet.SourceChannel {
Expand All @@ -190,10 +195,16 @@ func (k Keeper) WriteAcknowledgement(
return errorsmod.Wrap(channeltypes.ErrInvalidPacket, "receipt not found for packet")
}

// TODO: Validate Acknowledgment more thoroughly here after Issue #7472: https://github.com/cosmos/ibc-go/issues/7472

if len(ack.AcknowledgementResults) != len(packet.Data) {
return errorsmod.Wrapf(types.ErrInvalidAcknowledgement, "length of acknowledgement results %d does not match length of packet data %d", len(ack.AcknowledgementResults), len(packet.Data))
}

// set the acknowledgement so that it can be verified on the other side
k.SetPacketAcknowledgement(
ctx, packet.DestinationChannel, packet.Sequence,
channeltypesv2.CommitAcknowledgement(ack),
types.CommitAcknowledgement(ack),
)

k.Logger(ctx).Info("acknowledgement written", "sequence", strconv.FormatUint(packet.Sequence, 10), "dest-channel", packet.DestinationChannel)
Expand All @@ -203,12 +214,12 @@ func (k Keeper) WriteAcknowledgement(
return nil
}

func (k *Keeper) acknowledgePacket(ctx context.Context, packet channeltypesv2.Packet, acknowledgement channeltypesv2.Acknowledgement, proof []byte, proofHeight exported.Height) error {
func (k *Keeper) acknowledgePacket(ctx context.Context, packet types.Packet, acknowledgement types.Acknowledgement, proof []byte, proofHeight exported.Height) error {
// Lookup counterparty associated with our channel and ensure
// that the packet was indeed sent by our counterparty.
channel, ok := k.GetChannel(ctx, packet.SourceChannel)
if !ok {
return errorsmod.Wrap(channeltypesv2.ErrChannelNotFound, packet.SourceChannel)
return errorsmod.Wrap(types.ErrChannelNotFound, packet.SourceChannel)
}

if channel.CounterpartyChannelId != packet.DestinationChannel {
Expand All @@ -229,15 +240,15 @@ func (k *Keeper) acknowledgePacket(ctx context.Context, packet channeltypesv2.Pa
return channeltypes.ErrNoOpMsg
}

packetCommitment := channeltypesv2.CommitPacket(packet)
packetCommitment := types.CommitPacket(packet)

// verify we sent the packet and haven't cleared it out yet
if !bytes.Equal(commitment, packetCommitment) {
return errorsmod.Wrapf(channeltypes.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment)
}

path := hostv2.PacketAcknowledgementKey(packet.DestinationChannel, packet.Sequence)
merklePath := channeltypesv2.BuildMerklePath(channel.MerklePathPrefix, path)
merklePath := types.BuildMerklePath(channel.MerklePathPrefix, path)

if err := k.ClientKeeper.VerifyMembership(
ctx,
Expand All @@ -246,7 +257,7 @@ func (k *Keeper) acknowledgePacket(ctx context.Context, packet channeltypesv2.Pa
0, 0,
proof,
merklePath,
channeltypesv2.CommitAcknowledgement(acknowledgement),
types.CommitAcknowledgement(acknowledgement),
); err != nil {
return errorsmod.Wrapf(err, "failed packet acknowledgement verification for client (%s)", clientID)
}
Expand All @@ -269,7 +280,7 @@ func (k *Keeper) acknowledgePacket(ctx context.Context, packet channeltypesv2.Pa
// is deleted and the packet has completed its lifecycle.
func (k *Keeper) timeoutPacket(
ctx context.Context,
packet channeltypesv2.Packet,
packet types.Packet,
proof []byte,
proofHeight exported.Height,
) error {
Expand All @@ -280,7 +291,7 @@ func (k *Keeper) timeoutPacket(
// TODO: figure out how aliasing will work when more than one packet data is sent.
channel, ok = k.convertV1Channel(ctx, packet.Data[0].SourcePort, packet.SourceChannel)
if !ok {
return errorsmod.Wrap(channeltypesv2.ErrChannelNotFound, packet.DestinationChannel)
return errorsmod.Wrap(types.ErrChannelNotFound, packet.DestinationChannel)
}
}
if channel.CounterpartyChannelId != packet.DestinationChannel {
Expand All @@ -294,7 +305,9 @@ func (k *Keeper) timeoutPacket(
return err
}

timeout := channeltypes.NewTimeoutWithTimestamp(packet.GetTimeoutTimestamp())
// convert packet timeout to nanoseconds for now to use existing helper function
// TODO: Remove this workaround with Issue #7414: https://github.com/cosmos/ibc-go/issues/7414
timeout := channeltypes.NewTimeoutWithTimestamp(uint64(time.Unix(int64(packet.GetTimeoutTimestamp()), 0).UnixNano()))
if !timeout.Elapsed(clienttypes.ZeroHeight(), proofTimestamp) {
return errorsmod.Wrap(timeout.ErrTimeoutNotReached(proofHeight.(clienttypes.Height), proofTimestamp), "packet timeout not reached")
}
Expand All @@ -310,15 +323,15 @@ func (k *Keeper) timeoutPacket(
return channeltypes.ErrNoOpMsg
}

packetCommitment := channeltypesv2.CommitPacket(packet)
packetCommitment := types.CommitPacket(packet)
// verify we sent the packet and haven't cleared it out yet
if !bytes.Equal(commitment, packetCommitment) {
return errorsmod.Wrapf(channeltypes.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment)
}

// verify packet receipt absence
path := hostv2.PacketReceiptKey(packet.SourceChannel, packet.Sequence)
merklePath := channeltypesv2.BuildMerklePath(channel.MerklePathPrefix, path)
merklePath := types.BuildMerklePath(channel.MerklePathPrefix, path)

if err := k.ClientKeeper.VerifyNonMembership(
ctx,
Expand Down
Loading

0 comments on commit a3a61ff

Please sign in to comment.