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  }