github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/04-channel/keeper/packet.go (about) 1 package keeper 2 3 import ( 4 "bytes" 5 "time" 6 7 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 8 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 9 capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types" 10 clienttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types" 11 connectiontypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/03-connection/types" 12 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 13 host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host" 14 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 15 ) 16 17 // SendPacket is called by a module in order to send an IBC packet on a channel 18 // end owned by the calling module to the corresponding module on the counterparty 19 // chain. 20 func (k Keeper) SendPacket( 21 ctx sdk.Context, 22 channelCap *capabilitytypes.Capability, 23 packet exported.PacketI, 24 ) error { 25 if err := packet.ValidateBasic(); err != nil { 26 return sdkerrors.Wrap(err, "packet failed basic validation") 27 } 28 29 channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 30 if !found { 31 return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) 32 } 33 34 if channel.State == types.CLOSED { 35 return sdkerrors.Wrapf( 36 types.ErrInvalidChannelState, 37 "channel is CLOSED (got %s)", channel.State.String(), 38 ) 39 } 40 41 if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())) { 42 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) 43 } 44 45 if packet.GetDestPort() != channel.Counterparty.PortId { 46 return sdkerrors.Wrapf( 47 types.ErrInvalidPacket, 48 "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, 49 ) 50 } 51 52 if packet.GetDestChannel() != channel.Counterparty.ChannelId { 53 return sdkerrors.Wrapf( 54 types.ErrInvalidPacket, 55 "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, 56 ) 57 } 58 59 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 60 if !found { 61 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 62 } 63 64 clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID()) 65 if !found { 66 return clienttypes.ErrConsensusStateNotFound 67 } 68 69 // prevent accidental sends with clients that cannot be updated 70 clientStore := k.clientKeeper.ClientStore(ctx, connectionEnd.GetClientID()) 71 if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { 72 return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) 73 } 74 75 // check if packet is timed out on the receiving chain 76 latestHeight := clientState.GetLatestHeight() 77 timeoutHeight := packet.GetTimeoutHeight() 78 if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) { 79 return sdkerrors.Wrapf( 80 types.ErrPacketTimeout, 81 "receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight, 82 ) 83 } 84 85 clientType, _, err := clienttypes.ParseClientIdentifier(connectionEnd.GetClientID()) 86 if err != nil { 87 return err 88 } 89 90 // NOTE: this is a temporary fix. Solo machine does not support usage of 'GetTimestampAtHeight' 91 // A future change should move this function to be a ClientState callback. 92 if clientType != exported.Solomachine { 93 latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) 94 if err != nil { 95 return err 96 } 97 98 if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { 99 return sdkerrors.Wrapf( 100 types.ErrPacketTimeout, 101 "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), 102 ) 103 } 104 } 105 106 nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 107 if !found { 108 return sdkerrors.Wrapf( 109 types.ErrSequenceSendNotFound, 110 "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), 111 ) 112 } 113 114 if packet.GetSequence() != nextSequenceSend { 115 return sdkerrors.Wrapf( 116 types.ErrInvalidPacket, 117 "packet sequence ≠ next send sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceSend, 118 ) 119 } 120 121 commitment := types.CommitPacket(k.cdc, packet) 122 123 nextSequenceSend++ 124 k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend) 125 k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment) 126 127 EmitSendPacketEvent(ctx, packet, channel, timeoutHeight) 128 129 k.Logger(ctx).Info( 130 "packet sent", 131 "sequence", packet.GetSequence(), 132 "src_port", packet.GetSourcePort(), 133 "src_channel", packet.GetSourceChannel(), 134 "dst_port", packet.GetDestPort(), 135 "dst_channel", packet.GetDestChannel(), 136 ) 137 138 return nil 139 } 140 141 // RecvPacket is called by a module in order to receive & process an IBC packet 142 // sent on the corresponding channel end on the counterparty chain. 143 func (k Keeper) RecvPacket( 144 ctx sdk.Context, 145 chanCap *capabilitytypes.Capability, 146 packet exported.PacketI, 147 proof []byte, 148 proofHeight exported.Height, 149 ) error { 150 channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) 151 if !found { 152 return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) 153 } 154 155 if channel.State != types.OPEN { 156 return sdkerrors.Wrapf( 157 types.ErrInvalidChannelState, 158 "channel state is not OPEN (got %s)", channel.State.String(), 159 ) 160 } 161 162 // Authenticate capability to ensure caller has authority to receive packet on this channel 163 capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) 164 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { 165 return sdkerrors.Wrapf( 166 types.ErrInvalidChannelCapability, 167 "channel capability failed authentication for capability name %s", capName, 168 ) 169 } 170 171 // packet must come from the channel's counterparty 172 if packet.GetSourcePort() != channel.Counterparty.PortId { 173 return sdkerrors.Wrapf( 174 types.ErrInvalidPacket, 175 "packet source port doesn't match the counterparty's port (%s ≠ %s)", packet.GetSourcePort(), channel.Counterparty.PortId, 176 ) 177 } 178 179 if packet.GetSourceChannel() != channel.Counterparty.ChannelId { 180 return sdkerrors.Wrapf( 181 types.ErrInvalidPacket, 182 "packet source channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetSourceChannel(), channel.Counterparty.ChannelId, 183 ) 184 } 185 186 // Connection must be OPEN to receive a packet. It is possible for connection to not yet be open if packet was 187 // sent optimistically before connection and channel handshake completed. However, to receive a packet, 188 // connection and channel must both be open 189 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 190 if !found { 191 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 192 } 193 194 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 195 return sdkerrors.Wrapf( 196 connectiontypes.ErrInvalidConnectionState, 197 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 198 ) 199 } 200 201 // check if packet timeouted by comparing it with the latest height of the chain 202 selfHeight := clienttypes.GetSelfHeight(ctx) 203 timeoutHeight := packet.GetTimeoutHeight() 204 if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) { 205 return sdkerrors.Wrapf( 206 types.ErrPacketTimeout, 207 "block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight, 208 ) 209 } 210 211 // check if packet timeouted by comparing it with the latest timestamp of the chain 212 if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() { 213 return sdkerrors.Wrapf( 214 types.ErrPacketTimeout, 215 "block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())), 216 ) 217 } 218 219 commitment := types.CommitPacket(k.cdc, packet) 220 221 // verify that the counterparty did commit to sending this packet 222 if err := k.connectionKeeper.VerifyPacketCommitment( 223 ctx, connectionEnd, proofHeight, proof, 224 packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), 225 commitment, 226 ); err != nil { 227 return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") 228 } 229 230 switch channel.Ordering { 231 case types.UNORDERED: 232 // check if the packet receipt has been received already for unordered channels 233 _, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) 234 if found { 235 EmitRecvPacketEvent(ctx, packet, channel) 236 // This error indicates that the packet has already been relayed. Core IBC will 237 // treat this error as a no-op in order to prevent an entire relay transaction 238 // from failing and consuming unnecessary fees. 239 return types.ErrNoOpMsg 240 } 241 242 // All verification complete, update state 243 // For unordered channels we must set the receipt so it can be verified on the other side. 244 // This receipt does not contain any data, since the packet has not yet been processed, 245 // it's just a single store key set to an empty string to indicate that the packet has been received 246 k.SetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) 247 248 case types.ORDERED: 249 // check if the packet is being received in order 250 nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) 251 if !found { 252 return sdkerrors.Wrapf( 253 types.ErrSequenceReceiveNotFound, 254 "destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(), 255 ) 256 } 257 258 if packet.GetSequence() < nextSequenceRecv { 259 EmitRecvPacketEvent(ctx, packet, channel) 260 // This error indicates that the packet has already been relayed. Core IBC will 261 // treat this error as a no-op in order to prevent an entire relay transaction 262 // from failing and consuming unnecessary fees. 263 return types.ErrNoOpMsg 264 } 265 266 if packet.GetSequence() != nextSequenceRecv { 267 return sdkerrors.Wrapf( 268 types.ErrPacketSequenceOutOfOrder, 269 "packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv, 270 ) 271 } 272 273 // All verification complete, update state 274 // In ordered case, we must increment nextSequenceRecv 275 nextSequenceRecv++ 276 277 // incrementing nextSequenceRecv and storing under this chain's channelEnd identifiers 278 // Since this is the receiving chain, our channelEnd is packet's destination port and channel 279 k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv) 280 281 } 282 283 // log that a packet has been received & executed 284 k.Logger(ctx).Info( 285 "packet received", 286 "sequence", packet.GetSequence(), 287 "src_port", packet.GetSourcePort(), 288 "src_channel", packet.GetSourceChannel(), 289 "dst_port", packet.GetDestPort(), 290 "dst_channel", packet.GetDestChannel(), 291 ) 292 293 // emit an event that the relayer can query for 294 EmitRecvPacketEvent(ctx, packet, channel) 295 296 return nil 297 } 298 299 // WriteAcknowledgement writes the packet execution acknowledgement to the state, 300 // which will be verified by the counterparty chain using AcknowledgePacket. 301 // 302 // CONTRACT: 303 // 304 // 1) For synchronous execution, this function is be called in the IBC handler . 305 // For async handling, it needs to be called directly by the module which originally 306 // processed the packet. 307 // 308 // 2) Assumes that packet receipt has been written (unordered), or nextSeqRecv was incremented (ordered) 309 // previously by RecvPacket. 310 func (k Keeper) WriteAcknowledgement( 311 ctx sdk.Context, 312 chanCap *capabilitytypes.Capability, 313 packet exported.PacketI, 314 acknowledgement exported.Acknowledgement, 315 ) error { 316 channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) 317 if !found { 318 return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) 319 } 320 321 if channel.State != types.OPEN { 322 return sdkerrors.Wrapf( 323 types.ErrInvalidChannelState, 324 "channel state is not OPEN (got %s)", channel.State.String(), 325 ) 326 } 327 328 // Authenticate capability to ensure caller has authority to receive packet on this channel 329 capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) 330 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { 331 return sdkerrors.Wrapf( 332 types.ErrInvalidChannelCapability, 333 "channel capability failed authentication for capability name %s", capName, 334 ) 335 } 336 337 // NOTE: IBC app modules might have written the acknowledgement synchronously on 338 // the OnRecvPacket callback so we need to check if the acknowledgement is already 339 // set on the store and return an error if so. 340 if k.HasPacketAcknowledgement(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) { 341 return types.ErrAcknowledgementExists 342 } 343 344 if acknowledgement == nil { 345 return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be nil") 346 } 347 348 bz := acknowledgement.Acknowledgement() 349 if len(bz) == 0 { 350 return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty") 351 } 352 353 // set the acknowledgement so that it can be verified on the other side 354 k.SetPacketAcknowledgement( 355 ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), 356 types.CommitAcknowledgement(bz), 357 ) 358 359 // log that a packet acknowledgement has been written 360 k.Logger(ctx).Info( 361 "acknowledgement written", 362 "sequence", packet.GetSequence(), 363 "src_port", packet.GetSourcePort(), 364 "src_channel", packet.GetSourceChannel(), 365 "dst_port", packet.GetDestPort(), 366 "dst_channel", packet.GetDestChannel(), 367 ) 368 369 EmitWriteAcknowledgementEvent(ctx, packet, channel, bz) 370 371 return nil 372 } 373 374 // AcknowledgePacket is called by a module to process the acknowledgement of a 375 // packet previously sent by the calling module on a channel to a counterparty 376 // module on the counterparty chain. Its intended usage is within the ante 377 // handler. AcknowledgePacket will clean up the packet commitment, 378 // which is no longer necessary since the packet has been received and acted upon. 379 // It will also increment NextSequenceAck in case of ORDERED channels. 380 func (k Keeper) AcknowledgePacket( 381 ctx sdk.Context, 382 chanCap *capabilitytypes.Capability, 383 packet exported.PacketI, 384 acknowledgement []byte, 385 proof []byte, 386 proofHeight exported.Height, 387 ) error { 388 channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 389 if !found { 390 return sdkerrors.Wrapf( 391 types.ErrChannelNotFound, 392 "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), 393 ) 394 } 395 396 if channel.State != types.OPEN { 397 return sdkerrors.Wrapf( 398 types.ErrInvalidChannelState, 399 "channel state is not OPEN (got %s)", channel.State.String(), 400 ) 401 } 402 403 // Authenticate capability to ensure caller has authority to receive packet on this channel 404 capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) 405 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { 406 return sdkerrors.Wrapf( 407 types.ErrInvalidChannelCapability, 408 "channel capability failed authentication for capability name %s", capName, 409 ) 410 } 411 412 // packet must have been sent to the channel's counterparty 413 if packet.GetDestPort() != channel.Counterparty.PortId { 414 return sdkerrors.Wrapf( 415 types.ErrInvalidPacket, 416 "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, 417 ) 418 } 419 420 if packet.GetDestChannel() != channel.Counterparty.ChannelId { 421 return sdkerrors.Wrapf( 422 types.ErrInvalidPacket, 423 "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, 424 ) 425 } 426 427 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 428 if !found { 429 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 430 } 431 432 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 433 return sdkerrors.Wrapf( 434 connectiontypes.ErrInvalidConnectionState, 435 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 436 ) 437 } 438 439 commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) 440 441 if len(commitment) == 0 { 442 EmitAcknowledgePacketEvent(ctx, packet, channel) 443 // This error indicates that the acknowledgement has already been relayed 444 // or there is a misconfigured relayer attempting to prove an acknowledgement 445 // for a packet never sent. Core IBC will treat this error as a no-op in order to 446 // prevent an entire relay transaction from failing and consuming unnecessary fees. 447 return types.ErrNoOpMsg 448 } 449 450 packetCommitment := types.CommitPacket(k.cdc, packet) 451 452 // verify we sent the packet and haven't cleared it out yet 453 if !bytes.Equal(commitment, packetCommitment) { 454 return sdkerrors.Wrapf(types.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment) 455 } 456 457 if err := k.connectionKeeper.VerifyPacketAcknowledgement( 458 ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(), 459 packet.GetSequence(), acknowledgement, 460 ); err != nil { 461 return err 462 } 463 464 // assert packets acknowledged in order 465 if channel.Ordering == types.ORDERED { 466 nextSequenceAck, found := k.GetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 467 if !found { 468 return sdkerrors.Wrapf( 469 types.ErrSequenceAckNotFound, 470 "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), 471 ) 472 } 473 474 if packet.GetSequence() != nextSequenceAck { 475 return sdkerrors.Wrapf( 476 types.ErrPacketSequenceOutOfOrder, 477 "packet sequence ≠ next ack sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceAck, 478 ) 479 } 480 481 // All verification complete, in the case of ORDERED channels we must increment nextSequenceAck 482 nextSequenceAck++ 483 484 // incrementing NextSequenceAck and storing under this chain's channelEnd identifiers 485 // Since this is the original sending chain, our channelEnd is packet's source port and channel 486 k.SetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceAck) 487 488 } 489 490 // Delete packet commitment, since the packet has been acknowledged, the commitement is no longer necessary 491 k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) 492 493 // log that a packet has been acknowledged 494 k.Logger(ctx).Info( 495 "packet acknowledged", 496 "sequence", packet.GetSequence(), 497 "src_port", packet.GetSourcePort(), 498 "src_channel", packet.GetSourceChannel(), 499 "dst_port", packet.GetDestPort(), 500 "dst_channel", packet.GetDestChannel(), 501 ) 502 503 // emit an event marking that we have processed the acknowledgement 504 EmitAcknowledgePacketEvent(ctx, packet, channel) 505 506 return nil 507 }