From e05f5319550c568c7a3556c03b3252fa1cddb3a1 Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:44:19 -0800 Subject: [PATCH 1/6] feat: gmp middleware --- app/app.go | 13 ++- app/gmp_middleware/gmp_middleware.go | 167 +++++++++++++++++++++++++++ app/gmp_middleware/handler.go | 61 ++++++++++ app/gmp_middleware/types.go | 59 ++++++++++ x/gmp/keeper/keeper.go | 76 ++++++++++++ x/gmp/keeper/msg_server.go | 69 +---------- 6 files changed, 374 insertions(+), 71 deletions(-) create mode 100644 app/gmp_middleware/gmp_middleware.go create mode 100644 app/gmp_middleware/handler.go create mode 100644 app/gmp_middleware/types.go diff --git a/app/app.go b/app/app.go index 467eb8e7..2901703a 100644 --- a/app/app.go +++ b/app/app.go @@ -97,6 +97,7 @@ import ( ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/spf13/cast" + gmp_middleware "github.com/ojo-network/ojo/app/gmp_middleware" ibctransfer "github.com/ojo-network/ojo/app/ibctransfer" "github.com/ojo-network/ojo/util/genmap" @@ -485,7 +486,8 @@ func New( scopedTransferKeeper, ) transferModule := NewIBCTransferModule(app.TransferKeeper) - transferIBCModule := NewIBCAppModule(app.TransferKeeper) + var ibcStack ibcporttypes.IBCModule + ibcStack = NewIBCAppModule(app.TransferKeeper) // Create evidence Keeper for to register the IBC light client misbehavior evidence route evidenceKeeper := evidencekeeper.NewKeeper( @@ -523,9 +525,14 @@ func New( ), ) - // Create static IBC router, add transfer route, then set and seal it + // Create static IBC router, add transfer route, then set and seal it. + // We also need to add the axelar GMP middleware here. ibcRouter := ibcporttypes.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule) + ibcStack = gmp_middleware.NewIBCMiddleware( + ibcStack, + gmp_middleware.NewGmpHandler(app.GmpKeeper), + ) + ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcStack) app.IBCKeeper.SetRouter(ibcRouter) /**** Module Options ****/ diff --git a/app/gmp_middleware/gmp_middleware.go b/app/gmp_middleware/gmp_middleware.go new file mode 100644 index 00000000..c48fb120 --- /dev/null +++ b/app/gmp_middleware/gmp_middleware.go @@ -0,0 +1,167 @@ +package gmp_middleware + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +type IBCMiddleware struct { + app porttypes.IBCModule + handler GeneralMessageHandler +} + +func NewIBCMiddleware(app porttypes.IBCModule, handler GeneralMessageHandler) IBCMiddleware { + return IBCMiddleware{ + app: app, + handler: handler, + } +} + +// OnChanOpenInit implements the IBCModule interface +func (im IBCMiddleware) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // call underlying callback + return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) +} + +// OnChanOpenTry implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, channelCap, counterparty, counterpartyVersion) +} + +// OnChanOpenAck implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyChannelID string, + counterpartyVersion string, +) error { + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) +} + +// OnChanOpenConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return im.app.OnChanOpenConfirm(ctx, portID, channelID) +} + +// OnChanCloseInit implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + return im.app.OnChanCloseInit(ctx, portID, channelID) +} + +// OnChanCloseConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return im.app.OnChanCloseConfirm(ctx, portID, channelID) +} + +// OnRecvPacket implements the IBCMiddleware interface +func (im IBCMiddleware) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) ibcexported.Acknowledgement { + ack := im.app.OnRecvPacket(ctx, packet, relayer) + if !ack.Success() { + return ack + } + + var data transfertypes.FungibleTokenPacketData + if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { + return channeltypes.NewErrorAcknowledgement(fmt.Errorf("cannot unmarshal ICS-20 transfer packet data")) + } + + // authenticate the message with packet sender + channel-id + // TODO: authenticate the message with channel-id + if data.Sender != AxelarGMPAcc { + return ack + } + + var msg Message + var err error + + if err = json.Unmarshal([]byte(data.GetMemo()), &msg); err != nil { + return channeltypes.NewErrorAcknowledgement(fmt.Errorf("cannot unmarshal memo")) + } + + switch msg.Type { + case TypeGeneralMessage: + // implement the handler + err = im.handler.HandleGeneralMessage(ctx, msg.SourceChain, msg.SourceAddress, data.Receiver, msg.Payload) + case TypeGeneralMessageWithToken: + // parse the transfer amount + amt, ok := sdk.NewIntFromString(data.Amount) + if !ok { + return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(transfertypes.ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", data.Amount)) + } + + denom := parseDenom(packet, data.Denom) + // implement the handler + err = im.handler.HandleGeneralMessageWithToken(ctx, msg.SourceChain, msg.SourceAddress, data.Receiver, msg.Payload, sdk.NewCoin(denom, amt)) + default: + err = fmt.Errorf("unrecognized message type: %d", msg.Type) + } + + if err != nil { + return channeltypes.NewErrorAcknowledgement(err) + } + + return ack +} + +// OnAcknowledgementPacket implements the IBCMiddleware interface +func (im IBCMiddleware) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) +} + +// OnTimeoutPacket implements the IBCMiddleware interface +func (im IBCMiddleware) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + return im.app.OnTimeoutPacket(ctx, packet, relayer) +} diff --git a/app/gmp_middleware/handler.go b/app/gmp_middleware/handler.go new file mode 100644 index 00000000..822aeb07 --- /dev/null +++ b/app/gmp_middleware/handler.go @@ -0,0 +1,61 @@ +package gmp_middleware + +import ( + "context" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + gmptypes "github.com/ojo-network/ojo/x/gmp/types" +) + +type GmpKeeper interface { + RelayPrice(goCtx context.Context, msg *gmptypes.MsgRelayPrice) (*gmptypes.MsgRelayPriceResponse, error) +} + +type GmpHandler struct { + gmp GmpKeeper +} + +func NewGmpHandler(k GmpKeeper) *GmpHandler { + return &GmpHandler{ + gmp: k, + } +} + +// HandleGeneralMessage takes the receiving message from axelar, +// and sends it along to the GMP module. +func (h GmpHandler) HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte) error { + ctx.Logger().Info("HandleGeneralMessage called", + "srcChain", srcChain, + "srcAddress", srcAddress, + "destAddress", destAddress, + "payload", payload, + "module", "x/gmp-middleware", + ) + + denomString := string(payload) + denoms := strings.Split(denomString, ",") + + _, err := h.gmp.RelayPrice(ctx, &gmptypes.MsgRelayPrice{ + Relayer: srcAddress, + DestinationChain: srcChain, + DestinationAddress: destAddress, + Denoms: denoms, + }, + ) + + return err +} + +// HandleGeneralMessageWithToken currently performs a no-op. +func (h GmpHandler) HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, coin sdk.Coin) error { + ctx.Logger().Info("HandleGeneralMessageWithToken called", + "srcChain", srcChain, + "srcAddress", srcAddress, + "destAddress", destAddress, + "payload", payload, + "coin", coin, + ) + + return nil +} diff --git a/app/gmp_middleware/types.go b/app/gmp_middleware/types.go new file mode 100644 index 00000000..1c5e5f07 --- /dev/null +++ b/app/gmp_middleware/types.go @@ -0,0 +1,59 @@ +package gmp_middleware + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +type GeneralMessageHandler interface { + HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte) error + HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, coin sdk.Coin) error +} + +const AxelarGMPAcc = "axelar1dv7u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5" + +// Message is attached in ICS20 packet memo field +type Message struct { + SourceChain string `json:"source_chain"` + SourceAddress string `json:"source_address"` + Payload []byte `json:"payload"` + Type int64 `json:"type"` +} + +type MessageType int + +const ( + // TypeUnrecognized means coin type is unrecognized + TypeUnrecognized = iota + TypeGeneralMessage + // TypeGeneralMessageWithToken is a general message with token + TypeGeneralMessageWithToken +) + +// parseDenom convert denom to receiver chain representation +func parseDenom(packet channeltypes.Packet, denom string) string { + if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), denom) { + // remove prefix added by sender chain + voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := denom[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + denom = unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := types.ParseDenomTrace(unprefixedDenom) + if denomTrace.Path != "" { + denom = denomTrace.IBCDenom() + } + + return denom + } + + prefixedDenom := transfertypes.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + denom + denom = transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + + return denom +} diff --git a/x/gmp/keeper/keeper.go b/x/gmp/keeper/keeper.go index 886f11c0..40331ffe 100644 --- a/x/gmp/keeper/keeper.go +++ b/x/gmp/keeper/keeper.go @@ -1,11 +1,16 @@ package keeper import ( + "context" "fmt" + "math/big" + "time" "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -44,3 +49,74 @@ func NewKeeper( func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } + +// RelayPrice +func (k Keeper) RelayPrice( + goCtx context.Context, + msg *types.MsgRelayPrice, +) (*types.MsgRelayPriceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + params := k.GetParams(ctx) + + // encode oracle data + rates := []types.PriceFeedData{} + for _, denom := range msg.Denoms { + rate, err := k.oracleKeeper.GetExchangeRate(ctx, denom) + if err != nil { + return &types.MsgRelayPriceResponse{}, err + } + + priceFeed, err := types.NewPriceFeedData( + denom, + rate, + // TODO: replace with actual resolve time & id + // Ref: https://github.com/ojo-network/ojo/issues/309 + big.NewInt(1), + big.NewInt(1), + ) + if err != nil { + k.Logger(ctx).With(err).Error("unable to relay price to gmp") + continue + } + + rates = append(rates, priceFeed) + } + + // TODO: fill with actual disableResolve option + // Ref: https://github.com/ojo-network/ojo/issues/309 + payload, err := types.EncodeABI("postPrices", rates, false) + if err != nil { + return nil, err + } + + // package GMP + message := types.GmpMessage{ + DestinationChain: msg.DestinationChain, + DestinationAddress: msg.DestinationAddress, + Payload: payload, + Type: types.TypeGeneralMessage, + } + bz, err := message.Marshal() + if err != nil { + return nil, err + } + + // submit IBC transfer + transferMsg := ibctransfertypes.NewMsgTransfer( + ibctransfertypes.PortID, + params.GmpChannel, + msg.Token, + msg.Relayer, + params.GmpAddress, + clienttypes.ZeroHeight(), + uint64(ctx.BlockTime().Add(time.Duration(params.GmpTimeout)*time.Hour).UnixNano()), + string(bz), + ) + + _, err = k.ibcKeeper.Transfer(ctx, transferMsg) + if err != nil { + return &types.MsgRelayPriceResponse{}, err + } + + return &types.MsgRelayPriceResponse{}, nil +} diff --git a/x/gmp/keeper/msg_server.go b/x/gmp/keeper/msg_server.go index 015cdf72..221031b3 100644 --- a/x/gmp/keeper/msg_server.go +++ b/x/gmp/keeper/msg_server.go @@ -2,15 +2,11 @@ package keeper import ( "context" - "math/big" - "time" "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/ojo-network/ojo/x/gmp/types" ) @@ -53,68 +49,5 @@ func (ms msgServer) RelayPrice( goCtx context.Context, msg *types.MsgRelayPrice, ) (*types.MsgRelayPriceResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - params := ms.keeper.GetParams(ctx) - - // encode oracle data - rates := []types.PriceFeedData{} - for _, denom := range msg.Denoms { - rate, err := ms.keeper.oracleKeeper.GetExchangeRate(ctx, denom) - if err != nil { - return &types.MsgRelayPriceResponse{}, err - } - - priceFeed, err := types.NewPriceFeedData( - denom, - rate, - // TODO: replace with actual resolve time & id - // Ref: https://github.com/ojo-network/ojo/issues/309 - big.NewInt(1), - big.NewInt(1), - ) - if err != nil { - ms.keeper.Logger(ctx).With(err).Error("unable to relay price to gmp") - continue - } - - rates = append(rates, priceFeed) - } - - // TODO: fill with actual disableResolve option - // Ref: https://github.com/ojo-network/ojo/issues/309 - payload, err := types.EncodeABI("postPrices", rates, false) - if err != nil { - return nil, err - } - - // package GMP - message := types.GmpMessage{ - DestinationChain: msg.DestinationChain, - DestinationAddress: msg.DestinationAddress, - Payload: payload, - Type: types.TypeGeneralMessage, - } - bz, err := message.Marshal() - if err != nil { - return nil, err - } - - // submit IBC transfer - transferMsg := ibctransfertypes.NewMsgTransfer( - ibctransfertypes.PortID, - params.GmpChannel, - msg.Token, - msg.Relayer, - params.GmpAddress, - clienttypes.ZeroHeight(), - uint64(ctx.BlockTime().Add(time.Duration(params.GmpTimeout)*time.Hour).UnixNano()), - string(bz), - ) - - _, err = ms.keeper.ibcKeeper.Transfer(ctx, transferMsg) - if err != nil { - return &types.MsgRelayPriceResponse{}, err - } - - return &types.MsgRelayPriceResponse{}, nil + return ms.keeper.RelayPrice(goCtx, msg) } From 91cbc779d2b5843c0c2472f9d6b5f4b247bb24b8 Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:50:06 -0800 Subject: [PATCH 2/6] cleanup, add channel & axelar address validation --- app/gmp_middleware/gmp_middleware.go | 80 +++++++++++++++++++++------- app/gmp_middleware/handler.go | 62 +++++++++++++++++++-- app/gmp_middleware/types.go | 16 +----- 3 files changed, 120 insertions(+), 38 deletions(-) diff --git a/app/gmp_middleware/gmp_middleware.go b/app/gmp_middleware/gmp_middleware.go index c48fb120..c9528b31 100644 --- a/app/gmp_middleware/gmp_middleware.go +++ b/app/gmp_middleware/gmp_middleware.go @@ -4,14 +4,15 @@ import ( "encoding/json" "fmt" + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + gmptypes "github.com/ojo-network/ojo/x/gmp/types" ) type IBCMiddleware struct { @@ -38,7 +39,16 @@ func (im IBCMiddleware) OnChanOpenInit( version string, ) (string, error) { // call underlying callback - return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) + return im.app.OnChanOpenInit( + ctx, + order, + connectionHops, + portID, + channelID, + chanCap, + counterparty, + version, + ) } // OnChanOpenTry implements the IBCMiddleware interface @@ -52,7 +62,16 @@ func (im IBCMiddleware) OnChanOpenTry( counterparty channeltypes.Counterparty, counterpartyVersion string, ) (string, error) { - return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, channelCap, counterparty, counterpartyVersion) + return im.app.OnChanOpenTry( + ctx, + order, + connectionHops, + portID, + channelID, + channelCap, + counterparty, + counterpartyVersion, + ) } // OnChanOpenAck implements the IBCMiddleware interface @@ -63,7 +82,13 @@ func (im IBCMiddleware) OnChanOpenAck( counterpartyChannelID string, counterpartyVersion string, ) error { - return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + return im.app.OnChanOpenAck( + ctx, + portID, + channelID, + counterpartyChannelID, + counterpartyVersion, + ) } // OnChanOpenConfirm implements the IBCMiddleware interface @@ -106,13 +131,9 @@ func (im IBCMiddleware) OnRecvPacket( var data transfertypes.FungibleTokenPacketData if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { - return channeltypes.NewErrorAcknowledgement(fmt.Errorf("cannot unmarshal ICS-20 transfer packet data")) - } - - // authenticate the message with packet sender + channel-id - // TODO: authenticate the message with channel-id - if data.Sender != AxelarGMPAcc { - return ack + return channeltypes.NewErrorAcknowledgement( + fmt.Errorf("cannot unmarshal ICS-20 transfer packet data"), + ) } var msg Message @@ -123,19 +144,40 @@ func (im IBCMiddleware) OnRecvPacket( } switch msg.Type { - case TypeGeneralMessage: - // implement the handler - err = im.handler.HandleGeneralMessage(ctx, msg.SourceChain, msg.SourceAddress, data.Receiver, msg.Payload) - case TypeGeneralMessageWithToken: + case gmptypes.TypeGeneralMessage: + err = im.handler.HandleGeneralMessage( + ctx, + msg.SourceChain, + msg.SourceAddress, + data.Receiver, + msg.Payload, + data.Sender, + packet.DestinationChannel, + ) + case gmptypes.TypeGeneralMessageWithToken: // parse the transfer amount amt, ok := sdk.NewIntFromString(data.Amount) if !ok { - return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(transfertypes.ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", data.Amount)) + return channeltypes.NewErrorAcknowledgement( + errors.Wrapf( + transfertypes.ErrInvalidAmount, + "unable to parse transfer amount (%s) into sdk.Int", + data.Amount, + ), + ) } - denom := parseDenom(packet, data.Denom) - // implement the handler - err = im.handler.HandleGeneralMessageWithToken(ctx, msg.SourceChain, msg.SourceAddress, data.Receiver, msg.Payload, sdk.NewCoin(denom, amt)) + + err = im.handler.HandleGeneralMessageWithToken( + ctx, + msg.SourceChain, + msg.SourceAddress, + data.Receiver, + msg.Payload, + data.Sender, + packet.DestinationChannel, + sdk.NewCoin(denom, amt), + ) default: err = fmt.Errorf("unrecognized message type: %d", msg.Type) } diff --git a/app/gmp_middleware/handler.go b/app/gmp_middleware/handler.go index 822aeb07..473c22ae 100644 --- a/app/gmp_middleware/handler.go +++ b/app/gmp_middleware/handler.go @@ -2,14 +2,20 @@ package gmp_middleware import ( "context" + "fmt" "strings" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ojo-network/ojo/x/gmp/types" gmptypes "github.com/ojo-network/ojo/x/gmp/types" ) type GmpKeeper interface { - RelayPrice(goCtx context.Context, msg *gmptypes.MsgRelayPrice) (*gmptypes.MsgRelayPriceResponse, error) + RelayPrice( + goCtx context.Context, + msg *gmptypes.MsgRelayPrice, + ) (*gmptypes.MsgRelayPriceResponse, error) + GetParams(ctx sdk.Context) (params types.Params) } type GmpHandler struct { @@ -24,7 +30,15 @@ func NewGmpHandler(k GmpKeeper) *GmpHandler { // HandleGeneralMessage takes the receiving message from axelar, // and sends it along to the GMP module. -func (h GmpHandler) HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte) error { +func (h GmpHandler) HandleGeneralMessage( + ctx sdk.Context, + srcChain, + srcAddress string, + destAddress string, + payload []byte, + sender string, + channel string, +) error { ctx.Logger().Info("HandleGeneralMessage called", "srcChain", srcChain, "srcAddress", srcAddress, @@ -33,6 +47,14 @@ func (h GmpHandler) HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress s "module", "x/gmp-middleware", ) + params := h.gmp.GetParams(ctx) + if !strings.EqualFold(params.GmpAddress, sender) { + return fmt.Errorf("invalid sender address: %s", sender) + } + if !strings.EqualFold(params.GmpChannel, channel) { + return fmt.Errorf("invalid channel: %s", channel) + } + denomString := string(payload) denoms := strings.Split(denomString, ",") @@ -47,8 +69,18 @@ func (h GmpHandler) HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress s return err } -// HandleGeneralMessageWithToken currently performs a no-op. -func (h GmpHandler) HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, coin sdk.Coin) error { +// HandleGeneralMessage takes the receiving message from axelar, +// and sends it along to the GMP module. +func (h GmpHandler) HandleGeneralMessageWithToken( + ctx sdk.Context, + srcChain, + srcAddress string, + destAddress string, + payload []byte, + sender string, + channel string, + coin sdk.Coin, +) error { ctx.Logger().Info("HandleGeneralMessageWithToken called", "srcChain", srcChain, "srcAddress", srcAddress, @@ -57,5 +89,25 @@ func (h GmpHandler) HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, src "coin", coin, ) - return nil + params := h.gmp.GetParams(ctx) + if !strings.EqualFold(params.GmpAddress, sender) { + return fmt.Errorf("invalid sender address: %s", sender) + } + if !strings.EqualFold(params.GmpChannel, channel) { + return fmt.Errorf("invalid channel: %s", channel) + } + + denomString := string(payload) + denoms := strings.Split(denomString, ",") + + _, err := h.gmp.RelayPrice(ctx, &gmptypes.MsgRelayPrice{ + Relayer: srcAddress, + DestinationChain: srcChain, + DestinationAddress: destAddress, + Denoms: denoms, + Token: coin, + }, + ) + + return err } diff --git a/app/gmp_middleware/types.go b/app/gmp_middleware/types.go index 1c5e5f07..af514a31 100644 --- a/app/gmp_middleware/types.go +++ b/app/gmp_middleware/types.go @@ -8,12 +8,10 @@ import ( ) type GeneralMessageHandler interface { - HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte) error - HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, coin sdk.Coin) error + HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, sender string, channel string) error + HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, sender string, channel string, coin sdk.Coin) error } -const AxelarGMPAcc = "axelar1dv7u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5" - // Message is attached in ICS20 packet memo field type Message struct { SourceChain string `json:"source_chain"` @@ -22,16 +20,6 @@ type Message struct { Type int64 `json:"type"` } -type MessageType int - -const ( - // TypeUnrecognized means coin type is unrecognized - TypeUnrecognized = iota - TypeGeneralMessage - // TypeGeneralMessageWithToken is a general message with token - TypeGeneralMessageWithToken -) - // parseDenom convert denom to receiver chain representation func parseDenom(packet channeltypes.Packet, denom string) string { if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), denom) { From d4f773d49554b9f2f3469e63978b6fe7977bfe10 Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:52:57 -0800 Subject: [PATCH 3/6] lint fixes --- app/app.go | 6 +++--- app/{gmp_middleware => gmpmiddleware}/gmp_middleware.go | 0 app/{gmp_middleware => gmpmiddleware}/handler.go | 0 app/{gmp_middleware => gmpmiddleware}/types.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename app/{gmp_middleware => gmpmiddleware}/gmp_middleware.go (100%) rename app/{gmp_middleware => gmpmiddleware}/handler.go (100%) rename app/{gmp_middleware => gmpmiddleware}/types.go (98%) diff --git a/app/app.go b/app/app.go index 2901703a..70c9785a 100644 --- a/app/app.go +++ b/app/app.go @@ -97,7 +97,7 @@ import ( ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/spf13/cast" - gmp_middleware "github.com/ojo-network/ojo/app/gmp_middleware" + gmpmiddleware "github.com/ojo-network/ojo/app/gmpmiddleware" ibctransfer "github.com/ojo-network/ojo/app/ibctransfer" "github.com/ojo-network/ojo/util/genmap" @@ -528,9 +528,9 @@ func New( // Create static IBC router, add transfer route, then set and seal it. // We also need to add the axelar GMP middleware here. ibcRouter := ibcporttypes.NewRouter() - ibcStack = gmp_middleware.NewIBCMiddleware( + ibcStack = gmpmiddleware.NewIBCMiddleware( ibcStack, - gmp_middleware.NewGmpHandler(app.GmpKeeper), + gmpmiddleware.NewGmpHandler(app.GmpKeeper), ) ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcStack) app.IBCKeeper.SetRouter(ibcRouter) diff --git a/app/gmp_middleware/gmp_middleware.go b/app/gmpmiddleware/gmp_middleware.go similarity index 100% rename from app/gmp_middleware/gmp_middleware.go rename to app/gmpmiddleware/gmp_middleware.go diff --git a/app/gmp_middleware/handler.go b/app/gmpmiddleware/handler.go similarity index 100% rename from app/gmp_middleware/handler.go rename to app/gmpmiddleware/handler.go diff --git a/app/gmp_middleware/types.go b/app/gmpmiddleware/types.go similarity index 98% rename from app/gmp_middleware/types.go rename to app/gmpmiddleware/types.go index af514a31..d049ea88 100644 --- a/app/gmp_middleware/types.go +++ b/app/gmpmiddleware/types.go @@ -1,4 +1,4 @@ -package gmp_middleware +package gmpmiddleware import ( sdk "github.com/cosmos/cosmos-sdk/types" From 53df9eaa5b4ea48d67b1f0a8699fa37851dad32a Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:58:45 -0800 Subject: [PATCH 4/6] lint fixes --- app/gmpmiddleware/gmp_middleware.go | 7 +++---- app/gmpmiddleware/handler.go | 11 +++++------ app/gmpmiddleware/types.go | 5 ++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/app/gmpmiddleware/gmp_middleware.go b/app/gmpmiddleware/gmp_middleware.go index c9528b31..7067fe21 100644 --- a/app/gmpmiddleware/gmp_middleware.go +++ b/app/gmpmiddleware/gmp_middleware.go @@ -1,4 +1,4 @@ -package gmp_middleware +package gmpmiddleware import ( "encoding/json" @@ -8,7 +8,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -129,7 +128,7 @@ func (im IBCMiddleware) OnRecvPacket( return ack } - var data transfertypes.FungibleTokenPacketData + var data types.FungibleTokenPacketData if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { return channeltypes.NewErrorAcknowledgement( fmt.Errorf("cannot unmarshal ICS-20 transfer packet data"), @@ -160,7 +159,7 @@ func (im IBCMiddleware) OnRecvPacket( if !ok { return channeltypes.NewErrorAcknowledgement( errors.Wrapf( - transfertypes.ErrInvalidAmount, + types.ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", data.Amount, ), diff --git a/app/gmpmiddleware/handler.go b/app/gmpmiddleware/handler.go index 473c22ae..41d2d841 100644 --- a/app/gmpmiddleware/handler.go +++ b/app/gmpmiddleware/handler.go @@ -1,4 +1,4 @@ -package gmp_middleware +package gmpmiddleware import ( "context" @@ -7,14 +7,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ojo-network/ojo/x/gmp/types" - gmptypes "github.com/ojo-network/ojo/x/gmp/types" ) type GmpKeeper interface { RelayPrice( goCtx context.Context, - msg *gmptypes.MsgRelayPrice, - ) (*gmptypes.MsgRelayPriceResponse, error) + msg *types.MsgRelayPrice, + ) (*types.MsgRelayPriceResponse, error) GetParams(ctx sdk.Context) (params types.Params) } @@ -58,7 +57,7 @@ func (h GmpHandler) HandleGeneralMessage( denomString := string(payload) denoms := strings.Split(denomString, ",") - _, err := h.gmp.RelayPrice(ctx, &gmptypes.MsgRelayPrice{ + _, err := h.gmp.RelayPrice(ctx, &types.MsgRelayPrice{ Relayer: srcAddress, DestinationChain: srcChain, DestinationAddress: destAddress, @@ -100,7 +99,7 @@ func (h GmpHandler) HandleGeneralMessageWithToken( denomString := string(payload) denoms := strings.Split(denomString, ",") - _, err := h.gmp.RelayPrice(ctx, &gmptypes.MsgRelayPrice{ + _, err := h.gmp.RelayPrice(ctx, &types.MsgRelayPrice{ Relayer: srcAddress, DestinationChain: srcChain, DestinationAddress: destAddress, diff --git a/app/gmpmiddleware/types.go b/app/gmpmiddleware/types.go index d049ea88..23da0da9 100644 --- a/app/gmpmiddleware/types.go +++ b/app/gmpmiddleware/types.go @@ -3,7 +3,6 @@ package gmpmiddleware import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) @@ -40,8 +39,8 @@ func parseDenom(packet channeltypes.Packet, denom string) string { return denom } - prefixedDenom := transfertypes.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + denom - denom = transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + prefixedDenom := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + denom + denom = types.ParseDenomTrace(prefixedDenom).IBCDenom() return denom } From 6d3a9b5ad76088137c24ac8340dbff7161ccc8a5 Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:05:20 -0800 Subject: [PATCH 5/6] lint --- app/gmpmiddleware/types.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/gmpmiddleware/types.go b/app/gmpmiddleware/types.go index 23da0da9..70ae8892 100644 --- a/app/gmpmiddleware/types.go +++ b/app/gmpmiddleware/types.go @@ -7,8 +7,25 @@ import ( ) type GeneralMessageHandler interface { - HandleGeneralMessage(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, sender string, channel string) error - HandleGeneralMessageWithToken(ctx sdk.Context, srcChain, srcAddress string, destAddress string, payload []byte, sender string, channel string, coin sdk.Coin) error + HandleGeneralMessage( + ctx sdk.Context, + srcChain, + srcAddress string, + destAddress string, + payload []byte, + sender string, + channel string, + ) error + HandleGeneralMessageWithToken( + ctx sdk.Context, + srcChain, + srcAddress string, + destAddress string, + payload []byte, + sender string, + channel string, + coin sdk.Coin, + ) error } // Message is attached in ICS20 packet memo field From c1167956a48802d1803a3f3c61e412761ebd288e Mon Sep 17 00:00:00 2001 From: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:37:31 -0800 Subject: [PATCH 6/6] add some small tests --- app/gmpmiddleware/handler.go | 60 +++++++++++++++------------------ app/gmpmiddleware/types.go | 26 ++++++++++++++ app/gmpmiddleware/types_test.go | 51 ++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 33 deletions(-) create mode 100644 app/gmpmiddleware/types_test.go diff --git a/app/gmpmiddleware/handler.go b/app/gmpmiddleware/handler.go index 41d2d841..05a058eb 100644 --- a/app/gmpmiddleware/handler.go +++ b/app/gmpmiddleware/handler.go @@ -2,8 +2,6 @@ package gmpmiddleware import ( "context" - "fmt" - "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ojo-network/ojo/x/gmp/types" @@ -46,25 +44,23 @@ func (h GmpHandler) HandleGeneralMessage( "module", "x/gmp-middleware", ) - params := h.gmp.GetParams(ctx) - if !strings.EqualFold(params.GmpAddress, sender) { - return fmt.Errorf("invalid sender address: %s", sender) + err := verifyParams(h.gmp.GetParams(ctx), sender, channel) + if err != nil { + return err } - if !strings.EqualFold(params.GmpChannel, channel) { - return fmt.Errorf("invalid channel: %s", channel) + denoms, err := parsePayload(payload) + if err != nil { + return err } - denomString := string(payload) - denoms := strings.Split(denomString, ",") - - _, err := h.gmp.RelayPrice(ctx, &types.MsgRelayPrice{ - Relayer: srcAddress, - DestinationChain: srcChain, - DestinationAddress: destAddress, - Denoms: denoms, - }, + _, err = h.gmp.RelayPrice(ctx, + &types.MsgRelayPrice{ + Relayer: srcAddress, + DestinationChain: srcChain, + DestinationAddress: destAddress, + Denoms: denoms, + }, ) - return err } @@ -88,25 +84,23 @@ func (h GmpHandler) HandleGeneralMessageWithToken( "coin", coin, ) - params := h.gmp.GetParams(ctx) - if !strings.EqualFold(params.GmpAddress, sender) { - return fmt.Errorf("invalid sender address: %s", sender) + err := verifyParams(h.gmp.GetParams(ctx), sender, channel) + if err != nil { + return err } - if !strings.EqualFold(params.GmpChannel, channel) { - return fmt.Errorf("invalid channel: %s", channel) + denoms, err := parsePayload(payload) + if err != nil { + return err } - denomString := string(payload) - denoms := strings.Split(denomString, ",") - - _, err := h.gmp.RelayPrice(ctx, &types.MsgRelayPrice{ - Relayer: srcAddress, - DestinationChain: srcChain, - DestinationAddress: destAddress, - Denoms: denoms, - Token: coin, - }, + _, err = h.gmp.RelayPrice(ctx, + &types.MsgRelayPrice{ + Relayer: srcAddress, + DestinationChain: srcChain, + DestinationAddress: destAddress, + Denoms: denoms, + Token: coin, + }, ) - return err } diff --git a/app/gmpmiddleware/types.go b/app/gmpmiddleware/types.go index 70ae8892..a89cbe6d 100644 --- a/app/gmpmiddleware/types.go +++ b/app/gmpmiddleware/types.go @@ -1,9 +1,13 @@ package gmpmiddleware import ( + "fmt" + "strings" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + gmptypes "github.com/ojo-network/ojo/x/gmp/types" ) type GeneralMessageHandler interface { @@ -61,3 +65,25 @@ func parseDenom(packet channeltypes.Packet, denom string) string { return denom } + +func parsePayload(payload []byte) ([]string, error) { + denomString := string(payload) + if len(denomString) < 1 { + return []string{}, fmt.Errorf("unable to parse payload") + } + denoms := strings.Split(denomString, ",") + if len(denoms) < 1 { + return []string{}, fmt.Errorf("unable to parse payload") + } + return denoms, nil +} + +func verifyParams(params gmptypes.Params, sender string, channel string) error { + if !strings.EqualFold(params.GmpAddress, sender) { + return fmt.Errorf("invalid sender address: %s", sender) + } + if !strings.EqualFold(params.GmpChannel, channel) { + return fmt.Errorf("invalid channel: %s", channel) + } + return nil +} diff --git a/app/gmpmiddleware/types_test.go b/app/gmpmiddleware/types_test.go new file mode 100644 index 00000000..5c90c49b --- /dev/null +++ b/app/gmpmiddleware/types_test.go @@ -0,0 +1,51 @@ +package gmpmiddleware + +import ( + "testing" + + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/ojo-network/ojo/x/gmp/types" + "github.com/stretchr/testify/require" +) + +func TestVerifyParams(t *testing.T) { + params := types.Params{ + GmpAddress: "gmpAddress", + GmpChannel: "gmpChannel", + } + err := verifyParams(params, "gmpAddress", "gmpChannel") + require.NoError(t, err) + + err = verifyParams(params, "notAddress", "notChannel") + require.Error(t, err) +} + +func TestParsePayload(t *testing.T) { + payload := []byte(`denom1,denom2`) + denoms, err := parsePayload(payload) + require.NoError(t, err) + require.Equal(t, []string{"denom1", "denom2"}, denoms) + + payload = []byte(`denom1`) + denoms, err = parsePayload(payload) + require.NoError(t, err) + require.Equal(t, []string{"denom1"}, denoms) + + payload = []byte(``) + _, err = parsePayload(payload) + require.Error(t, err) +} + +func TestParseDenom(t *testing.T) { + packet := channeltypes.Packet{ + SourcePort: "ibc", + SourceChannel: "channel-0", + } + denom := "ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F" + parsedDenom := parseDenom(packet, denom) + require.Equal(t, "//ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F", parsedDenom) + + denom = "ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F/ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F" + parsedDenom = parseDenom(packet, denom) + require.Equal(t, "//ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F/ibc/1D2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F", parsedDenom) +}