package keeper_test import ( "fmt" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) // TestConnOpenInit - Chain A (ID #1) initializes (INIT state) a connection with // Chain B (ID #2) which is yet UNINITIALIZED func (suite *KeeperTestSuite) TestConnOpenInit() { testCases := []struct { msg string malleate func() expPass bool }{ {"success", func() { suite.chainA.CreateClient(suite.chainB) }, true}, {"connection already exists", func() { suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, ibctypes.INIT) }, false}, {"couldn't add connection to client", func() {}, false}, } counterparty := connection.NewCounterparty(testClientIDB, testConnectionIDB, commitmenttypes.NewMerklePrefix(suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes())) for i, tc := range testCases { tc := tc i := i suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset tc.malleate() err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.chainA.GetContext(), testConnectionIDA, testClientIDB, counterparty) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) } } // TestConnOpenTry - Chain B (ID #2) calls ConnOpenTry to verify the state of // connection on Chain A (ID #1) is INIT func (suite *KeeperTestSuite) TestConnOpenTry() { // counterparty for A on B counterparty := connection.NewCounterparty( testClientIDB, testConnectionIDA, commitmenttypes.NewMerklePrefix(suite.chainB.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes()), ) testCases := []struct { msg string malleate func() uint64 expPass bool }{ {"success", func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) return suite.chainB.Header.GetHeight() - 1 }, true}, {"consensus height > latest height", func() uint64 { return 0 }, false}, {"self consensus state not found", func() uint64 { //suite.ctx = suite.ctx.WithBlockHeight(100) return 100 }, false}, {"connection state verification invalid", func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) return 0 }, false}, {"consensus state verification invalid", func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) return suite.chainB.Header.GetHeight() }, false}, {"invalid previous connection", func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) return 0 }, false}, {"couldn't add connection to client", func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) return 0 }, false}, } for i, tc := range testCases { tc := tc i := i suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset consensusHeight := tc.malleate() connectionKey := ibctypes.KeyConnection(testConnectionIDA) proofInit, proofHeight := queryProof(suite.chainA, connectionKey) consensusKey := prefixedClientKey(testClientIDB, ibctypes.KeyConsensusState(consensusHeight)) proofConsensus, _ := queryProof(suite.chainA, consensusKey) err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenTry( suite.chainB.GetContext(), testConnectionIDB, counterparty, testClientIDA, connection.GetCompatibleVersions(), proofInit, proofConsensus, proofHeight+1, consensusHeight, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed with consensus height %d and proof height %d: %s", i, consensusHeight, proofHeight, tc.msg) } else { suite.Require().Error(err, "invalid test case %d passed with consensus height %d and proof height %d: %s", i, consensusHeight, proofHeight, tc.msg) } }) } } // TestConnOpenAck - Chain A (ID #1) calls TestConnOpenAck to acknowledge (ACK state) // the initialization (TRYINIT) of the connection on Chain B (ID #2). func (suite *KeeperTestSuite) TestConnOpenAck() { version := connection.GetCompatibleVersions()[0] testCases := []struct { msg string version string malleate func() uint64 expPass bool }{ {"success", version, func() uint64 { suite.chainA.CreateClient(suite.chainB) suite.chainB.CreateClient(suite.chainA) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.TRYOPEN) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.updateClient(suite.chainA) suite.chainA.updateClient(suite.chainB) return suite.chainB.Header.GetHeight() }, true}, {"consensus height > latest height", version, func() uint64 { return 10 }, false}, {"connection not found", version, func() uint64 { return 2 }, false}, {"connection state is not INIT", version, func() uint64 { suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) return suite.chainB.Header.GetHeight() }, false}, {"incompatible IBC versions", "2.0", func() uint64 { suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, ibctypes.INIT) suite.chainB.updateClient(suite.chainA) return suite.chainB.Header.GetHeight() }, false}, {"self consensus state not found", version, func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.TRYOPEN) suite.chainB.updateClient(suite.chainA) return suite.chainB.Header.GetHeight() }, false}, {"connection state verification failed", version, func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) return suite.chainB.Header.GetHeight() }, false}, {"consensus state verification failed", version, func() uint64 { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.INIT) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.UNINITIALIZED) suite.chainB.updateClient(suite.chainA) return suite.chainB.Header.GetHeight() }, false}, } for i, tc := range testCases { tc := tc i := i suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset consensusHeight := tc.malleate() connectionKey := ibctypes.KeyConnection(testConnectionIDB) proofTry, proofHeight := queryProof(suite.chainB, connectionKey) consensusKey := prefixedClientKey(testClientIDA, ibctypes.KeyConsensusState(consensusHeight)) proofConsensus, _ := queryProof(suite.chainB, consensusKey) err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenAck( suite.chainA.GetContext(), testConnectionIDA, tc.version, proofTry, proofConsensus, proofHeight+1, consensusHeight, ) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed with consensus height %d: %s", i, consensusHeight, tc.msg) } else { suite.Require().Error(err, "invalid test case %d passed with consensus height %d: %s", i, consensusHeight, tc.msg) } }) } } // TestConnOpenConfirm - Chain B (ID #2) calls ConnOpenConfirm to confirm that // Chain A (ID #1) state is now OPEN. func (suite *KeeperTestSuite) TestConnOpenConfirm() { testCases := []testCase{ {"success", func() { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, ibctypes.OPEN) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.TRYOPEN) suite.chainB.updateClient(suite.chainA) }, true}, {"connection not found", func() {}, false}, {"chain B's connection state is not TRYOPEN", func() { suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, ibctypes.UNINITIALIZED) suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, ibctypes.OPEN) suite.chainA.updateClient(suite.chainB) }, false}, {"connection state verification failed", func() { suite.chainB.CreateClient(suite.chainA) suite.chainA.CreateClient(suite.chainB) suite.chainB.updateClient(suite.chainA) suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, ibctypes.INIT) suite.chainB.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, ibctypes.TRYOPEN) suite.chainA.updateClient(suite.chainA) }, false}, } for i, tc := range testCases { tc := tc i := i suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset tc.malleate() connectionKey := ibctypes.KeyConnection(testConnectionIDA) proofAck, proofHeight := queryProof(suite.chainA, connectionKey) if tc.expPass { err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenConfirm( suite.chainB.GetContext(), testConnectionIDB, proofAck, proofHeight+1, ) suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) } else { err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenConfirm( suite.chainB.GetContext(), testConnectionIDB, proofAck, proofHeight+1, ) suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) } }) } } type testCase = struct { msg string malleate func() expPass bool }