diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b0b202822..0cc8794c755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#432](https://github.com/cosmos/ibc-go/pull/432) Introduce `MockIBCApp` struct to the mock module. Allows the mock module to be reused to perform custom logic on each IBC App interface function. This might be useful when testing out IBC applications written as middleware. * [\#380](https://github.com/cosmos/ibc-go/pull/380) Adding the Interchain Accounts module v1 * [\#679](https://github.com/cosmos/ibc-go/pull/679) New CLI command `query ibc-transfer denom-hash ` to get the denom hash for a denom trace; this might be useful for debug +* (channel) [\#895](https://github.com/cosmos/ibc-go/pull/895) Adding UnpackAcknowledgement and PackAcknowledgement helper functions to pack or unpack an Acknowledgement to and from a proto Any type + ### Bug Fixes diff --git a/modules/core/04-channel/types/codec.go b/modules/core/04-channel/types/codec.go index 8981417130b..f77cd90c051 100644 --- a/modules/core/04-channel/types/codec.go +++ b/modules/core/04-channel/types/codec.go @@ -4,7 +4,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/msgservice" + proto "github.com/gogo/protobuf/proto" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -50,3 +52,35 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { // The actual codec used for serialization should be provided to x/ibc/core/04-channel and // defined at the application level. var SubModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + +// UnpackAcknowledgement unpacks an Any into an Acknowledgement. It returns an error if the +// Any can't be unpacked into an Acknowledgement. +func UnpackAcknowledgement(any *codectypes.Any) (exported.Acknowledgement, error) { + if any == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + ack, ok := any.GetCachedValue().(exported.Acknowledgement) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Acknowledgement %T", any) + } + + return ack, nil +} + +// PackAcknowledgement constructs a new Any packed with the given acknowledgement value. It returns +// an error if the acknowledgement can't be casted to a protobuf message or if the concrete +// implemention is not registered to the protobuf codec. +func PackAcknowledgement(acknowledgement exported.Acknowledgement) (*codectypes.Any, error) { + msg, ok := acknowledgement.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", acknowledgement) + } + + anyAcknowledgement, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + } + + return anyAcknowledgement, nil +} diff --git a/modules/core/04-channel/types/codec_test.go b/modules/core/04-channel/types/codec_test.go new file mode 100644 index 00000000000..bb0c8b81ddb --- /dev/null +++ b/modules/core/04-channel/types/codec_test.go @@ -0,0 +1,58 @@ +package types_test + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + + "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" +) + +type caseAny struct { + name string + any *codectypes.Any + expPass bool +} + +func (suite *TypesTestSuite) TestPackAcknowledgement() { + + testCases := []struct { + name string + acknowledgement exported.Acknowledgement + expPass bool + }{ + { + "success", + &ibcmock.MockAcknowledgement, + true, + }, + { + "nil", + nil, + false, + }, + } + + testCasesAny := []caseAny{} + + for _, tc := range testCases { + ackAny, err := types.PackAcknowledgement(tc.acknowledgement) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + + testCasesAny = append(testCasesAny, caseAny{tc.name, ackAny, tc.expPass}) + } + + for i, tc := range testCasesAny { + cs, err := types.UnpackAcknowledgement(tc.any) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(testCases[i].acknowledgement, cs, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +}