cosmos-sdk/x/ibc/20-transfer/module.go

348 lines
10 KiB
Go

package transfer
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/capability"
channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel"
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
port "github.com/cosmos/cosmos-sdk/x/ibc/05-port"
porttypes "github.com/cosmos/cosmos-sdk/x/ibc/05-port/types"
"github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/client/cli"
"github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/client/rest"
"github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)
var (
_ module.AppModule = AppModule{}
_ port.IBCModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)
// AppModuleBasic is the 20-transfer appmodulebasic
type AppModuleBasic struct{}
// Name implements AppModuleBasic interface
func (AppModuleBasic) Name() string {
return ModuleName
}
// RegisterCodec implements AppModuleBasic interface
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
RegisterCodec(cdc)
}
// DefaultGenesis returns default genesis state as raw bytes for the ibc
// transfer module.
func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage {
return cdc.MustMarshalJSON(types.DefaultGenesis())
}
// ValidateGenesis performs genesis state validation for the ibc transfer module.
func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, _ json.RawMessage) error {
return nil
}
// RegisterRESTRoutes implements AppModuleBasic interface
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
rest.RegisterRoutes(ctx, rtr)
}
// GetTxCmd implements AppModuleBasic interface
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetTxCmd(cdc)
}
// GetQueryCmd implements AppModuleBasic interface
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetQueryCmd(cdc, QuerierRoute)
}
// AppModule represents the AppModule for this module
type AppModule struct {
AppModuleBasic
keeper Keeper
}
// NewAppModule creates a new 20-transfer module
func NewAppModule(k Keeper) AppModule {
return AppModule{
keeper: k,
}
}
// RegisterInvariants implements the AppModule interface
func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
// TODO
}
// Route implements the AppModule interface
func (AppModule) Route() string {
return RouterKey
}
// NewHandler implements the AppModule interface
func (am AppModule) NewHandler() sdk.Handler {
return NewHandler(am.keeper)
}
// QuerierRoute implements the AppModule interface
func (AppModule) QuerierRoute() string {
return QuerierRoute
}
// NewQuerierHandler implements the AppModule interface
func (am AppModule) NewQuerierHandler() sdk.Querier {
return nil
}
// InitGenesis performs genesis initialization for the ibc transfer module. It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState types.GenesisState
cdc.MustUnmarshalJSON(data, &genesisState)
// TODO: check if the IBC transfer module account is set
InitGenesis(ctx, am.keeper, genesisState)
return []abci.ValidatorUpdate{}
}
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return cdc.MustMarshalJSON(gs)
}
// BeginBlock implements the AppModule interface
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
}
// EndBlock implements the AppModule interface
func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}
// Implement IBCModule callbacks
func (am AppModule) OnChanOpenInit(
ctx sdk.Context,
order channelexported.Order,
connectionHops []string,
portID string,
channelID string,
chanCap *capability.Capability,
counterparty channeltypes.Counterparty,
version string,
) error {
// TODO: Enforce ordering, currently relayers use ORDERED channels
// Require portID is the portID transfer module is bound to
boundPort := am.keeper.GetPort(ctx)
if boundPort != portID {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
}
if version != types.Version {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid version: %s, expected %s", version, "ics20-1")
}
// Claim channel capability passed back by IBC module
if err := am.keeper.ClaimCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)); err != nil {
return sdkerrors.Wrap(channel.ErrChannelCapabilityNotFound, err.Error())
}
// TODO: escrow
return nil
}
func (am AppModule) OnChanOpenTry(
ctx sdk.Context,
order channelexported.Order,
connectionHops []string,
portID,
channelID string,
chanCap *capability.Capability,
counterparty channeltypes.Counterparty,
version,
counterpartyVersion string,
) error {
// TODO: Enforce ordering, currently relayers use ORDERED channels
// Require portID is the portID transfer module is bound to
boundPort := am.keeper.GetPort(ctx)
if boundPort != portID {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
}
if version != types.Version {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid version: %s, expected %s", version, "ics20-1")
}
if counterpartyVersion != types.Version {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid counterparty version: %s, expected %s", counterpartyVersion, "ics20-1")
}
// Claim channel capability passed back by IBC module
if err := am.keeper.ClaimCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)); err != nil {
return sdkerrors.Wrap(channel.ErrChannelCapabilityNotFound, err.Error())
}
// TODO: escrow
return nil
}
func (am AppModule) OnChanOpenAck(
ctx sdk.Context,
portID,
channelID string,
counterpartyVersion string,
) error {
if counterpartyVersion != types.Version {
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid counterparty version: %s, expected %s", counterpartyVersion, "ics20-1")
}
return nil
}
func (am AppModule) OnChanOpenConfirm(
ctx sdk.Context,
portID,
channelID string,
) error {
return nil
}
func (am AppModule) OnChanCloseInit(
ctx sdk.Context,
portID,
channelID string,
) error {
// Disallow user-initiated channel closing for transfer channels
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel")
}
func (am AppModule) OnChanCloseConfirm(
ctx sdk.Context,
portID,
channelID string,
) error {
return nil
}
func (am AppModule) OnRecvPacket(
ctx sdk.Context,
packet channeltypes.Packet,
) (*sdk.Result, error) {
var data FungibleTokenPacketData
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
}
acknowledgement := FungibleTokenPacketAcknowledgement{
Success: true,
Error: "",
}
if err := am.keeper.OnRecvPacket(ctx, packet, data); err != nil {
acknowledgement = FungibleTokenPacketAcknowledgement{
Success: false,
Error: err.Error(),
}
}
if err := am.keeper.PacketExecuted(ctx, packet, acknowledgement.GetBytes()); err != nil {
return nil, err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypePacket,
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
sdk.NewAttribute(AttributeKeyReceiver, data.Receiver),
sdk.NewAttribute(AttributeKeyValue, data.Amount.String()),
),
)
return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, nil
}
func (am AppModule) OnAcknowledgementPacket(
ctx sdk.Context,
packet channeltypes.Packet,
acknowledgement []byte,
) (*sdk.Result, error) {
var ack FungibleTokenPacketAcknowledgement
if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err)
}
var data FungibleTokenPacketData
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
}
if err := am.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil {
return nil, err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypePacket,
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
sdk.NewAttribute(AttributeKeyReceiver, data.Receiver),
sdk.NewAttribute(AttributeKeyValue, data.Amount.String()),
sdk.NewAttribute(AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success)),
),
)
if !ack.Success {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypePacket,
sdk.NewAttribute(AttributeKeyAckError, ack.Error),
),
)
}
return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, nil
}
func (am AppModule) OnTimeoutPacket(
ctx sdk.Context,
packet channeltypes.Packet,
) (*sdk.Result, error) {
var data FungibleTokenPacketData
if err := types.ModuleCdc.UnmarshalBinaryBare(packet.GetData(), &data); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
}
// refund tokens
if err := am.keeper.OnTimeoutPacket(ctx, packet, data); err != nil {
return nil, err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypeTimeout,
sdk.NewAttribute(AttributeKeyRefundReceiver, data.Sender),
sdk.NewAttribute(AttributeKeyRefundValue, data.Amount.String()),
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
),
)
return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, nil
}