github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/04-channel/keeper/handshake.go (about) 1 package keeper 2 3 import ( 4 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 5 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 6 capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types" 7 connectiontypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/03-connection/types" 8 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 9 porttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/05-port/types" 10 host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host" 11 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 12 ) 13 14 // CounterpartyHops returns the connection hops of the counterparty channel. 15 // The counterparty hops are stored in the inverse order as the channel's. 16 // NOTE: Since connectionHops only supports single connection channels for now, 17 // this function requires that connection hops only contain a single connection id 18 func (k Keeper) CounterpartyHops(ctx sdk.Context, ch types.Channel) ([]string, bool) { 19 // Return empty array if connection hops is more than one 20 // ConnectionHops length should be verified earlier 21 if len(ch.ConnectionHops) != 1 { 22 return []string{}, false 23 } 24 counterpartyHops := make([]string, 1) 25 hop := ch.ConnectionHops[0] 26 conn, found := k.connectionKeeper.GetConnection(ctx, hop) 27 if !found { 28 return []string{}, false 29 } 30 31 counterpartyHops[0] = conn.GetCounterparty().GetConnectionID() 32 return counterpartyHops, true 33 } 34 35 // ChanOpenInit is called by a module to initiate a channel opening handshake with 36 // a module on another chain. The counterparty channel identifier is validated to be 37 // empty in msg validation. 38 func (k Keeper) ChanOpenInit( 39 ctx sdk.Context, 40 order types.Order, 41 connectionHops []string, 42 portID string, 43 portCap *capabilitytypes.Capability, 44 counterparty types.Counterparty, 45 version string, 46 ) (string, *capabilitytypes.Capability, error) { 47 // connection hop length checked on msg.ValidateBasic() 48 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) 49 if !found { 50 return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) 51 } 52 53 getVersions := connectionEnd.GetVersions() 54 if len(getVersions) != 1 { 55 return "", nil, sdkerrors.Wrapf( 56 connectiontypes.ErrInvalidVersion, 57 "single version must be negotiated on connection before opening channel, got: %v", 58 getVersions, 59 ) 60 } 61 62 if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { 63 return "", nil, sdkerrors.Wrapf( 64 connectiontypes.ErrInvalidVersion, 65 "connection version %s does not support channel ordering: %s", 66 getVersions[0], order.String(), 67 ) 68 } 69 70 if !k.portKeeper.Authenticate(ctx, portCap, portID) { 71 return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) 72 } 73 74 channelID := k.GenerateChannelIdentifier(ctx) 75 channel := types.NewChannel(types.INIT, order, counterparty, connectionHops, version) 76 k.SetChannel(ctx, portID, channelID, channel) 77 78 capKey, err := k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) 79 if err != nil { 80 return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) 81 } 82 83 k.SetNextSequenceSend(ctx, portID, channelID, 1) 84 k.SetNextSequenceRecv(ctx, portID, channelID, 1) 85 k.SetNextSequenceAck(ctx, portID, channelID, 1) 86 87 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "NONE", "new-state", "INIT") 88 89 ctx.EventManager().EmitEvents(sdk.Events{ 90 sdk.NewEvent( 91 types.EventTypeChannelOpenInit, 92 sdk.NewAttribute(types.AttributeKeyPortID, portID), 93 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 94 sdk.NewAttribute(types.AttributeCounterpartyPortID, counterparty.PortId), 95 sdk.NewAttribute(types.AttributeCounterpartyChannelID, counterparty.ChannelId), 96 sdk.NewAttribute(types.AttributeKeyConnectionID, connectionHops[0]), 97 ), 98 }) 99 100 return channelID, capKey, nil 101 } 102 103 // ChanOpenTry is called by a module to accept the first step of a channel opening 104 // handshake initiated by a module on another chain. 105 func (k Keeper) ChanOpenTryV2( 106 ctx sdk.Context, 107 order types.Order, 108 connectionHops []string, 109 portID, 110 previousChannelID string, 111 portCap *capabilitytypes.Capability, 112 counterparty types.Counterparty, 113 version, 114 counterpartyVersion string, 115 proofInit []byte, 116 proofHeight exported.Height, 117 ) (string, *capabilitytypes.Capability, error) { 118 var ( 119 previousChannel types.Channel 120 previousChannelFound bool 121 ) 122 123 channelID := previousChannelID 124 125 // empty channel identifier indicates continuing a previous channel handshake 126 if previousChannelID != "" { 127 // channel identifier and connection hop length checked on msg.ValidateBasic() 128 // ensure that the previous channel exists 129 previousChannel, previousChannelFound = k.GetChannel(ctx, portID, previousChannelID) 130 if !previousChannelFound { 131 return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannel, "previous channel does not exist for supplied previous channelID %s", previousChannelID) 132 } 133 // previous channel must use the same fields 134 if !(previousChannel.Ordering == order && 135 previousChannel.Counterparty.PortId == counterparty.PortId && 136 previousChannel.Counterparty.ChannelId == "" && 137 previousChannel.ConnectionHops[0] == connectionHops[0] && 138 previousChannel.Version == version) { 139 return "", nil, sdkerrors.Wrap(types.ErrInvalidChannel, "channel fields mismatch previous channel fields") 140 } 141 142 if previousChannel.State != types.INIT { 143 return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannelState, "previous channel state is in %s, expected INIT", previousChannel.State) 144 } 145 146 } else { 147 // generate a new channel 148 channelID = k.GenerateChannelIdentifier(ctx) 149 } 150 151 if !k.portKeeper.Authenticate(ctx, portCap, portID) { 152 return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) 153 } 154 155 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) 156 if !found { 157 return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) 158 } 159 160 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 161 return "", nil, sdkerrors.Wrapf( 162 connectiontypes.ErrInvalidConnectionState, 163 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 164 ) 165 } 166 167 getVersions := connectionEnd.GetVersions() 168 if len(getVersions) != 1 { 169 return "", nil, sdkerrors.Wrapf( 170 connectiontypes.ErrInvalidVersion, 171 "single version must be negotiated on connection before opening channel, got: %v", 172 getVersions, 173 ) 174 } 175 176 if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { 177 return "", nil, sdkerrors.Wrapf( 178 connectiontypes.ErrInvalidVersion, 179 "connection version %s does not support channel ordering: %s", 180 getVersions[0], order.String(), 181 ) 182 } 183 184 // NOTE: this step has been switched with the one below to reverse the connection 185 // hops 186 channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version) 187 188 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 189 if !found { 190 // should not reach here, connectionEnd was able to be retrieved above 191 panic("cannot find connection") 192 } 193 194 // expectedCounterpaty is the counterparty of the counterparty's channel end 195 // (i.e self) 196 expectedCounterparty := types.NewCounterparty(portID, "") 197 expectedChannel := types.NewChannel( 198 types.INIT, channel.Ordering, expectedCounterparty, 199 counterpartyHops, counterpartyVersion, 200 ) 201 202 if err := k.connectionKeeper.VerifyChannelState( 203 ctx, connectionEnd, proofHeight, proofInit, 204 counterparty.PortId, counterparty.ChannelId, expectedChannel, 205 ); err != nil { 206 return "", nil, err 207 } 208 209 var ( 210 capKey *capabilitytypes.Capability 211 err error 212 ) 213 214 if !previousChannelFound { 215 capKey, err = k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) 216 if err != nil { 217 return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) 218 } 219 220 k.SetNextSequenceSend(ctx, portID, channelID, 1) 221 k.SetNextSequenceRecv(ctx, portID, channelID, 1) 222 k.SetNextSequenceAck(ctx, portID, channelID, 1) 223 } else { 224 // capability initialized in ChanOpenInit 225 capKey, found = k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) 226 if !found { 227 return "", nil, sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, 228 "capability not found for existing channel, portID (%s) channelID (%s)", portID, channelID, 229 ) 230 } 231 } 232 233 k.SetChannel(ctx, portID, channelID, channel) 234 235 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", previousChannel.State.String(), "new-state", "TRYOPEN") 236 237 ctx.EventManager().EmitEvents(sdk.Events{ 238 sdk.NewEvent( 239 types.EventTypeChannelOpenTry, 240 sdk.NewAttribute(types.AttributeKeyPortID, portID), 241 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 242 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 243 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 244 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 245 ), 246 }) 247 248 return channelID, capKey, nil 249 } 250 251 // ChanOpenAck is called by the handshake-originating module to acknowledge the 252 // acceptance of the initial request by the counterparty module on the other chain. 253 func (k Keeper) ChanOpenAck( 254 ctx sdk.Context, 255 portID, 256 channelID string, 257 chanCap *capabilitytypes.Capability, 258 counterpartyVersion, 259 counterpartyChannelID string, 260 proofTry []byte, 261 proofHeight exported.Height, 262 ) error { 263 channel, found := k.GetChannel(ctx, portID, channelID) 264 if !found { 265 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 266 } 267 268 if !(channel.State == types.INIT || channel.State == types.TRYOPEN) { 269 return sdkerrors.Wrapf( 270 types.ErrInvalidChannelState, 271 "channel state should be INIT or TRYOPEN (got %s)", channel.State.String(), 272 ) 273 } 274 275 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 276 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) 277 } 278 279 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 280 if !found { 281 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 282 } 283 284 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 285 return sdkerrors.Wrapf( 286 connectiontypes.ErrInvalidConnectionState, 287 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 288 ) 289 } 290 291 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 292 if !found { 293 // should not reach here, connectionEnd was able to be retrieved above 294 panic("cannot find connection") 295 } 296 297 // counterparty of the counterparty channel end (i.e self) 298 expectedCounterparty := types.NewCounterparty(portID, channelID) 299 expectedChannel := types.NewChannel( 300 types.TRYOPEN, channel.Ordering, expectedCounterparty, 301 counterpartyHops, counterpartyVersion, 302 ) 303 304 if err := k.connectionKeeper.VerifyChannelState( 305 ctx, connectionEnd, proofHeight, proofTry, 306 channel.Counterparty.PortId, counterpartyChannelID, 307 expectedChannel, 308 ); err != nil { 309 return err 310 } 311 312 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "OPEN") 313 314 channel.State = types.OPEN 315 channel.Version = counterpartyVersion 316 channel.Counterparty.ChannelId = counterpartyChannelID 317 k.SetChannel(ctx, portID, channelID, channel) 318 319 ctx.EventManager().EmitEvents(sdk.Events{ 320 sdk.NewEvent( 321 types.EventTypeChannelOpenAck, 322 sdk.NewAttribute(types.AttributeKeyPortID, portID), 323 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 324 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 325 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 326 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 327 ), 328 }) 329 330 return nil 331 } 332 333 // ChanOpenConfirm is called by the counterparty module to close their end of the 334 // 335 // channel, since the other end has been closed. 336 func (k Keeper) ChanOpenConfirm( 337 ctx sdk.Context, 338 portID, 339 channelID string, 340 chanCap *capabilitytypes.Capability, 341 proofAck []byte, 342 proofHeight exported.Height, 343 ) error { 344 channel, found := k.GetChannel(ctx, portID, channelID) 345 if !found { 346 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 347 } 348 349 if channel.State != types.TRYOPEN { 350 return sdkerrors.Wrapf( 351 types.ErrInvalidChannelState, 352 "channel state is not TRYOPEN (got %s)", channel.State.String(), 353 ) 354 } 355 356 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 357 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) 358 } 359 360 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 361 if !found { 362 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 363 } 364 365 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 366 return sdkerrors.Wrapf( 367 connectiontypes.ErrInvalidConnectionState, 368 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 369 ) 370 } 371 372 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 373 if !found { 374 // Should not reach here, connectionEnd was able to be retrieved above 375 panic("cannot find connection") 376 } 377 378 counterparty := types.NewCounterparty(portID, channelID) 379 expectedChannel := types.NewChannel( 380 types.OPEN, channel.Ordering, counterparty, 381 counterpartyHops, channel.Version, 382 ) 383 384 if err := k.connectionKeeper.VerifyChannelState( 385 ctx, connectionEnd, proofHeight, proofAck, 386 channel.Counterparty.PortId, channel.Counterparty.ChannelId, 387 expectedChannel, 388 ); err != nil { 389 return err 390 } 391 392 channel.State = types.OPEN 393 k.SetChannel(ctx, portID, channelID, channel) 394 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "TRYOPEN", "new-state", "OPEN") 395 396 ctx.EventManager().EmitEvents(sdk.Events{ 397 sdk.NewEvent( 398 types.EventTypeChannelOpenConfirm, 399 sdk.NewAttribute(types.AttributeKeyPortID, portID), 400 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 401 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 402 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 403 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 404 ), 405 }) 406 407 return nil 408 } 409 410 // Closing Handshake 411 // 412 // This section defines the set of functions required to close a channel handshake 413 // as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#closing-handshake 414 // 415 // ChanCloseInit is called by either module to close their end of the channel. Once 416 // closed, channels cannot be reopened. 417 func (k Keeper) ChanCloseInit( 418 ctx sdk.Context, 419 portID, 420 channelID string, 421 chanCap *capabilitytypes.Capability, 422 ) error { 423 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 424 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) 425 } 426 427 channel, found := k.GetChannel(ctx, portID, channelID) 428 if !found { 429 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 430 } 431 432 if channel.State == types.CLOSED { 433 return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") 434 } 435 436 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 437 if !found { 438 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 439 } 440 441 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 442 return sdkerrors.Wrapf( 443 connectiontypes.ErrInvalidConnectionState, 444 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 445 ) 446 } 447 448 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") 449 450 channel.State = types.CLOSED 451 k.SetChannel(ctx, portID, channelID, channel) 452 453 ctx.EventManager().EmitEvents(sdk.Events{ 454 sdk.NewEvent( 455 types.EventTypeChannelCloseInit, 456 sdk.NewAttribute(types.AttributeKeyPortID, portID), 457 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 458 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 459 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 460 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 461 ), 462 }) 463 464 return nil 465 } 466 467 // ChanCloseConfirm is called by the counterparty module to close their end of the 468 // channel, since the other end has been closed. 469 func (k Keeper) ChanCloseConfirm( 470 ctx sdk.Context, 471 portID, 472 channelID string, 473 chanCap *capabilitytypes.Capability, 474 proofInit []byte, 475 proofHeight exported.Height, 476 ) error { 477 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 478 return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)") 479 } 480 481 channel, found := k.GetChannel(ctx, portID, channelID) 482 if !found { 483 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 484 } 485 486 if channel.State == types.CLOSED { 487 return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") 488 } 489 490 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 491 if !found { 492 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 493 } 494 495 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 496 return sdkerrors.Wrapf( 497 connectiontypes.ErrInvalidConnectionState, 498 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 499 ) 500 } 501 502 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 503 if !found { 504 // Should not reach here, connectionEnd was able to be retrieved above 505 panic("cannot find connection") 506 } 507 508 counterparty := types.NewCounterparty(portID, channelID) 509 expectedChannel := types.NewChannel( 510 types.CLOSED, channel.Ordering, counterparty, 511 counterpartyHops, channel.Version, 512 ) 513 514 if err := k.connectionKeeper.VerifyChannelState( 515 ctx, connectionEnd, proofHeight, proofInit, 516 channel.Counterparty.PortId, channel.Counterparty.ChannelId, 517 expectedChannel, 518 ); err != nil { 519 return err 520 } 521 522 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") 523 524 channel.State = types.CLOSED 525 k.SetChannel(ctx, portID, channelID, channel) 526 527 ctx.EventManager().EmitEvents(sdk.Events{ 528 sdk.NewEvent( 529 types.EventTypeChannelCloseConfirm, 530 sdk.NewAttribute(types.AttributeKeyPortID, portID), 531 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 532 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 533 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 534 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 535 ), 536 }) 537 538 return nil 539 }