diff --git a/x/ibchooks/types/types.go b/x/ibchooks/types/types.go index df766920c8..21bb01f324 100644 --- a/x/ibchooks/types/types.go +++ b/x/ibchooks/types/types.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" @@ -86,13 +87,13 @@ func UnmarshalIBCAck(bz []byte) (*IBCAck, error) { } type IbcAck struct { - Channel string `json:"channel"` - Sequence uint64 `json:"sequence"` - Ack string `json:"ack"` - Success string `json:"success"` + Channel string `json:"channel"` + Sequence uint64 `json:"sequence"` + Ack JsonBytes `json:"ack"` + Success bool `json:"success"` } -type IbcLifecycleCompleteSuccess struct { +type IbcLifecycleCompleteAck struct { IbcAck IbcAck `json:"ibc_ack"` } @@ -101,10 +102,14 @@ type IbcTimeout struct { Sequence uint64 `json:"sequence"` } -type IbcLifecycleComplete struct { +type IbcLifecycleCompleteTimeout struct { IbcTimeout IbcTimeout `json:"ibc_timeout"` } +type IbcLifecycleComplete struct { + IbcLifecycleComplete interface{} `json:"ibc_lifecycle_complete"` +} + type MarkerMemo struct { Marker MarkerPayload `json:"marker"` } @@ -122,3 +127,35 @@ func NewMarkerMemo(transferAuthAddrs []sdk.AccAddress) MarkerMemo { TransferAuth: addresses, }} } + +func NewIbcLifecycleCompleteAck(sourceChannel string, sequence uint64, ackAsJSON []byte, success bool) IbcLifecycleComplete { + sourceChannel = strings.ReplaceAll(sourceChannel, "\"", "") + ibcLifecycleCompleteAck := IbcLifecycleCompleteAck{ + IbcAck: IbcAck{ + Channel: sourceChannel, + Sequence: sequence, + Ack: ackAsJSON, + Success: success, + }, + } + return IbcLifecycleComplete{IbcLifecycleComplete: ibcLifecycleCompleteAck} +} + +func NewIbcLifecycleCompleteTimeout(sourceChannel string, sequence uint64) IbcLifecycleComplete { + ibcLifecycleCompleteAck := IbcLifecycleCompleteTimeout{ + IbcTimeout: IbcTimeout{ + Channel: sourceChannel, + Sequence: sequence, + }, + } + return IbcLifecycleComplete{IbcLifecycleComplete: ibcLifecycleCompleteAck} +} + +type JsonBytes []byte + +func (jb JsonBytes) MarshalJSON() ([]byte, error) { + if len(jb) == 0 { + return []byte("{}"), nil + } + return jb, nil +} diff --git a/x/ibchooks/types/types_test.go b/x/ibchooks/types/types_test.go new file mode 100644 index 0000000000..afc3be8f10 --- /dev/null +++ b/x/ibchooks/types/types_test.go @@ -0,0 +1,35 @@ +package types + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/suite" +) + +type IbcHooksTypesTestSuite struct { + suite.Suite +} + +func (s *IbcHooksTypesTestSuite) SetupTest() {} + +func TestSITestSuite(t *testing.T) { + suite.Run(t, new(IbcHooksTypesTestSuite)) +} + +func (s *IbcHooksTypesTestSuite) TestIbcLifecycleCompleteAckJsonSerialization() { + ack := RequestAckI{SourceChannel: "channel", PacketSequence: 100} + ackBz, err := json.Marshal(ack) + s.Require().NoError(err) + ibcLifecycleCompleteAck := NewIbcLifecycleCompleteAck("channel-1", 100, ackBz, true) + sudoMsg, err := json.Marshal(ibcLifecycleCompleteAck) + s.Require().NoError(err) + s.Require().Equal(`{"ibc_lifecycle_complete":{"ibc_ack":{"channel":"channel-1","sequence":100,"ack":{"packet_sequence":100,"source_channel":"channel"},"success":true}}}`, string(sudoMsg)) +} + +func (s *IbcHooksTypesTestSuite) TestIbcLifecycleCompleteTimeoutJsonSerialization() { + ibcLifecycleCompleteAck := NewIbcLifecycleCompleteTimeout("channel-1", 100) + sudoMsg, err := json.Marshal(ibcLifecycleCompleteAck) + s.Require().NoError(err) + s.Require().Equal(`{"ibc_lifecycle_complete":{"ibc_timeout":{"channel":"channel-1","sequence":100}}}`, string(sudoMsg)) +} diff --git a/x/ibchooks/wasm_hook.go b/x/ibchooks/wasm_hook.go index 2c6e5cd9a3..1bff573930 100644 --- a/x/ibchooks/wasm_hook.go +++ b/x/ibchooks/wasm_hook.go @@ -3,7 +3,6 @@ package ibchooks import ( "encoding/json" "fmt" - "strings" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -322,10 +321,7 @@ func (h WasmHooks) OnAcknowledgementPacketOverride(im IBCMiddleware, ctx sdktype return sdkerrors.Wrap(err, "Ack callback error") } - success := "false" - if !IsJSONAckError(acknowledgement) { - success = "true" - } + success := !IsJSONAckError(acknowledgement) // Notify the sender that the ack has been received ackAsJSON, err := json.Marshal(acknowledgement) @@ -333,17 +329,7 @@ func (h WasmHooks) OnAcknowledgementPacketOverride(im IBCMiddleware, ctx sdktype return err } - // This should never match anything, but we want to satisfy the github code scanning flag. - sanitizedSourceChannel := strings.ReplaceAll(packet.SourceChannel, "\"", "") - - ibcLifecycleComplete := types.IbcLifecycleCompleteSuccess{ - IbcAck: types.IbcAck{ - Channel: sanitizedSourceChannel, - Sequence: packet.Sequence, - Ack: string(ackAsJSON), - Success: success, - }, - } + ibcLifecycleComplete := types.NewIbcLifecycleCompleteAck(packet.SourceChannel, packet.Sequence, ackAsJSON, success) sudoMsg, err := json.Marshal(ibcLifecycleComplete) if err != nil { return sdkerrors.Wrap(err, "Ack callback error") @@ -369,30 +355,25 @@ func (h WasmHooks) OnTimeoutPacketOverride(im IBCMiddleware, ctx sdktypes.Contex } if !h.ProperlyConfigured() { - // Not configured. Return from the underlying implementation return nil } contract := h.ibcHooksKeeper.GetPacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence()) if contract == "" { - // No callback configured return nil } contractAddr, err := sdktypes.AccAddressFromBech32(contract) if err != nil { - return sdkerrors.Wrap(err, "Timeout callback error") // The callback configured is not a bech32. Error out + return sdkerrors.Wrap(err, "Timeout callback error") } - sudoMsg := IbcLifecycleComplete{ - IbcTimeout: IbcTimeout{ - Channel: packet.SourceChannel, - Sequence: packet.Sequence, - }, + sudoMsg := types.NewIbcLifecycleCompleteTimeout(packet.SourceChannel, packet.Sequence) + jsonData, err := json.Marshal(sudoMsg) + if err != nil { + return sdkerrors.Wrap(err, "Timeout callback error") } - // TODO do something with error - jsonData, _ := json.Marshal(sudoMsg) _, err = h.ContractKeeper.Sudo(ctx, contractAddr, jsonData) if err != nil { // error processing the callback. This could be because the contract doesn't implement the message type to