github.com/okex/exchain@v1.8.0/libs/ibc-go/modules/core/04-channel/keeper/handshake.go (about) 1 package keeper 2 3 import ( 4 sdk "github.com/okex/exchain/libs/cosmos-sdk/types" 5 sdkerrors "github.com/okex/exchain/libs/cosmos-sdk/types/errors" 6 capabilitytypes "github.com/okex/exchain/libs/cosmos-sdk/x/capability/types" 7 connectiontypes "github.com/okex/exchain/libs/ibc-go/modules/core/03-connection/types" 8 "github.com/okex/exchain/libs/ibc-go/modules/core/04-channel/types" 9 porttypes "github.com/okex/exchain/libs/ibc-go/modules/core/05-port/types" 10 host "github.com/okex/exchain/libs/ibc-go/modules/core/24-host" 11 "github.com/okex/exchain/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 // channel, since the other end has been closed. 335 func (k Keeper) ChanOpenConfirm( 336 ctx sdk.Context, 337 portID, 338 channelID string, 339 chanCap *capabilitytypes.Capability, 340 proofAck []byte, 341 proofHeight exported.Height, 342 ) error { 343 channel, found := k.GetChannel(ctx, portID, channelID) 344 if !found { 345 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 346 } 347 348 if channel.State != types.TRYOPEN { 349 return sdkerrors.Wrapf( 350 types.ErrInvalidChannelState, 351 "channel state is not TRYOPEN (got %s)", channel.State.String(), 352 ) 353 } 354 355 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 356 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) 357 } 358 359 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 360 if !found { 361 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 362 } 363 364 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 365 return sdkerrors.Wrapf( 366 connectiontypes.ErrInvalidConnectionState, 367 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 368 ) 369 } 370 371 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 372 if !found { 373 // Should not reach here, connectionEnd was able to be retrieved above 374 panic("cannot find connection") 375 } 376 377 counterparty := types.NewCounterparty(portID, channelID) 378 expectedChannel := types.NewChannel( 379 types.OPEN, channel.Ordering, counterparty, 380 counterpartyHops, channel.Version, 381 ) 382 383 if err := k.connectionKeeper.VerifyChannelState( 384 ctx, connectionEnd, proofHeight, proofAck, 385 channel.Counterparty.PortId, channel.Counterparty.ChannelId, 386 expectedChannel, 387 ); err != nil { 388 return err 389 } 390 391 channel.State = types.OPEN 392 k.SetChannel(ctx, portID, channelID, channel) 393 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "TRYOPEN", "new-state", "OPEN") 394 395 ctx.EventManager().EmitEvents(sdk.Events{ 396 sdk.NewEvent( 397 types.EventTypeChannelOpenConfirm, 398 sdk.NewAttribute(types.AttributeKeyPortID, portID), 399 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 400 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 401 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 402 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 403 ), 404 }) 405 406 return nil 407 } 408 409 // Closing Handshake 410 // 411 // This section defines the set of functions required to close a channel handshake 412 // as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#closing-handshake 413 // 414 // ChanCloseInit is called by either module to close their end of the channel. Once 415 // closed, channels cannot be reopened. 416 func (k Keeper) ChanCloseInit( 417 ctx sdk.Context, 418 portID, 419 channelID string, 420 chanCap *capabilitytypes.Capability, 421 ) error { 422 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 423 return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) 424 } 425 426 channel, found := k.GetChannel(ctx, portID, channelID) 427 if !found { 428 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 429 } 430 431 if channel.State == types.CLOSED { 432 return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") 433 } 434 435 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 436 if !found { 437 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 438 } 439 440 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 441 return sdkerrors.Wrapf( 442 connectiontypes.ErrInvalidConnectionState, 443 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 444 ) 445 } 446 447 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") 448 449 channel.State = types.CLOSED 450 k.SetChannel(ctx, portID, channelID, channel) 451 452 ctx.EventManager().EmitEvents(sdk.Events{ 453 sdk.NewEvent( 454 types.EventTypeChannelCloseInit, 455 sdk.NewAttribute(types.AttributeKeyPortID, portID), 456 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 457 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 458 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 459 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 460 ), 461 }) 462 463 return nil 464 } 465 466 // ChanCloseConfirm is called by the counterparty module to close their end of the 467 // channel, since the other end has been closed. 468 func (k Keeper) ChanCloseConfirm( 469 ctx sdk.Context, 470 portID, 471 channelID string, 472 chanCap *capabilitytypes.Capability, 473 proofInit []byte, 474 proofHeight exported.Height, 475 ) error { 476 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { 477 return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)") 478 } 479 480 channel, found := k.GetChannel(ctx, portID, channelID) 481 if !found { 482 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) 483 } 484 485 if channel.State == types.CLOSED { 486 return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") 487 } 488 489 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 490 if !found { 491 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 492 } 493 494 if connectionEnd.GetState() != int32(connectiontypes.OPEN) { 495 return sdkerrors.Wrapf( 496 connectiontypes.ErrInvalidConnectionState, 497 "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), 498 ) 499 } 500 501 counterpartyHops, found := k.CounterpartyHops(ctx, channel) 502 if !found { 503 // Should not reach here, connectionEnd was able to be retrieved above 504 panic("cannot find connection") 505 } 506 507 counterparty := types.NewCounterparty(portID, channelID) 508 expectedChannel := types.NewChannel( 509 types.CLOSED, channel.Ordering, counterparty, 510 counterpartyHops, channel.Version, 511 ) 512 513 if err := k.connectionKeeper.VerifyChannelState( 514 ctx, connectionEnd, proofHeight, proofInit, 515 channel.Counterparty.PortId, channel.Counterparty.ChannelId, 516 expectedChannel, 517 ); err != nil { 518 return err 519 } 520 521 k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") 522 523 channel.State = types.CLOSED 524 k.SetChannel(ctx, portID, channelID, channel) 525 526 ctx.EventManager().EmitEvents(sdk.Events{ 527 sdk.NewEvent( 528 types.EventTypeChannelCloseConfirm, 529 sdk.NewAttribute(types.AttributeKeyPortID, portID), 530 sdk.NewAttribute(types.AttributeKeyChannelID, channelID), 531 sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), 532 sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), 533 sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), 534 ), 535 }) 536 537 return nil 538 }