Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
282 lines
8.7 KiB
Go
282 lines
8.7 KiB
Go
package transfer
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"slices"
|
|
"strings"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/internal/events"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/internal/telemetry"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/keeper"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types"
|
|
porttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/05-port/types"
|
|
ibcerrors "git.cw.tr/mukan-network/mukan-ibc/modules/core/errors"
|
|
ibcexported "git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
|
|
)
|
|
|
|
var (
|
|
_ porttypes.IBCModule = (*IBCModule)(nil)
|
|
_ porttypes.PacketDataUnmarshaler = (*IBCModule)(nil)
|
|
)
|
|
|
|
// IBCModule implements the ICS26 interface for transfer given the transfer keeper.
|
|
type IBCModule struct {
|
|
keeper keeper.Keeper
|
|
}
|
|
|
|
// NewIBCModule creates a new IBCModule given the keeper
|
|
func NewIBCModule(k keeper.Keeper) IBCModule {
|
|
return IBCModule{
|
|
keeper: k,
|
|
}
|
|
}
|
|
|
|
// ValidateTransferChannelParams does validation of a newly created transfer channel. A transfer
|
|
// channel must be UNORDERED, use the correct port (by default 'transfer'), and use the current
|
|
// supported version. Only 2^32 channels are allowed to be created.
|
|
func ValidateTransferChannelParams(
|
|
ctx sdk.Context,
|
|
transferkeeper keeper.Keeper,
|
|
order channeltypes.Order,
|
|
portID string,
|
|
channelID string,
|
|
) error {
|
|
// NOTE: for escrow address security only 2^32 channels are allowed to be created
|
|
// Issue: https://git.cw.tr/mukan-network/mukan-sdk/issues/7737
|
|
channelSequence, err := channeltypes.ParseChannelSequence(channelID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if channelSequence > uint64(math.MaxUint32) {
|
|
return errorsmod.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, uint64(math.MaxUint32))
|
|
}
|
|
if order != channeltypes.UNORDERED {
|
|
return errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.UNORDERED, order)
|
|
}
|
|
|
|
// Require portID is the portID transfer module is bound to
|
|
boundPort := transferkeeper.GetPort(ctx)
|
|
if boundPort != portID {
|
|
return errorsmod.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnChanOpenInit implements the IBCModule interface
|
|
func (im IBCModule) OnChanOpenInit(
|
|
ctx sdk.Context,
|
|
order channeltypes.Order,
|
|
connectionHops []string,
|
|
portID string,
|
|
channelID string,
|
|
counterparty channeltypes.Counterparty,
|
|
version string,
|
|
) (string, error) {
|
|
if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// default to latest supported version
|
|
if strings.TrimSpace(version) == "" {
|
|
version = types.V1
|
|
}
|
|
|
|
if !slices.Contains(types.SupportedVersions, version) {
|
|
return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected one of %s, got %s", types.SupportedVersions, version)
|
|
}
|
|
|
|
return version, nil
|
|
}
|
|
|
|
// OnChanOpenTry implements the IBCModule interface.
|
|
func (im IBCModule) OnChanOpenTry(
|
|
ctx sdk.Context,
|
|
order channeltypes.Order,
|
|
connectionHops []string,
|
|
portID,
|
|
channelID string,
|
|
counterparty channeltypes.Counterparty,
|
|
counterpartyVersion string,
|
|
) (string, error) {
|
|
if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !slices.Contains(types.SupportedVersions, counterpartyVersion) {
|
|
im.keeper.Logger(ctx).Debug("invalid counterparty version, proposing latest app version", "counterpartyVersion", counterpartyVersion, "version", types.V1)
|
|
return types.V1, nil
|
|
}
|
|
|
|
return counterpartyVersion, nil
|
|
}
|
|
|
|
// OnChanOpenAck implements the IBCModule interface
|
|
func (IBCModule) OnChanOpenAck(
|
|
ctx sdk.Context,
|
|
portID,
|
|
channelID string,
|
|
_ string,
|
|
counterpartyVersion string,
|
|
) error {
|
|
if !slices.Contains(types.SupportedVersions, counterpartyVersion) {
|
|
return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected one of %s, got %s", types.SupportedVersions, counterpartyVersion)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnChanOpenConfirm implements the IBCModule interface
|
|
func (IBCModule) OnChanOpenConfirm(
|
|
ctx sdk.Context,
|
|
portID,
|
|
channelID string,
|
|
) error {
|
|
return nil
|
|
}
|
|
|
|
// OnChanCloseInit implements the IBCModule interface
|
|
func (IBCModule) OnChanCloseInit(
|
|
ctx sdk.Context,
|
|
portID,
|
|
channelID string,
|
|
) error {
|
|
// Disallow user-initiated channel closing for transfer channels
|
|
return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "user cannot close channel")
|
|
}
|
|
|
|
// OnChanCloseConfirm implements the IBCModule interface
|
|
func (IBCModule) OnChanCloseConfirm(
|
|
ctx sdk.Context,
|
|
portID,
|
|
channelID string,
|
|
) error {
|
|
return nil
|
|
}
|
|
|
|
// OnRecvPacket implements the IBCModule interface. A successful acknowledgement
|
|
// is returned if the packet data is successfully decoded and the receive application
|
|
// logic returns without error.
|
|
func (im IBCModule) OnRecvPacket(
|
|
ctx sdk.Context,
|
|
channelVersion string,
|
|
packet channeltypes.Packet,
|
|
relayer sdk.AccAddress,
|
|
) ibcexported.Acknowledgement {
|
|
var (
|
|
ack ibcexported.Acknowledgement
|
|
ackErr error
|
|
data types.InternalTransferRepresentation
|
|
)
|
|
|
|
// we are explicitly wrapping this emit event call in an anonymous function so that
|
|
// the packet data is evaluated after it has been assigned a value.
|
|
defer func() {
|
|
events.EmitOnRecvPacketEvent(ctx, data, ack, ackErr)
|
|
}()
|
|
|
|
data, ackErr = types.UnmarshalPacketData(packet.GetData(), channelVersion, "")
|
|
if ackErr != nil {
|
|
ack = channeltypes.NewErrorAcknowledgement(ackErr)
|
|
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence))
|
|
return ack
|
|
}
|
|
|
|
// NOTE: this needs to set the ackErr variable and not do if ackErr := ... because the ackErr variable is used in the defer function
|
|
ackErr = im.keeper.OnRecvPacket(
|
|
ctx,
|
|
data,
|
|
packet.SourcePort,
|
|
packet.SourceChannel,
|
|
packet.DestinationPort,
|
|
packet.DestinationChannel,
|
|
)
|
|
if ackErr != nil {
|
|
ack = channeltypes.NewErrorAcknowledgement(ackErr)
|
|
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence))
|
|
return ack
|
|
}
|
|
|
|
ack = channeltypes.NewResultAcknowledgement([]byte{byte(1)})
|
|
|
|
telemetry.ReportOnRecvPacket(packet.SourcePort, packet.SourceChannel, packet.DestinationPort, packet.DestinationChannel, data.Token)
|
|
|
|
im.keeper.Logger(ctx).Info("successfully handled ICS-20 packet", "sequence", packet.Sequence)
|
|
|
|
// NOTE: acknowledgement will be written synchronously during IBC handler execution.
|
|
return ack
|
|
}
|
|
|
|
// OnAcknowledgementPacket implements the IBCModule interface
|
|
func (im IBCModule) OnAcknowledgementPacket(
|
|
ctx sdk.Context,
|
|
channelVersion string,
|
|
packet channeltypes.Packet,
|
|
acknowledgement []byte,
|
|
relayer sdk.AccAddress,
|
|
) error {
|
|
var ack channeltypes.Acknowledgement
|
|
if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
|
|
return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err)
|
|
}
|
|
|
|
data, err := types.UnmarshalPacketData(packet.GetData(), channelVersion, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bz := types.ModuleCdc.MustMarshalJSON(&ack)
|
|
if !bytes.Equal(bz, acknowledgement) {
|
|
return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "acknowledgement did not marshal to expected bytes: %X ≠ %X", bz, acknowledgement)
|
|
}
|
|
|
|
if err := im.keeper.OnAcknowledgementPacket(ctx, packet.SourcePort, packet.SourceChannel, data, ack); err != nil {
|
|
return err
|
|
}
|
|
|
|
events.EmitOnAcknowledgementPacketEvent(ctx, data, ack)
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnTimeoutPacket implements the IBCModule interface
|
|
func (im IBCModule) OnTimeoutPacket(
|
|
ctx sdk.Context,
|
|
channelVersion string,
|
|
packet channeltypes.Packet,
|
|
relayer sdk.AccAddress,
|
|
) error {
|
|
data, err := types.UnmarshalPacketData(packet.GetData(), channelVersion, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// refund tokens
|
|
if err := im.keeper.OnTimeoutPacket(ctx, packet.SourcePort, packet.SourceChannel, data); err != nil {
|
|
return err
|
|
}
|
|
|
|
events.EmitOnTimeoutEvent(ctx, data)
|
|
|
|
return nil
|
|
}
|
|
|
|
// UnmarshalPacketData attempts to unmarshal the provided packet data bytes
|
|
// into a FungibleTokenPacketData. This function implements the optional
|
|
// PacketDataUnmarshaler interface required for ADR 008 support.
|
|
func (im IBCModule) UnmarshalPacketData(ctx sdk.Context, portID string, channelID string, bz []byte) (any, string, error) {
|
|
ics20Version, found := im.keeper.GetICS4Wrapper().GetAppVersion(ctx, portID, channelID)
|
|
if !found {
|
|
return types.InternalTransferRepresentation{}, "", errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", portID, channelID)
|
|
}
|
|
|
|
ftpd, err := types.UnmarshalPacketData(bz, ics20Version, "")
|
|
return ftpd, ics20Version, err
|
|
}
|