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 }