github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/ibc.go (about)

     1  package wasm
     2  
     3  import (
     4  	"math"
     5  
     6  	ibcexported "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported"
     7  
     8  	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
     9  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    10  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    11  	capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types"
    12  	channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    13  	porttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/05-port/types"
    14  	host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host"
    15  
    16  	types "github.com/fibonacci-chain/fbc/x/wasm/types"
    17  )
    18  
    19  var _ porttypes.IBCModule = IBCHandler{}
    20  
    21  type IBCHandler struct {
    22  	keeper        types.IBCContractKeeper
    23  	channelKeeper types.ChannelKeeper
    24  }
    25  
    26  func (i IBCHandler) OnChanOpenTry(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version, counterpartyVersion string) (string, error) {
    27  	panic("implement me")
    28  }
    29  
    30  func (i IBCHandler) OnChanOpenAck(ctx sdk.Context, portID, channelID string, string, counterpartyVersion string) error {
    31  	panic("implement me")
    32  }
    33  
    34  func NewIBCHandler(k types.IBCContractKeeper, ck types.ChannelKeeper) IBCHandler {
    35  	return IBCHandler{keeper: k, channelKeeper: ck}
    36  }
    37  
    38  // OnChanOpenInit implements the IBCModule interface
    39  func (i IBCHandler) OnChanOpenInit(
    40  	ctx sdk.Context,
    41  	order channeltypes.Order,
    42  	connectionHops []string,
    43  	portID string,
    44  	channelID string,
    45  	chanCap *capabilitytypes.Capability,
    46  	counterParty channeltypes.Counterparty,
    47  	version string,
    48  ) (string, error) {
    49  	// ensure port, version, capability
    50  	if err := ValidateChannelParams(channelID); err != nil {
    51  		return "", err
    52  	}
    53  	contractAddr, err := ContractFromPortID(portID)
    54  	if err != nil {
    55  		return "", sdkerrors.Wrapf(err, "contract port id")
    56  	}
    57  
    58  	msg := wasmvmtypes.IBCChannelOpenMsg{
    59  		OpenInit: &wasmvmtypes.IBCOpenInit{
    60  			Channel: wasmvmtypes.IBCChannel{
    61  				Endpoint:             wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
    62  				CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: counterParty.PortId, ChannelID: counterParty.ChannelId},
    63  				Order:                order.String(),
    64  				// DESIGN V3: this may be "" ??
    65  				Version:      version,
    66  				ConnectionID: connectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
    67  			},
    68  		},
    69  	}
    70  	_, err = i.keeper.OnOpenChannel(ctx, contractAddr, msg)
    71  	if err != nil {
    72  		return "", err
    73  	}
    74  	// Claim channel capability passed back by IBC module
    75  	if err := i.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
    76  		return "", sdkerrors.Wrap(err, "claim capability")
    77  	}
    78  	return version, nil
    79  }
    80  
    81  // OnChanOpenTry implements the IBCModule interface
    82  func (i IBCHandler) OnChanOpenTryV3(
    83  	ctx sdk.Context,
    84  	order channeltypes.Order,
    85  	connectionHops []string,
    86  	portID, channelID string,
    87  	chanCap *capabilitytypes.Capability,
    88  	counterParty channeltypes.Counterparty,
    89  	counterpartyVersion string,
    90  ) (string, error) {
    91  	// ensure port, version, capability
    92  	if err := ValidateChannelParams(channelID); err != nil {
    93  		return "", err
    94  	}
    95  
    96  	contractAddr, err := ContractFromPortID(portID)
    97  	if err != nil {
    98  		return "", sdkerrors.Wrapf(err, "contract port id")
    99  	}
   100  
   101  	msg := wasmvmtypes.IBCChannelOpenMsg{
   102  		OpenTry: &wasmvmtypes.IBCOpenTry{
   103  			Channel: wasmvmtypes.IBCChannel{
   104  				Endpoint:             wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
   105  				CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: counterParty.PortId, ChannelID: counterParty.ChannelId},
   106  				Order:                order.String(),
   107  				Version:              counterpartyVersion,
   108  				ConnectionID:         connectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
   109  			},
   110  			CounterpartyVersion: counterpartyVersion,
   111  		},
   112  	}
   113  
   114  	// Allow contracts to return a version (or default to counterpartyVersion if unset)
   115  	version, err := i.keeper.OnOpenChannel(ctx, contractAddr, msg)
   116  	if err != nil {
   117  		return "", err
   118  	}
   119  	if version == "" {
   120  		version = counterpartyVersion
   121  	}
   122  
   123  	// Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos
   124  	// (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry)
   125  	// If module can already authenticate the capability then module already owns it so we don't need to claim
   126  	// Otherwise, module does not have channel capability and we must claim it from IBC
   127  	if !i.keeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) {
   128  		// Only claim channel capability passed back by IBC module if we do not already own it
   129  		if err := i.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
   130  			return "", sdkerrors.Wrap(err, "claim capability")
   131  		}
   132  	}
   133  
   134  	return version, nil
   135  }
   136  
   137  // OnChanOpenAck implements the IBCModule interface
   138  func (i IBCHandler) OnChanOpenAckV3(
   139  	ctx sdk.Context,
   140  	portID, channelID string,
   141  	counterpartyChannelID string,
   142  	counterpartyVersion string,
   143  ) error {
   144  	contractAddr, err := ContractFromPortID(portID)
   145  	if err != nil {
   146  		return sdkerrors.Wrapf(err, "contract port id")
   147  	}
   148  	channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
   149  	if !ok {
   150  		return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
   151  	}
   152  	channelInfo.Counterparty.ChannelId = counterpartyChannelID
   153  	// This is a bit ugly, but it is set AFTER the callback is done, yet we want to provide the contract
   154  	// access to the channel in queries. We can revisit how to better integrate with ibc-go in the future,
   155  	// but this is the best/safest we can do now. (If you remove this, you error when sending a packet during the
   156  	// OnChanOpenAck entry point)
   157  	// https://github.com/cosmos/ibc-go/pull/647/files#diff-54b5be375a2333c56f2ae1b5b4dc13ac9c734561e30286505f39837ee75762c7R25
   158  	i.channelKeeper.SetChannel(ctx, portID, channelID, channelInfo)
   159  	msg := wasmvmtypes.IBCChannelConnectMsg{
   160  		OpenAck: &wasmvmtypes.IBCOpenAck{
   161  			Channel:             toWasmVMChannel(portID, channelID, channelInfo),
   162  			CounterpartyVersion: counterpartyVersion,
   163  		},
   164  	}
   165  	return i.keeper.OnConnectChannel(ctx, contractAddr, msg)
   166  }
   167  
   168  // OnChanOpenConfirm implements the IBCModule interface
   169  func (i IBCHandler) OnChanOpenConfirm(ctx sdk.Context, portID, channelID string) error {
   170  	contractAddr, err := ContractFromPortID(portID)
   171  	if err != nil {
   172  		return sdkerrors.Wrapf(err, "contract port id")
   173  	}
   174  	channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
   175  	if !ok {
   176  		return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
   177  	}
   178  	msg := wasmvmtypes.IBCChannelConnectMsg{
   179  		OpenConfirm: &wasmvmtypes.IBCOpenConfirm{
   180  			Channel: toWasmVMChannel(portID, channelID, channelInfo),
   181  		},
   182  	}
   183  	return i.keeper.OnConnectChannel(ctx, contractAddr, msg)
   184  }
   185  
   186  // OnChanCloseInit implements the IBCModule interface
   187  func (i IBCHandler) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
   188  	contractAddr, err := ContractFromPortID(portID)
   189  	if err != nil {
   190  		return sdkerrors.Wrapf(err, "contract port id")
   191  	}
   192  	channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
   193  	if !ok {
   194  		return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
   195  	}
   196  
   197  	msg := wasmvmtypes.IBCChannelCloseMsg{
   198  		CloseInit: &wasmvmtypes.IBCCloseInit{Channel: toWasmVMChannel(portID, channelID, channelInfo)},
   199  	}
   200  	err = i.keeper.OnCloseChannel(ctx, contractAddr, msg)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	// emit events?
   205  
   206  	return err
   207  }
   208  
   209  // OnChanCloseConfirm implements the IBCModule interface
   210  func (i IBCHandler) OnChanCloseConfirm(ctx sdk.Context, portID, channelID string) error {
   211  	// counterparty has closed the channel
   212  	contractAddr, err := ContractFromPortID(portID)
   213  	if err != nil {
   214  		return sdkerrors.Wrapf(err, "contract port id")
   215  	}
   216  	channelInfo, ok := i.channelKeeper.GetChannel(ctx, portID, channelID)
   217  	if !ok {
   218  		return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
   219  	}
   220  
   221  	msg := wasmvmtypes.IBCChannelCloseMsg{
   222  		CloseConfirm: &wasmvmtypes.IBCCloseConfirm{Channel: toWasmVMChannel(portID, channelID, channelInfo)},
   223  	}
   224  	err = i.keeper.OnCloseChannel(ctx, contractAddr, msg)
   225  	if err != nil {
   226  		return err
   227  	}
   228  	// emit events?
   229  
   230  	return err
   231  }
   232  
   233  func toWasmVMChannel(portID, channelID string, channelInfo channeltypes.Channel) wasmvmtypes.IBCChannel {
   234  	return wasmvmtypes.IBCChannel{
   235  		Endpoint:             wasmvmtypes.IBCEndpoint{PortID: portID, ChannelID: channelID},
   236  		CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{PortID: channelInfo.Counterparty.PortId, ChannelID: channelInfo.Counterparty.ChannelId},
   237  		Order:                channelInfo.Ordering.String(),
   238  		Version:              channelInfo.Version,
   239  		ConnectionID:         channelInfo.ConnectionHops[0], // At the moment this list must be of length 1. In the future multi-hop channels may be supported.
   240  	}
   241  }
   242  
   243  // OnRecvPacket implements the IBCModule interface
   244  func (i IBCHandler) OnRecvPacket(
   245  	ctx sdk.Context,
   246  	packet channeltypes.Packet,
   247  	relayer sdk.AccAddress,
   248  ) ibcexported.Acknowledgement {
   249  	contractAddr, err := ContractFromPortID(packet.DestinationPort)
   250  	if err != nil {
   251  		return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(err, "contract port id").Error())
   252  	}
   253  	msg := wasmvmtypes.IBCPacketReceiveMsg{Packet: newIBCPacket(packet), Relayer: relayer.String()}
   254  	ack, err := i.keeper.OnRecvPacket(ctx, contractAddr, msg)
   255  	if err != nil {
   256  		return channeltypes.NewErrorAcknowledgement(err.Error())
   257  	}
   258  	return ContractConfirmStateAck(ack)
   259  }
   260  
   261  var _ ibcexported.Acknowledgement = ContractConfirmStateAck{}
   262  
   263  type ContractConfirmStateAck []byte
   264  
   265  func (w ContractConfirmStateAck) Success() bool {
   266  	return true // always commit state
   267  }
   268  
   269  func (w ContractConfirmStateAck) Acknowledgement() []byte {
   270  	return w
   271  }
   272  
   273  // OnAcknowledgementPacket implements the IBCModule interface
   274  func (i IBCHandler) OnAcknowledgementPacket(
   275  	ctx sdk.Context,
   276  	packet channeltypes.Packet,
   277  	acknowledgement []byte,
   278  	relayer sdk.AccAddress,
   279  ) error {
   280  	contractAddr, err := ContractFromPortID(packet.SourcePort)
   281  	if err != nil {
   282  		return sdkerrors.Wrapf(err, "contract port id")
   283  	}
   284  
   285  	err = i.keeper.OnAckPacket(ctx, contractAddr, wasmvmtypes.IBCPacketAckMsg{
   286  		Acknowledgement: wasmvmtypes.IBCAcknowledgement{Data: acknowledgement},
   287  		OriginalPacket:  newIBCPacket(packet),
   288  		Relayer:         relayer.String(),
   289  	})
   290  	if err != nil {
   291  		return sdkerrors.Wrap(err, "on ack")
   292  	}
   293  	return nil
   294  }
   295  
   296  // OnTimeoutPacket implements the IBCModule interface
   297  func (i IBCHandler) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error {
   298  	contractAddr, err := ContractFromPortID(packet.SourcePort)
   299  	if err != nil {
   300  		return sdkerrors.Wrapf(err, "contract port id")
   301  	}
   302  	msg := wasmvmtypes.IBCPacketTimeoutMsg{Packet: newIBCPacket(packet), Relayer: relayer.String()}
   303  	err = i.keeper.OnTimeoutPacket(ctx, contractAddr, msg)
   304  	if err != nil {
   305  		return sdkerrors.Wrap(err, "on timeout")
   306  	}
   307  	return nil
   308  }
   309  
   310  func (i IBCHandler) NegotiateAppVersion(
   311  	ctx sdk.Context,
   312  	order channeltypes.Order,
   313  	connectionID string,
   314  	portID string,
   315  	counterparty channeltypes.Counterparty,
   316  	proposedVersion string,
   317  ) (version string, err error) {
   318  	return proposedVersion, nil // accept all
   319  }
   320  
   321  func newIBCPacket(packet channeltypes.Packet) wasmvmtypes.IBCPacket {
   322  	timeout := wasmvmtypes.IBCTimeout{
   323  		Timestamp: packet.TimeoutTimestamp,
   324  	}
   325  	if !packet.TimeoutHeight.IsZero() {
   326  		timeout.Block = &wasmvmtypes.IBCTimeoutBlock{
   327  			Height:   packet.TimeoutHeight.RevisionHeight,
   328  			Revision: packet.TimeoutHeight.RevisionNumber,
   329  		}
   330  	}
   331  
   332  	return wasmvmtypes.IBCPacket{
   333  		Data:     packet.Data,
   334  		Src:      wasmvmtypes.IBCEndpoint{ChannelID: packet.SourceChannel, PortID: packet.SourcePort},
   335  		Dest:     wasmvmtypes.IBCEndpoint{ChannelID: packet.DestinationChannel, PortID: packet.DestinationPort},
   336  		Sequence: packet.Sequence,
   337  		Timeout:  timeout,
   338  	}
   339  }
   340  
   341  func ValidateChannelParams(channelID string) error {
   342  	// NOTE: for escrow address security only 2^32 channels are allowed to be created
   343  	// Issue: https://github.com/fibonacci-chain/fbc/libs/cosmos-sdk/issues/7737
   344  	channelSequence, err := channeltypes.ParseChannelSequence(channelID)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if channelSequence > math.MaxUint32 {
   349  		return sdkerrors.Wrapf(types.ErrMaxIBCChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, math.MaxUint32)
   350  	}
   351  	return nil
   352  }