github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/03-connection/keeper/handshake.go (about) 1 package keeper 2 3 import ( 4 "bytes" 5 6 types2 "github.com/fibonacci-chain/fbc/libs/tendermint/types" 7 8 //"github.com/cosmos/cosmos-sdk/telemetry" 9 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 10 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 11 clienttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types" 12 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/03-connection/types" 13 commitmenttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/23-commitment/types" 14 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 15 "github.com/gogo/protobuf/proto" 16 ) 17 18 // ConnOpenInit initialises a connection attempt on chain A. The generated connection identifier 19 // is returned. 20 // 21 // NOTE: Msg validation verifies the supplied identifiers and ensures that the counterparty 22 // connection identifier is empty. 23 func (k Keeper) ConnOpenInit( 24 ctx sdk.Context, 25 clientID string, 26 counterparty types.Counterparty, // counterpartyPrefix, counterpartyClientIdentifier 27 version *types.Version, 28 delayPeriod uint64, 29 ) (string, error) { 30 versions := types.GetCompatibleVersions() 31 if version != nil { 32 if !types.IsSupportedVersion(version) { 33 return "", sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported") 34 } 35 36 versions = []exported.Version{version} 37 } 38 39 // connection defines chain A's ConnectionEnd 40 connectionID := k.GenerateConnectionIdentifier(ctx) 41 connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.ExportedVersionsToProto(versions), delayPeriod) 42 k.SetConnection(ctx, connectionID, connection) 43 44 if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { 45 return "", err 46 } 47 48 k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "INIT") 49 50 return connectionID, nil 51 } 52 53 // ConnOpenTry relays notice of a connection attempt on chain A to chain B (this 54 // code is executed on chain B). 55 // 56 // NOTE: 57 // - Here chain A acts as the counterparty 58 // - Identifiers are checked on msg validation 59 func (k Keeper) ConnOpenTry( 60 ctx sdk.Context, 61 previousConnectionID string, // previousIdentifier 62 counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier 63 delayPeriod uint64, 64 clientID string, // clientID of chainA 65 clientState exported.ClientState, // clientState that chainA has for chainB 66 counterpartyVersions []exported.Version, // supported versions of chain A 67 proofInit []byte, // proof that chainA stored connectionEnd in state (on ConnOpenInit) 68 proofClient []byte, // proof that chainA stored a light client of chainB 69 proofConsensus []byte, // proof that chainA stored chainB's consensus state at consensus height 70 proofHeight exported.Height, // height at which relayer constructs proof of A storing connectionEnd in state 71 consensusHeight exported.Height, // latest height of chain B which chain A has stored in its chain B client 72 ) (string, error) { 73 var ( 74 connectionID string 75 previousConnection types.ConnectionEnd 76 found bool 77 ) 78 79 // empty connection identifier indicates continuing a previous connection handshake 80 if previousConnectionID != "" { 81 // ensure that the previous connection exists 82 previousConnection, found = k.GetConnection(ctx, previousConnectionID) 83 if !found { 84 return "", sdkerrors.Wrapf(types.ErrConnectionNotFound, "previous connection does not exist for supplied previous connectionID %s", previousConnectionID) 85 } 86 87 // ensure that the existing connection's 88 // counterparty is chainA and connection is on INIT stage. 89 // Check that existing connection versions for initialized connection is equal to compatible 90 // versions for this chain. 91 // ensure that existing connection's delay period is the same as desired delay period. 92 if !(previousConnection.Counterparty.ConnectionId == "" && 93 bytes.Equal(previousConnection.Counterparty.Prefix.Bytes(), counterparty.Prefix.Bytes()) && 94 previousConnection.ClientId == clientID && 95 previousConnection.Counterparty.ClientId == counterparty.ClientId && 96 previousConnection.DelayPeriod == delayPeriod) { 97 return "", sdkerrors.Wrap(types.ErrInvalidConnection, "connection fields mismatch previous connection fields") 98 } 99 100 if !(previousConnection.State == types.INIT) { 101 return "", sdkerrors.Wrapf(types.ErrInvalidConnectionState, "previous connection state is in state %s, expected INIT", previousConnection.State) 102 } 103 104 // continue with previous connection 105 connectionID = previousConnectionID 106 107 } else { 108 // generate a new connection 109 connectionID = k.GenerateConnectionIdentifier(ctx) 110 } 111 112 selfHeight := clienttypes.GetSelfHeight(ctx) 113 if consensusHeight.GTE(selfHeight) { 114 return "", sdkerrors.Wrapf( 115 sdkerrors.ErrInvalidHeight, 116 "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, 117 ) 118 } 119 120 // validate client parameters of a chainB client stored on chainA 121 if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { 122 return "", err 123 } 124 125 expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) 126 if !found { 127 return "", sdkerrors.Wrap(clienttypes.ErrSelfConsensusStateNotFound, consensusHeight.String()) 128 } 129 130 // expectedConnection defines Chain A's ConnectionEnd 131 // NOTE: chain A's counterparty is chain B (i.e where this code is executed) 132 // NOTE: chainA and chainB must have the same delay period 133 prefix := k.GetCommitmentPrefix() 134 expectedCounterparty := types.NewCounterparty(clientID, "", commitmenttypes.NewMerklePrefix(prefix.Bytes())) 135 expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientId, expectedCounterparty, types.ExportedVersionsToProto(counterpartyVersions), delayPeriod) 136 137 supportedVersions := types.GetCompatibleVersions() 138 if !types2.HigherThanVenus4(ctx.BlockHeight()) { 139 if len(previousConnection.Versions) != 0 { 140 supportedVersions = previousConnection.GetVersions() 141 } 142 } 143 144 // chain B picks a version from Chain A's available versions that is compatible 145 // with Chain B's supported IBC versions. PickVersion will select the intersection 146 // of the supported versions and the counterparty versions. 147 version, err := types.PickVersion(supportedVersions, counterpartyVersions) 148 if err != nil { 149 return "", err 150 } 151 152 // connection defines chain B's ConnectionEnd 153 connection := types.NewConnectionEnd(types.TRYOPEN, clientID, counterparty, []*types.Version{version}, delayPeriod) 154 155 // Check that ChainA committed expectedConnectionEnd to its state 156 if err := k.VerifyConnectionState( 157 ctx, connection, proofHeight, proofInit, counterparty.ConnectionId, 158 expectedConnection, 159 ); err != nil { 160 return "", err 161 } 162 163 // Check that ChainA stored the clientState provided in the msg 164 if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { 165 return "", err 166 } 167 168 // Check that ChainA stored the correct ConsensusState of chainB at the given consensusHeight 169 if err := k.VerifyClientConsensusState( 170 ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, 171 ); err != nil { 172 return "", err 173 } 174 175 // store connection in chainB state 176 if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { 177 return "", sdkerrors.Wrapf(err, "failed to add connection with ID %s to client with ID %s", connectionID, clientID) 178 } 179 180 k.SetConnection(ctx, connectionID, connection) 181 k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", previousConnection.State.String(), "new-state", "TRYOPEN") 182 183 return connectionID, nil 184 } 185 186 // ConnOpenAck relays acceptance of a connection open attempt from chain B back 187 // to chain A (this code is executed on chain A). 188 // 189 // NOTE: Identifiers are checked on msg validation. 190 func (k Keeper) ConnOpenAck( 191 ctx sdk.Context, 192 connectionID string, 193 clientState exported.ClientState, // client state for chainA on chainB 194 version *types.Version, // version that ChainB chose in ConnOpenTry 195 counterpartyConnectionID string, 196 proofTry []byte, // proof that connectionEnd was added to ChainB state in ConnOpenTry 197 proofClient []byte, // proof of client state on chainB for chainA 198 proofConsensus []byte, // proof that chainB has stored ConsensusState of chainA on its client 199 proofHeight exported.Height, // height that relayer constructed proofTry 200 consensusHeight exported.Height, // latest height of chainA that chainB has stored on its chainA client 201 ) error { 202 // Check that chainB client hasn't stored invalid height 203 selfHeight := clienttypes.GetSelfHeight(ctx) 204 if consensusHeight.GTE(selfHeight) { 205 return sdkerrors.Wrapf( 206 sdkerrors.ErrInvalidHeight, 207 "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, 208 ) 209 } 210 211 // Retrieve connection 212 connection, found := k.GetConnection(ctx, connectionID) 213 if !found { 214 return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) 215 } 216 217 // Verify the provided version against the previously set connection state 218 switch { 219 // connection on ChainA must be in INIT or TRYOPEN 220 case connection.State != types.INIT && connection.State != types.TRYOPEN: 221 return sdkerrors.Wrapf( 222 types.ErrInvalidConnectionState, 223 "connection state is not INIT or TRYOPEN (got %s)", connection.State.String(), 224 ) 225 226 // if the connection is INIT then the provided version must be supproted 227 case connection.State == types.INIT && !types.IsSupportedVersion(version): 228 return sdkerrors.Wrapf( 229 types.ErrInvalidConnectionState, 230 "connection state is in INIT but the provided version is not supported %s", version, 231 ) 232 233 // if the connection is in TRYOPEN then the version must be the only set version in the 234 // retreived connection state. 235 case connection.State == types.TRYOPEN && (len(connection.Versions) != 1 || !proto.Equal(connection.Versions[0], version)): 236 return sdkerrors.Wrapf( 237 types.ErrInvalidConnectionState, 238 "connection state is in TRYOPEN but the provided version (%s) is not set in the previous connection versions %s", version, connection.Versions, 239 ) 240 } 241 242 // validate client parameters of a chainA client stored on chainB 243 if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { 244 return err 245 } 246 247 // Retrieve chainA's consensus state at consensusheight 248 expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) 249 if !found { 250 return clienttypes.ErrSelfConsensusStateNotFound 251 } 252 253 prefix := k.GetCommitmentPrefix() 254 expectedCounterparty := types.NewCounterparty(connection.ClientId, connectionID, commitmenttypes.NewMerklePrefix(prefix.Bytes())) 255 expectedConnection := types.NewConnectionEnd(types.TRYOPEN, connection.Counterparty.ClientId, expectedCounterparty, []*types.Version{version}, connection.DelayPeriod) 256 257 // Ensure that ChainB stored expected connectionEnd in its state during ConnOpenTry 258 if err := k.VerifyConnectionState( 259 ctx, connection, proofHeight, proofTry, counterpartyConnectionID, 260 expectedConnection, 261 ); err != nil { 262 return err 263 } 264 265 // Check that ChainB stored the clientState provided in the msg 266 if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { 267 return err 268 } 269 270 // Ensure that ChainB has stored the correct ConsensusState for chainA at the consensusHeight 271 if err := k.VerifyClientConsensusState( 272 ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, 273 ); err != nil { 274 return err 275 } 276 277 k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", connection.State.String(), "new-state", "OPEN") 278 279 // Update connection state to Open 280 connection.State = types.OPEN 281 connection.Versions = []*types.Version{version} 282 connection.Counterparty.ConnectionId = counterpartyConnectionID 283 k.SetConnection(ctx, connectionID, connection) 284 return nil 285 } 286 287 // ConnOpenConfirm confirms opening of a connection on chain A to chain B, after 288 // which the connection is open on both chains (this code is executed on chain B). 289 // 290 // NOTE: Identifiers are checked on msg validation. 291 func (k Keeper) ConnOpenConfirm( 292 ctx sdk.Context, 293 connectionID string, 294 proofAck []byte, // proof that connection opened on ChainA during ConnOpenAck 295 proofHeight exported.Height, // height that relayer constructed proofAck 296 ) error { 297 // Retrieve connection 298 connection, found := k.GetConnection(ctx, connectionID) 299 if !found { 300 return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) 301 } 302 303 // Check that connection state on ChainB is on state: TRYOPEN 304 if connection.State != types.TRYOPEN { 305 return sdkerrors.Wrapf( 306 types.ErrInvalidConnectionState, 307 "connection state is not TRYOPEN (got %s)", connection.State.String(), 308 ) 309 } 310 311 prefix := k.GetCommitmentPrefix() 312 expectedCounterparty := types.NewCounterparty(connection.ClientId, connectionID, commitmenttypes.NewMerklePrefix(prefix.Bytes())) 313 expectedConnection := types.NewConnectionEnd(types.OPEN, connection.Counterparty.ClientId, expectedCounterparty, connection.Versions, connection.DelayPeriod) 314 315 // Check that connection on ChainA is open 316 if err := k.VerifyConnectionState( 317 ctx, connection, proofHeight, proofAck, connection.Counterparty.ConnectionId, 318 expectedConnection, 319 ); err != nil { 320 return err 321 } 322 323 // Update ChainB's connection to Open 324 connection.State = types.OPEN 325 k.SetConnection(ctx, connectionID, connection) 326 k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "TRYOPEN", "new-state", "OPEN") 327 328 return nil 329 }